##// END OF EJS Templates
Added limit option for revision ranges
marcink -
r984:ccd323bf beta
parent child Browse files
Show More
@@ -1,243 +1,247 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.changeset
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 changeset controller for pylons showoing changes beetween
7 7 revisions
8 8
9 9 :created_on: Apr 25, 2010
10 10 :author: marcink
11 11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 12 :license: GPLv3, see COPYING for more details.
13 13 """
14 14 # This program is free software; you can redistribute it and/or
15 15 # modify it under the terms of the GNU General Public License
16 16 # as published by the Free Software Foundation; version 2
17 17 # of the License or (at your opinion) any later version of the license.
18 18 #
19 19 # This program is distributed in the hope that it will be useful,
20 20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 22 # GNU General Public License for more details.
23 23 #
24 24 # You should have received a copy of the GNU General Public License
25 25 # along with this program; if not, write to the Free Software
26 26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 27 # MA 02110-1301, USA.
28 28 import logging
29 29 import traceback
30 30
31 31 from pylons import tmpl_context as c, url, request, response
32 32 from pylons.i18n.translation import _
33 33 from pylons.controllers.util import redirect
34 34
35 35 import rhodecode.lib.helpers as h
36 36 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
37 37 from rhodecode.lib.base import BaseController, render
38 38 from rhodecode.lib.utils import EmptyChangeset
39 39 from rhodecode.model.scm import ScmModel
40 40
41 41 from vcs.exceptions import RepositoryError, ChangesetError, \
42 42 ChangesetDoesNotExistError
43 43 from vcs.nodes import FileNode
44 44 from vcs.utils import diffs as differ
45 45 from vcs.utils.ordered_dict import OrderedDict
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49 class ChangesetController(BaseController):
50 50
51 51 @LoginRequired()
52 52 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
53 53 'repository.admin')
54 54 def __before__(self):
55 55 super(ChangesetController, self).__before__()
56 56
57 57 def index(self, revision):
58 58 hg_model = ScmModel()
59 59
60 60 def wrap_to_table(str):
61 61
62 62 return '''<table class="code-difftable">
63 63 <tr class="line">
64 64 <td class="lineno new"></td>
65 65 <td class="code"><pre>%s</pre></td>
66 66 </tr>
67 67 </table>''' % str
68 68
69 def get_cs_range(repo, rev_start, rev_end):
69 def get_cs_range(repo, rev_start, rev_end, limit=None):
70 70 """
71 71 Temp fix function until VCS will handle that
72 72 see issue #48
73 73 :param rev_start:
74 74 :param rev_end:
75 75 """
76 76
77 77 start_cs = repo.get_changeset(rev_start)
78 78 end_cs = repo.get_changeset(rev_end)
79 79
80 80 if start_cs.revision >= end_cs.revision:
81 81 raise Exception('Start revision cannot be after End')
82 82
83 83 yield start_cs
84 84
85 cnt = 0
85 86 while 1:
87
86 88 next = start_cs.next()
87 89 yield next
88 90 start_cs = next
91 cnt += 1
89 92 if next == end_cs:
90 93 break
91
94 if limit and cnt > limit:
95 break
92 96 #======================================================================
93 97 # REAL CODE BELOW
94 98 #======================================================================
95 99 #get ranges of revisions if preset
96 100 rev_range = revision.split('...')[:2]
97
101 range_limit = 50
98 102 try:
99 103 repo = hg_model.get_repo(c.repo_name)
100 104 if len(rev_range) == 2:
101 105 rev_start = rev_range[0]
102 106 rev_end = rev_range[1]
103 rev_ranges = get_cs_range(repo, rev_start, rev_end)
107 rev_ranges = get_cs_range(repo, rev_start, rev_end, range_limit)
104 108 else:
105 109 rev_ranges = [repo.get_changeset(revision)]
106 110
107 111 c.cs_ranges = list(rev_ranges)
108 112
109 113 except (RepositoryError, ChangesetDoesNotExistError, Exception), e:
110 114 log.error(traceback.format_exc())
111 115 h.flash(str(e), category='warning')
112 116 return redirect(url('home'))
113 117
114 118 c.changes = OrderedDict()
115 119 c.sum_added = 0
116 120 c.sum_removed = 0
117 121
118 122
119 123 for changeset in c.cs_ranges:
120 124 c.changes[changeset.raw_id] = []
121 125 try:
122 126 changeset_parent = changeset.parents[0]
123 127 except IndexError:
124 128 changeset_parent = None
125 129
126 130
127 131 #==================================================================
128 132 # ADDED FILES
129 133 #==================================================================
130 134 for node in changeset.added:
131 135 filenode_old = FileNode(node.path, '', EmptyChangeset())
132 136 if filenode_old.is_binary or node.is_binary:
133 137 diff = wrap_to_table(_('binary file'))
134 138 else:
135 139 c.sum_added += node.size
136 140 if c.sum_added < self.cut_off_limit:
137 141 f_udiff = differ.get_udiff(filenode_old, node)
138 142 diff = differ.DiffProcessor(f_udiff).as_html()
139 143
140 144 else:
141 145 diff = wrap_to_table(_('Changeset is to big and was cut'
142 146 ' off, see raw changeset instead'))
143 147
144 148 cs1 = None
145 149 cs2 = node.last_changeset.raw_id
146 150 c.changes[changeset.raw_id].append(('added', node, diff, cs1, cs2))
147 151
148 152 #==================================================================
149 153 # CHANGED FILES
150 154 #==================================================================
151 155 for node in changeset.changed:
152 156 try:
153 157 filenode_old = changeset_parent.get_node(node.path)
154 158 except ChangesetError:
155 159 filenode_old = FileNode(node.path, '', EmptyChangeset())
156 160
157 161 if filenode_old.is_binary or node.is_binary:
158 162 diff = wrap_to_table(_('binary file'))
159 163 else:
160 164
161 165 if c.sum_removed < self.cut_off_limit:
162 166 f_udiff = differ.get_udiff(filenode_old, node)
163 167 diff = differ.DiffProcessor(f_udiff).as_html()
164 168 if diff:
165 169 c.sum_removed += len(diff)
166 170 else:
167 171 diff = wrap_to_table(_('Changeset is to big and was cut'
168 172 ' off, see raw changeset instead'))
169 173
170 174
171 175 cs1 = filenode_old.last_changeset.raw_id
172 176 cs2 = node.last_changeset.raw_id
173 177 c.changes[changeset.raw_id].append(('changed', node, diff, cs1, cs2))
174 178
175 179 #==================================================================
176 180 # REMOVED FILES
177 181 #==================================================================
178 182 for node in changeset.removed:
179 183 c.changes[changeset.raw_id].append(('removed', node, None, None, None))
180 184
181 185 if len(c.cs_ranges) == 1:
182 186 c.changeset = c.cs_ranges[0]
183 187 c.changes = c.changes[c.changeset.raw_id]
184 188
185 189 return render('changeset/changeset.html')
186 190 else:
187 191 return render('changeset/changeset_range.html')
188 192
189 193 def raw_changeset(self, revision):
190 194
191 195 hg_model = ScmModel()
192 196 method = request.GET.get('diff', 'show')
193 197 try:
194 198 r = hg_model.get_repo(c.repo_name)
195 199 c.scm_type = r.alias
196 200 c.changeset = r.get_changeset(revision)
197 201 except RepositoryError:
198 202 log.error(traceback.format_exc())
199 203 return redirect(url('home'))
200 204 else:
201 205 try:
202 206 c.changeset_parent = c.changeset.parents[0]
203 207 except IndexError:
204 208 c.changeset_parent = None
205 209 c.changes = []
206 210
207 211 for node in c.changeset.added:
208 212 filenode_old = FileNode(node.path, '')
209 213 if filenode_old.is_binary or node.is_binary:
210 214 diff = _('binary file') + '\n'
211 215 else:
212 216 f_udiff = differ.get_udiff(filenode_old, node)
213 217 diff = differ.DiffProcessor(f_udiff).raw_diff()
214 218
215 219 cs1 = None
216 220 cs2 = node.last_changeset.raw_id
217 221 c.changes.append(('added', node, diff, cs1, cs2))
218 222
219 223 for node in c.changeset.changed:
220 224 filenode_old = c.changeset_parent.get_node(node.path)
221 225 if filenode_old.is_binary or node.is_binary:
222 226 diff = _('binary file')
223 227 else:
224 228 f_udiff = differ.get_udiff(filenode_old, node)
225 229 diff = differ.DiffProcessor(f_udiff).raw_diff()
226 230
227 231 cs1 = filenode_old.last_changeset.raw_id
228 232 cs2 = node.last_changeset.raw_id
229 233 c.changes.append(('changed', node, diff, cs1, cs2))
230 234
231 235 response.content_type = 'text/plain'
232 236
233 237 if method == 'download':
234 238 response.content_disposition = 'attachment; filename=%s.patch' % revision
235 239
236 240 parent = True if len(c.changeset.parents) > 0 else False
237 241 c.parent_tmpl = 'Parent %s' % c.changeset.parents[0].raw_id if parent else ''
238 242
239 243 c.diffs = ''
240 244 for x in c.changes:
241 245 c.diffs += x[2]
242 246
243 247 return render('changeset/raw_changeset.html')
@@ -1,143 +1,143 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.smtp_mailer
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Simple smtp mailer used in RhodeCode
7 7
8 :created_on: Sep 13, 2011
8 :created_on: Sep 13, 2010
9 9 :copyright: (c) 2011 by marcink.
10 10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 11 """
12 12
13 13 import logging
14 14 import smtplib
15 15 import mimetypes
16 16 from socket import sslerror
17 17
18 18 from email.mime.multipart import MIMEMultipart
19 19 from email.mime.image import MIMEImage
20 20 from email.mime.audio import MIMEAudio
21 21 from email.mime.base import MIMEBase
22 22 from email.mime.text import MIMEText
23 23 from email.utils import formatdate
24 24 from email import encoders
25 25
26 26 class SmtpMailer(object):
27 27 """SMTP mailer class
28 28
29 29 mailer = SmtpMailer(mail_from, user, passwd, mail_server, mail_port, ssl, tls)
30 30 mailer.send(recipients, subject, body, attachment_files)
31 31
32 32 :param recipients might be a list of string or single string
33 33 :param attachment_files is a dict of {filename:location}
34 34 it tries to guess the mimetype and attach the file
35 35
36 36 """
37 37
38 38 def __init__(self, mail_from, user, passwd, mail_server,
39 39 mail_port=None, ssl=False, tls=False):
40 40
41 41 self.mail_from = mail_from
42 42 self.mail_server = mail_server
43 43 self.mail_port = mail_port
44 44 self.user = user
45 45 self.passwd = passwd
46 46 self.ssl = ssl
47 47 self.tls = tls
48 48 self.debug = False
49 49
50 50 def send(self, recipients=[], subject='', body='', attachment_files={}):
51 51
52 52 if isinstance(recipients, basestring):
53 53 recipients = [recipients]
54 54 if self.ssl:
55 55 smtp_serv = smtplib.SMTP_SSL(self.mail_server, self.mail_port)
56 56 else:
57 57 smtp_serv = smtplib.SMTP(self.mail_server, self.mail_port)
58 58
59 59 if self.tls:
60 60 smtp_serv.ehlo()
61 61 smtp_serv.starttls()
62 62
63 63 if self.debug:
64 64 smtp_serv.set_debuglevel(1)
65 65
66 66 smtp_serv.ehlo()
67 67
68 68 #if server requires authorization you must provide login and password
69 69 #but only if we have them
70 70 if self.user and self.passwd:
71 71 smtp_serv.login(self.user, self.passwd)
72 72
73 73
74 74 date_ = formatdate(localtime=True)
75 75 msg = MIMEMultipart()
76 76 msg['From'] = self.mail_from
77 77 msg['To'] = ','.join(recipients)
78 78 msg['Date'] = date_
79 79 msg['Subject'] = subject
80 80 msg.preamble = 'You will not see this in a MIME-aware mail reader.\n'
81 81
82 82 msg.attach(MIMEText(body))
83 83
84 84 if attachment_files:
85 85 self.__atach_files(msg, attachment_files)
86 86
87 87 smtp_serv.sendmail(self.mail_from, recipients, msg.as_string())
88 88 logging.info('MAIL SEND TO: %s' % recipients)
89 89
90 90 try:
91 91 smtp_serv.quit()
92 92 except sslerror:
93 93 # sslerror is raised in tls connections on closing sometimes
94 94 pass
95 95
96 96
97 97
98 98 def __atach_files(self, msg, attachment_files):
99 99 if isinstance(attachment_files, dict):
100 100 for f_name, msg_file in attachment_files.items():
101 101 ctype, encoding = mimetypes.guess_type(f_name)
102 102 logging.info("guessing file %s type based on %s" , ctype, f_name)
103 103 if ctype is None or encoding is not None:
104 104 # No guess could be made, or the file is encoded (compressed), so
105 105 # use a generic bag-of-bits type.
106 106 ctype = 'application/octet-stream'
107 107 maintype, subtype = ctype.split('/', 1)
108 108 if maintype == 'text':
109 109 # Note: we should handle calculating the charset
110 110 file_part = MIMEText(self.get_content(msg_file),
111 111 _subtype=subtype)
112 112 elif maintype == 'image':
113 113 file_part = MIMEImage(self.get_content(msg_file),
114 114 _subtype=subtype)
115 115 elif maintype == 'audio':
116 116 file_part = MIMEAudio(self.get_content(msg_file),
117 117 _subtype=subtype)
118 118 else:
119 119 file_part = MIMEBase(maintype, subtype)
120 120 file_part.set_payload(self.get_content(msg_file))
121 121 # Encode the payload using Base64
122 122 encoders.encode_base64(msg)
123 123 # Set the filename parameter
124 124 file_part.add_header('Content-Disposition', 'attachment',
125 125 filename=f_name)
126 126 file_part.add_header('Content-Type', ctype, name=f_name)
127 127 msg.attach(file_part)
128 128 else:
129 129 raise Exception('Attachment files should be'
130 130 'a dict in format {"filename":"filepath"}')
131 131
132 132 def get_content(self, msg_file):
133 133 '''
134 134 Get content based on type, if content is a string do open first
135 135 else just read because it's a probably open file object
136 136 :param msg_file:
137 137 '''
138 138 if isinstance(msg_file, str):
139 139 return open(msg_file, "rb").read()
140 140 else:
141 141 #just for safe seek to 0
142 142 msg_file.seek(0)
143 143 return msg_file.read()
General Comments 0
You need to be logged in to leave comments. Login now