##// END OF EJS Templates
White space cleanup
marcink -
r3029:685ebc84 beta
parent child Browse files
Show More
@@ -1,183 +1,182 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.feed
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Feed controller for rhodecode
7 7
8 8 :created_on: Apr 23, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27
28 28 from pylons import url, response, tmpl_context as c
29 29 from pylons.i18n.translation import _
30 30
31 31 from beaker.cache import cache_region, region_invalidate
32 32 from webhelpers.feedgenerator import Atom1Feed, Rss201rev2Feed
33 33
34 34 from rhodecode.lib import helpers as h
35 35 from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator
36 36 from rhodecode.lib.base import BaseRepoController
37 37 from rhodecode.lib.diffs import DiffProcessor, LimitedDiffContainer
38 38 from rhodecode.model.db import CacheInvalidation
39 39 from rhodecode.lib.utils2 import safe_int, str2bool
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43
44 44 class FeedController(BaseRepoController):
45 45
46 46 @LoginRequired(api_access=True)
47 47 @HasRepoPermissionAnyDecorator('repository.read', 'repository.write',
48 48 'repository.admin')
49 49 def __before__(self):
50 50 super(FeedController, self).__before__()
51 51 #common values for feeds
52 52 self.description = _('Changes on %s repository')
53 53 self.title = self.title = _('%s %s feed') % (c.rhodecode_name, '%s')
54 54 self.language = 'en-us'
55 55 self.ttl = "5"
56 56 import rhodecode
57 57 CONF = rhodecode.CONFIG
58 58 self.include_diff = str2bool(CONF.get('rss_include_diff', False))
59 59 self.feed_nr = safe_int(CONF.get('rss_items_per_page', 20))
60 60 # we need to protect from parsing huge diffs here other way
61 61 # we can kill the server
62 62 self.feed_diff_limit = safe_int(CONF.get('rss_cut_off_limit'), 32 * 1024)
63 63
64 64 def _get_title(self, cs):
65 65 return "%s" % (
66 66 h.shorter(cs.message, 160)
67 67 )
68 68
69 69 def __changes(self, cs):
70 70 changes = []
71 71 diff_processor = DiffProcessor(cs.diff(),
72 72 diff_limit=self.feed_diff_limit)
73 73 _parsed = diff_processor.prepare(inline_diff=False)
74 74 limited_diff = False
75 75 if isinstance(_parsed, LimitedDiffContainer):
76 76 limited_diff = True
77 77
78 78 for st in _parsed:
79 79 st.update({'added': st['stats'][0],
80 80 'removed': st['stats'][1]})
81 81 changes.append('\n %(operation)s %(filename)s '
82 82 '(%(added)s lines added, %(removed)s lines removed)'
83 83 % st)
84 84 if limited_diff:
85 85 changes = changes + ['\n ' +
86 86 _('Changeset was too big and was cut off...')]
87 87 return diff_processor, changes
88 88
89 89 def __get_desc(self, cs):
90 90 desc_msg = []
91 91 desc_msg.append('%s %s %s<br/>' % (h.person(cs.author),
92 92 _('commited on'),
93 93 h.fmt_date(cs.date)))
94 94 #branches, tags, bookmarks
95 95 if cs.branch:
96 96 desc_msg.append('branch: %s<br/>' % cs.branch)
97 97 if h.is_hg(c.rhodecode_repo):
98 98 for book in cs.bookmarks:
99 99 desc_msg.append('bookmark: %s<br/>' % book)
100 100 for tag in cs.tags:
101 101 desc_msg.append('tag: %s<br/>' % tag)
102 102 diff_processor, changes = self.__changes(cs)
103 103 # rev link
104 104 _url = url('changeset_home', repo_name=cs.repository.name,
105 105 revision=cs.raw_id, qualified=True)
106 106 desc_msg.append('changesest: <a href="%s">%s</a>' % (_url, cs.raw_id[:8]))
107 107
108 108 desc_msg.append('<pre>')
109 109 desc_msg.append(cs.message)
110 110 desc_msg.append('\n')
111 111 desc_msg.extend(changes)
112 112 if self.include_diff:
113 113 desc_msg.append('\n\n')
114 114 desc_msg.append(diff_processor.as_raw())
115 115 desc_msg.append('</pre>')
116 116 return desc_msg
117 117
118 118 def atom(self, repo_name):
119 119 """Produce an atom-1.0 feed via feedgenerator module"""
120 120
121 121 @cache_region('long_term')
122 122 def _get_feed_from_cache(key):
123 123 feed = Atom1Feed(
124 124 title=self.title % repo_name,
125 125 link=url('summary_home', repo_name=repo_name,
126 126 qualified=True),
127 127 description=self.description % repo_name,
128 128 language=self.language,
129 129 ttl=self.ttl
130 130 )
131 131
132 132 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
133 133 feed.add_item(title=self._get_title(cs),
134 134 link=url('changeset_home', repo_name=repo_name,
135 135 revision=cs.raw_id, qualified=True),
136 136 author_name=cs.author,
137 137 description=''.join(self.__get_desc(cs)),
138 138 pubdate=cs.date,
139 139 )
140 140
141 141 response.content_type = feed.mime_type
142 142 return feed.writeString('utf-8')
143 143
144 144 key = repo_name + '_ATOM'
145 145 inv = CacheInvalidation.invalidate(key)
146 146 if inv is not None:
147 147 region_invalidate(_get_feed_from_cache, None, key)
148 148 CacheInvalidation.set_valid(inv.cache_key)
149 149 return _get_feed_from_cache(key)
150 150
151 151 def rss(self, repo_name):
152 152 """Produce an rss2 feed via feedgenerator module"""
153 153
154 154 @cache_region('long_term')
155 155 def _get_feed_from_cache(key):
156 156 feed = Rss201rev2Feed(
157 157 title=self.title % repo_name,
158 158 link=url('summary_home', repo_name=repo_name,
159 159 qualified=True),
160 160 description=self.description % repo_name,
161 161 language=self.language,
162 162 ttl=self.ttl
163 163 )
164 164
165 165 for cs in reversed(list(c.rhodecode_repo[-self.feed_nr:])):
166 166 feed.add_item(title=self._get_title(cs),
167 167 link=url('changeset_home', repo_name=repo_name,
168 168 revision=cs.raw_id, qualified=True),
169 169 author_name=cs.author,
170 170 description=''.join(self.__get_desc(cs)),
171 171 pubdate=cs.date,
172 172 )
173 173
174 174 response.content_type = feed.mime_type
175 175 return feed.writeString('utf-8')
176 176
177 177 key = repo_name + '_RSS'
178 178 inv = CacheInvalidation.invalidate(key)
179 179 if inv is not None:
180 180 region_invalidate(_get_feed_from_cache, None, key)
181 181 CacheInvalidation.set_valid(inv.cache_key)
182 182 return _get_feed_from_cache(key)
183
@@ -1,768 +1,768 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.diffs
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Set of diffing helpers, previously part of vcs
7 7
8 8
9 9 :created_on: Dec 4, 2011
10 10 :author: marcink
11 11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 12 :original copyright: 2007-2008 by Armin Ronacher
13 13 :license: GPLv3, see COPYING for more details.
14 14 """
15 15 # This program is free software: you can redistribute it and/or modify
16 16 # it under the terms of the GNU General Public License as published by
17 17 # the Free Software Foundation, either version 3 of the License, or
18 18 # (at your option) any later version.
19 19 #
20 20 # This program is distributed in the hope that it will be useful,
21 21 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 22 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 23 # GNU General Public License for more details.
24 24 #
25 25 # You should have received a copy of the GNU General Public License
26 26 # along with this program. If not, see <http://www.gnu.org/licenses/>.
27 27
28 28 import re
29 29 import difflib
30 30 import logging
31 31 import traceback
32 32
33 33 from itertools import tee, imap
34 34
35 35 from mercurial import patch
36 36 from mercurial.mdiff import diffopts
37 37 from mercurial.bundlerepo import bundlerepository
38 38
39 39 from pylons.i18n.translation import _
40 40
41 41 from rhodecode.lib.compat import BytesIO
42 42 from rhodecode.lib.vcs.utils.hgcompat import localrepo
43 43 from rhodecode.lib.vcs.exceptions import VCSError
44 44 from rhodecode.lib.vcs.nodes import FileNode, SubModuleNode
45 45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46 46 from rhodecode.lib.helpers import escape
47 47 from rhodecode.lib.utils import make_ui
48 48 from rhodecode.lib.utils2 import safe_unicode
49 49
50 50 log = logging.getLogger(__name__)
51 51
52 52
53 53 def wrap_to_table(str_):
54 54 return '''<table class="code-difftable">
55 55 <tr class="line no-comment">
56 56 <td class="lineno new"></td>
57 57 <td class="code no-comment"><pre>%s</pre></td>
58 58 </tr>
59 59 </table>''' % str_
60 60
61 61
62 62 def wrapped_diff(filenode_old, filenode_new, cut_off_limit=None,
63 63 ignore_whitespace=True, line_context=3,
64 64 enable_comments=False):
65 65 """
66 66 returns a wrapped diff into a table, checks for cut_off_limit and presents
67 67 proper message
68 68 """
69 69
70 70 if filenode_old is None:
71 71 filenode_old = FileNode(filenode_new.path, '', EmptyChangeset())
72 72
73 73 if filenode_old.is_binary or filenode_new.is_binary:
74 74 diff = wrap_to_table(_('binary file'))
75 75 stats = (0, 0)
76 76 size = 0
77 77
78 78 elif cut_off_limit != -1 and (cut_off_limit is None or
79 79 (filenode_old.size < cut_off_limit and filenode_new.size < cut_off_limit)):
80 80
81 81 f_gitdiff = get_gitdiff(filenode_old, filenode_new,
82 82 ignore_whitespace=ignore_whitespace,
83 83 context=line_context)
84 84 diff_processor = DiffProcessor(f_gitdiff, format='gitdiff')
85 85
86 86 diff = diff_processor.as_html(enable_comments=enable_comments)
87 87 stats = diff_processor.stat()
88 88 size = len(diff or '')
89 89 else:
90 90 diff = wrap_to_table(_('Changeset was too big and was cut off, use '
91 91 'diff menu to display this diff'))
92 92 stats = (0, 0)
93 93 size = 0
94 94 if not diff:
95 95 submodules = filter(lambda o: isinstance(o, SubModuleNode),
96 96 [filenode_new, filenode_old])
97 97 if submodules:
98 98 diff = wrap_to_table(escape('Submodule %r' % submodules[0]))
99 99 else:
100 100 diff = wrap_to_table(_('No changes detected'))
101 101
102 102 cs1 = filenode_old.changeset.raw_id
103 103 cs2 = filenode_new.changeset.raw_id
104 104
105 105 return size, cs1, cs2, diff, stats
106 106
107 107
108 108 def get_gitdiff(filenode_old, filenode_new, ignore_whitespace=True, context=3):
109 109 """
110 110 Returns git style diff between given ``filenode_old`` and ``filenode_new``.
111 111
112 112 :param ignore_whitespace: ignore whitespaces in diff
113 113 """
114 114 # make sure we pass in default context
115 115 context = context or 3
116 116 submodules = filter(lambda o: isinstance(o, SubModuleNode),
117 117 [filenode_new, filenode_old])
118 118 if submodules:
119 119 return ''
120 120
121 121 for filenode in (filenode_old, filenode_new):
122 122 if not isinstance(filenode, FileNode):
123 123 raise VCSError("Given object should be FileNode object, not %s"
124 124 % filenode.__class__)
125 125
126 126 repo = filenode_new.changeset.repository
127 127 old_raw_id = getattr(filenode_old.changeset, 'raw_id', repo.EMPTY_CHANGESET)
128 128 new_raw_id = getattr(filenode_new.changeset, 'raw_id', repo.EMPTY_CHANGESET)
129 129
130 130 vcs_gitdiff = repo.get_diff(old_raw_id, new_raw_id, filenode_new.path,
131 131 ignore_whitespace, context)
132 132 return vcs_gitdiff
133 133
134 134 NEW_FILENODE = 1
135 135 DEL_FILENODE = 2
136 136 MOD_FILENODE = 3
137 137 RENAMED_FILENODE = 4
138 138 CHMOD_FILENODE = 5
139 139
140 140
141 141 class DiffLimitExceeded(Exception):
142 142 pass
143 143
144 144
145 145 class LimitedDiffContainer(object):
146 146
147 147 def __init__(self, diff_limit, cur_diff_size, diff):
148 148 self.diff = diff
149 149 self.diff_limit = diff_limit
150 150 self.cur_diff_size = cur_diff_size
151 151
152 152 def __iter__(self):
153 153 for l in self.diff:
154 154 yield l
155 155
156 156
157 157 class DiffProcessor(object):
158 158 """
159 159 Give it a unified or git diff and it returns a list of the files that were
160 160 mentioned in the diff together with a dict of meta information that
161 161 can be used to render it in a HTML template.
162 162 """
163 163 _chunk_re = re.compile(r'^@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@(.*)')
164 164 _newline_marker = re.compile(r'^\\ No newline at end of file')
165 165 _git_header_re = re.compile(r"""
166 166 #^diff[ ]--git
167 167 [ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
168 168 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%\n
169 169 ^rename[ ]from[ ](?P<rename_from>\S+)\n
170 170 ^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
171 171 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
172 172 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
173 173 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
174 174 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
175 175 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
176 176 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
177 177 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
178 178 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
179 179 """, re.VERBOSE | re.MULTILINE)
180 180 _hg_header_re = re.compile(r"""
181 181 #^diff[ ]--git
182 182 [ ]a/(?P<a_path>.+?)[ ]b/(?P<b_path>.+?)\n
183 183 (?:^similarity[ ]index[ ](?P<similarity_index>\d+)%(?:\n|$))?
184 184 (?:^rename[ ]from[ ](?P<rename_from>\S+)\n
185 185 ^rename[ ]to[ ](?P<rename_to>\S+)(?:\n|$))?
186 186 (?:^old[ ]mode[ ](?P<old_mode>\d+)\n
187 187 ^new[ ]mode[ ](?P<new_mode>\d+)(?:\n|$))?
188 188 (?:^new[ ]file[ ]mode[ ](?P<new_file_mode>.+)(?:\n|$))?
189 189 (?:^deleted[ ]file[ ]mode[ ](?P<deleted_file_mode>.+)(?:\n|$))?
190 190 (?:^index[ ](?P<a_blob_id>[0-9A-Fa-f]+)
191 191 \.\.(?P<b_blob_id>[0-9A-Fa-f]+)[ ]?(?P<b_mode>.+)?(?:\n|$))?
192 192 (?:^---[ ](a/(?P<a_file>.+)|/dev/null)(?:\n|$))?
193 193 (?:^\+\+\+[ ](b/(?P<b_file>.+)|/dev/null)(?:\n|$))?
194 194 """, re.VERBOSE | re.MULTILINE)
195 195
196 196 def __init__(self, diff, vcs='hg', format='gitdiff', diff_limit=None):
197 197 """
198 198 :param diff: a text in diff format
199 199 :param vcs: type of version controll hg or git
200 200 :param format: format of diff passed, `udiff` or `gitdiff`
201 201 :param diff_limit: define the size of diff that is considered "big"
202 202 based on that parameter cut off will be triggered, set to None
203 203 to show full diff
204 204 """
205 205 if not isinstance(diff, basestring):
206 206 raise Exception('Diff must be a basestring got %s instead' % type(diff))
207 207
208 208 self._diff = diff
209 209 self._format = format
210 210 self.adds = 0
211 211 self.removes = 0
212 212 # calculate diff size
213 213 self.diff_size = len(diff)
214 214 self.diff_limit = diff_limit
215 215 self.cur_diff_size = 0
216 216 self.parsed = False
217 217 self.parsed_diff = []
218 218 self.vcs = vcs
219 219
220 220 if format == 'gitdiff':
221 221 self.differ = self._highlight_line_difflib
222 222 self._parser = self._parse_gitdiff
223 223 else:
224 224 self.differ = self._highlight_line_udiff
225 225 self._parser = self._parse_udiff
226 226
227 227 def _copy_iterator(self):
228 228 """
229 229 make a fresh copy of generator, we should not iterate thru
230 230 an original as it's needed for repeating operations on
231 231 this instance of DiffProcessor
232 232 """
233 233 self.__udiff, iterator_copy = tee(self.__udiff)
234 234 return iterator_copy
235 235
236 236 def _escaper(self, string):
237 237 """
238 238 Escaper for diff escapes special chars and checks the diff limit
239 239
240 240 :param string:
241 241 :type string:
242 242 """
243 243
244 244 self.cur_diff_size += len(string)
245 245
246 246 # escaper get's iterated on each .next() call and it checks if each
247 247 # parsed line doesn't exceed the diff limit
248 248 if self.diff_limit is not None and self.cur_diff_size > self.diff_limit:
249 249 raise DiffLimitExceeded('Diff Limit Exceeded')
250 250
251 251 return safe_unicode(string).replace('&', '&amp;')\
252 252 .replace('<', '&lt;')\
253 253 .replace('>', '&gt;')
254 254
255 255 def _line_counter(self, l):
256 256 """
257 257 Checks each line and bumps total adds/removes for this diff
258 258
259 259 :param l:
260 260 """
261 261 if l.startswith('+') and not l.startswith('+++'):
262 262 self.adds += 1
263 263 elif l.startswith('-') and not l.startswith('---'):
264 264 self.removes += 1
265 265 return safe_unicode(l)
266 266
267 267 def _highlight_line_difflib(self, line, next_):
268 268 """
269 269 Highlight inline changes in both lines.
270 270 """
271 271
272 272 if line['action'] == 'del':
273 273 old, new = line, next_
274 274 else:
275 275 old, new = next_, line
276 276
277 277 oldwords = re.split(r'(\W)', old['line'])
278 278 newwords = re.split(r'(\W)', new['line'])
279 279
280 280 sequence = difflib.SequenceMatcher(None, oldwords, newwords)
281 281
282 282 oldfragments, newfragments = [], []
283 283 for tag, i1, i2, j1, j2 in sequence.get_opcodes():
284 284 oldfrag = ''.join(oldwords[i1:i2])
285 285 newfrag = ''.join(newwords[j1:j2])
286 286 if tag != 'equal':
287 287 if oldfrag:
288 288 oldfrag = '<del>%s</del>' % oldfrag
289 289 if newfrag:
290 290 newfrag = '<ins>%s</ins>' % newfrag
291 291 oldfragments.append(oldfrag)
292 292 newfragments.append(newfrag)
293 293
294 294 old['line'] = "".join(oldfragments)
295 295 new['line'] = "".join(newfragments)
296 296
297 297 def _highlight_line_udiff(self, line, next_):
298 298 """
299 299 Highlight inline changes in both lines.
300 300 """
301 301 start = 0
302 302 limit = min(len(line['line']), len(next_['line']))
303 303 while start < limit and line['line'][start] == next_['line'][start]:
304 304 start += 1
305 305 end = -1
306 306 limit -= start
307 307 while -end <= limit and line['line'][end] == next_['line'][end]:
308 308 end -= 1
309 309 end += 1
310 310 if start or end:
311 311 def do(l):
312 312 last = end + len(l['line'])
313 313 if l['action'] == 'add':
314 314 tag = 'ins'
315 315 else:
316 316 tag = 'del'
317 317 l['line'] = '%s<%s>%s</%s>%s' % (
318 318 l['line'][:start],
319 319 tag,
320 320 l['line'][start:last],
321 321 tag,
322 322 l['line'][last:]
323 323 )
324 324 do(line)
325 325 do(next_)
326 326
327 327 def _get_header(self, diff_chunk):
328 328 """
329 329 parses the diff header, and returns parts, and leftover diff
330 330 parts consists of 14 elements::
331 331
332 332 a_path, b_path, similarity_index, rename_from, rename_to,
333 333 old_mode, new_mode, new_file_mode, deleted_file_mode,
334 334 a_blob_id, b_blob_id, b_mode, a_file, b_file
335 335
336 336 :param diff_chunk:
337 337 :type diff_chunk:
338 338 """
339 339
340 340 if self.vcs == 'git':
341 341 match = self._git_header_re.match(diff_chunk)
342 342 diff = diff_chunk[match.end():]
343 343 return match.groupdict(), imap(self._escaper, diff.splitlines(1))
344 344 elif self.vcs == 'hg':
345 345 match = self._hg_header_re.match(diff_chunk)
346 346 diff = diff_chunk[match.end():]
347 347 return match.groupdict(), imap(self._escaper, diff.splitlines(1))
348 348 else:
349 349 raise Exception('VCS type %s is not supported' % self.vcs)
350 350
351 351 def _clean_line(self, line, command):
352 352 if command in ['+', '-', ' ']:
353 353 #only modify the line if it's actually a diff thing
354 354 line = line[1:]
355 355 return line
356 356
357 357 def _parse_gitdiff(self, inline_diff=True):
358 358 _files = []
359 359 diff_container = lambda arg: arg
360 360
361 361 ##split the diff in chunks of separate --git a/file b/file chunks
362 362 for raw_diff in ('\n' + self._diff).split('\ndiff --git')[1:]:
363 363 binary = False
364 364 binary_msg = 'unknown binary'
365 365 head, diff = self._get_header(raw_diff)
366 366
367 367 if not head['a_file'] and head['b_file']:
368 368 op = 'A'
369 369 elif head['a_file'] and head['b_file']:
370 370 op = 'M'
371 371 elif head['a_file'] and not head['b_file']:
372 372 op = 'D'
373 373 else:
374 374 #probably we're dealing with a binary file 1
375 375 binary = True
376 376 if head['deleted_file_mode']:
377 377 op = 'D'
378 378 stats = ['b', DEL_FILENODE]
379 379 binary_msg = 'deleted binary file'
380 380 elif head['new_file_mode']:
381 381 op = 'A'
382 382 stats = ['b', NEW_FILENODE]
383 383 binary_msg = 'new binary file %s' % head['new_file_mode']
384 384 else:
385 385 if head['new_mode'] and head['old_mode']:
386 386 stats = ['b', CHMOD_FILENODE]
387 387 op = 'M'
388 388 binary_msg = ('modified binary file chmod %s => %s'
389 389 % (head['old_mode'], head['new_mode']))
390 390 elif (head['rename_from'] and head['rename_to']
391 391 and head['rename_from'] != head['rename_to']):
392 392 stats = ['b', RENAMED_FILENODE]
393 393 op = 'M'
394 394 binary_msg = ('file renamed from %s to %s'
395 395 % (head['rename_from'], head['rename_to']))
396 396 else:
397 397 stats = ['b', MOD_FILENODE]
398 398 op = 'M'
399 399 binary_msg = 'modified binary file'
400 400
401 401 if not binary:
402 402 try:
403 403 chunks, stats = self._parse_lines(diff)
404 404 except DiffLimitExceeded:
405 405 diff_container = lambda _diff: LimitedDiffContainer(
406 406 self.diff_limit,
407 407 self.cur_diff_size,
408 408 _diff)
409 409 break
410 410 else:
411 411 chunks = []
412 412 chunks.append([{
413 413 'old_lineno': '',
414 414 'new_lineno': '',
415 415 'action': 'binary',
416 416 'line': binary_msg,
417 417 }])
418 418
419 419 _files.append({
420 420 'filename': head['b_path'],
421 421 'old_revision': head['a_blob_id'],
422 422 'new_revision': head['b_blob_id'],
423 423 'chunks': chunks,
424 424 'operation': op,
425 425 'stats': stats,
426 426 })
427 427
428 428 sorter = lambda info: {'A': 0, 'M': 1, 'D': 2}.get(info['operation'])
429 429
430 430 if inline_diff is False:
431 431 return diff_container(sorted(_files, key=sorter))
432 432
433 433 # highlight inline changes
434 434 for diff_data in _files:
435 435 for chunk in diff_data['chunks']:
436 436 lineiter = iter(chunk)
437 437 try:
438 438 while 1:
439 439 line = lineiter.next()
440 440 if line['action'] not in ['unmod', 'context']:
441 441 nextline = lineiter.next()
442 442 if nextline['action'] in ['unmod', 'context'] or \
443 443 nextline['action'] == line['action']:
444 444 continue
445 445 self.differ(line, nextline)
446 446 except StopIteration:
447 447 pass
448 448
449 449 return diff_container(sorted(_files, key=sorter))
450 450
451 451 def _parse_udiff(self, inline_diff=True):
452 452 raise NotImplementedError()
453 453
454 454 def _parse_lines(self, diff):
455 455 """
456 456 Parse the diff an return data for the template.
457 457 """
458 458
459 459 lineiter = iter(diff)
460 460 stats = [0, 0]
461 461
462 462 try:
463 463 chunks = []
464 464 line = lineiter.next()
465 465
466 466 while line:
467 467 lines = []
468 468 chunks.append(lines)
469 469
470 470 match = self._chunk_re.match(line)
471 471
472 472 if not match:
473 473 break
474 474
475 475 gr = match.groups()
476 476 (old_line, old_end,
477 477 new_line, new_end) = [int(x or 1) for x in gr[:-1]]
478 478 old_line -= 1
479 479 new_line -= 1
480 480
481 481 context = len(gr) == 5
482 482 old_end += old_line
483 483 new_end += new_line
484 484
485 485 if context:
486 486 # skip context only if it's first line
487 487 if int(gr[0]) > 1:
488 488 lines.append({
489 489 'old_lineno': '...',
490 490 'new_lineno': '...',
491 491 'action': 'context',
492 492 'line': line,
493 493 })
494 494
495 495 line = lineiter.next()
496 496
497 497 while old_line < old_end or new_line < new_end:
498 498 command = ' '
499 499 if line:
500 500 command = line[0]
501 501
502 502 affects_old = affects_new = False
503 503
504 504 # ignore those if we don't expect them
505 505 if command in '#@':
506 506 continue
507 507 elif command == '+':
508 508 affects_new = True
509 509 action = 'add'
510 510 stats[0] += 1
511 511 elif command == '-':
512 512 affects_old = True
513 513 action = 'del'
514 514 stats[1] += 1
515 515 else:
516 516 affects_old = affects_new = True
517 517 action = 'unmod'
518 518
519 519 if not self._newline_marker.match(line):
520 520 old_line += affects_old
521 521 new_line += affects_new
522 522 lines.append({
523 523 'old_lineno': affects_old and old_line or '',
524 524 'new_lineno': affects_new and new_line or '',
525 525 'action': action,
526 526 'line': self._clean_line(line, command)
527 527 })
528 528
529 529 line = lineiter.next()
530 530
531 531 if self._newline_marker.match(line):
532 532 # we need to append to lines, since this is not
533 533 # counted in the line specs of diff
534 534 lines.append({
535 535 'old_lineno': '...',
536 536 'new_lineno': '...',
537 537 'action': 'context',
538 538 'line': self._clean_line(line, command)
539 539 })
540 540
541 541 except StopIteration:
542 542 pass
543 543 return chunks, stats
544 544
545 545 def _safe_id(self, idstring):
546 546 """Make a string safe for including in an id attribute.
547 547
548 548 The HTML spec says that id attributes 'must begin with
549 549 a letter ([A-Za-z]) and may be followed by any number
550 550 of letters, digits ([0-9]), hyphens ("-"), underscores
551 551 ("_"), colons (":"), and periods (".")'. These regexps
552 552 are slightly over-zealous, in that they remove colons
553 553 and periods unnecessarily.
554 554
555 555 Whitespace is transformed into underscores, and then
556 556 anything which is not a hyphen or a character that
557 557 matches \w (alphanumerics and underscore) is removed.
558 558
559 559 """
560 560 # Transform all whitespace to underscore
561 561 idstring = re.sub(r'\s', "_", '%s' % idstring)
562 562 # Remove everything that is not a hyphen or a member of \w
563 563 idstring = re.sub(r'(?!-)\W', "", idstring).lower()
564 564 return idstring
565 565
566 566 def prepare(self, inline_diff=True):
567 567 """
568 568 Prepare the passed udiff for HTML rendering. It'l return a list
569 569 of dicts with diff information
570 570 """
571 571 parsed = self._parser(inline_diff=inline_diff)
572 572 self.parsed = True
573 573 self.parsed_diff = parsed
574 574 return parsed
575 575
576 576 def as_raw(self, diff_lines=None):
577 577 """
578 578 Returns raw string diff
579 579 """
580 580 return self._diff
581 581 #return u''.join(imap(self._line_counter, self._diff.splitlines(1)))
582 582
583 583 def as_html(self, table_class='code-difftable', line_class='line',
584 584 new_lineno_class='lineno old', old_lineno_class='lineno new',
585 585 code_class='code', enable_comments=False, parsed_lines=None):
586 586 """
587 587 Return given diff as html table with customized css classes
588 588 """
589 589 def _link_to_if(condition, label, url):
590 590 """
591 591 Generates a link if condition is meet or just the label if not.
592 592 """
593 593
594 594 if condition:
595 595 return '''<a href="%(url)s">%(label)s</a>''' % {
596 596 'url': url,
597 597 'label': label
598 598 }
599 599 else:
600 600 return label
601 601 if not self.parsed:
602 602 self.prepare()
603 603
604 604 diff_lines = self.parsed_diff
605 605 if parsed_lines:
606 606 diff_lines = parsed_lines
607 607
608 608 _html_empty = True
609 609 _html = []
610 610 _html.append('''<table class="%(table_class)s">\n''' % {
611 611 'table_class': table_class
612 612 })
613 613
614 614 for diff in diff_lines:
615 615 for line in diff['chunks']:
616 616 _html_empty = False
617 617 for change in line:
618 618 _html.append('''<tr class="%(lc)s %(action)s">\n''' % {
619 619 'lc': line_class,
620 620 'action': change['action']
621 621 })
622 622 anchor_old_id = ''
623 623 anchor_new_id = ''
624 624 anchor_old = "%(filename)s_o%(oldline_no)s" % {
625 625 'filename': self._safe_id(diff['filename']),
626 626 'oldline_no': change['old_lineno']
627 627 }
628 628 anchor_new = "%(filename)s_n%(oldline_no)s" % {
629 629 'filename': self._safe_id(diff['filename']),
630 630 'oldline_no': change['new_lineno']
631 631 }
632 632 cond_old = (change['old_lineno'] != '...' and
633 633 change['old_lineno'])
634 634 cond_new = (change['new_lineno'] != '...' and
635 635 change['new_lineno'])
636 636 if cond_old:
637 637 anchor_old_id = 'id="%s"' % anchor_old
638 638 if cond_new:
639 639 anchor_new_id = 'id="%s"' % anchor_new
640 640 ###########################################################
641 641 # OLD LINE NUMBER
642 642 ###########################################################
643 643 _html.append('''\t<td %(a_id)s class="%(olc)s">''' % {
644 644 'a_id': anchor_old_id,
645 645 'olc': old_lineno_class
646 646 })
647 647
648 648 _html.append('''%(link)s''' % {
649 649 'link': _link_to_if(True, change['old_lineno'],
650 650 '#%s' % anchor_old)
651 651 })
652 652 _html.append('''</td>\n''')
653 653 ###########################################################
654 654 # NEW LINE NUMBER
655 655 ###########################################################
656 656
657 657 _html.append('''\t<td %(a_id)s class="%(nlc)s">''' % {
658 658 'a_id': anchor_new_id,
659 659 'nlc': new_lineno_class
660 660 })
661 661
662 662 _html.append('''%(link)s''' % {
663 663 'link': _link_to_if(True, change['new_lineno'],
664 664 '#%s' % anchor_new)
665 665 })
666 666 _html.append('''</td>\n''')
667 667 ###########################################################
668 668 # CODE
669 669 ###########################################################
670 670 comments = '' if enable_comments else 'no-comment'
671 671 _html.append('''\t<td class="%(cc)s %(inc)s">''' % {
672 672 'cc': code_class,
673 673 'inc': comments
674 674 })
675 675 _html.append('''\n\t\t<pre>%(code)s</pre>\n''' % {
676 676 'code': change['line']
677 677 })
678 678
679 679 _html.append('''\t</td>''')
680 680 _html.append('''\n</tr>\n''')
681 681 _html.append('''</table>''')
682 682 if _html_empty:
683 683 return None
684 684 return ''.join(_html)
685 685
686 686 def stat(self):
687 687 """
688 688 Returns tuple of added, and removed lines for this instance
689 689 """
690 690 return self.adds, self.removes
691 691
692 692
693 693 class InMemoryBundleRepo(bundlerepository):
694 694 def __init__(self, ui, path, bundlestream):
695 695 self._tempparent = None
696 696 localrepo.localrepository.__init__(self, ui, path)
697 697 self.ui.setconfig('phases', 'publish', False)
698 698
699 699 self.bundle = bundlestream
700 700
701 701 # dict with the mapping 'filename' -> position in the bundle
702 702 self.bundlefilespos = {}
703 703
704 704
705 705 def differ(org_repo, org_ref, other_repo, other_ref, discovery_data=None,
706 706 remote_compare=False, context=3, ignore_whitespace=False):
707 707 """
708 708 General differ between branches, bookmarks, revisions of two remote or
709 709 local but related repositories
710 710
711 711 :param org_repo:
712 712 :param org_ref:
713 713 :param other_repo:
714 714 :type other_repo:
715 715 :type other_ref:
716 716 """
717 717
718 718 org_repo_scm = org_repo.scm_instance
719 719 other_repo_scm = other_repo.scm_instance
720 720
721 721 org_repo = org_repo_scm._repo
722 722 other_repo = other_repo_scm._repo
723 723
724 724 org_ref = org_ref[1]
725 725 other_ref = other_ref[1]
726 726
727 727 if org_repo == other_repo:
728 728 log.debug('running diff between %s@%s and %s@%s'
729 729 % (org_repo.path, org_ref, other_repo.path, other_ref))
730 730 _diff = org_repo_scm.get_diff(rev1=org_ref, rev2=other_ref,
731 731 ignore_whitespace=ignore_whitespace, context=context)
732 732 return _diff
733 733
734 734 elif remote_compare:
735 735 opts = diffopts(git=True, ignorews=ignore_whitespace, context=context)
736 736 common, incoming, rheads = discovery_data
737 737 org_repo_peer = localrepo.locallegacypeer(org_repo.local())
738 738 # create a bundle (uncompressed if other repo is not local)
739 739 if org_repo_peer.capable('getbundle'):
740 740 # disable repo hooks here since it's just bundle !
741 741 # patch and reset hooks section of UI config to not run any
742 742 # hooks on fetching archives with subrepos
743 743 for k, _ in org_repo.ui.configitems('hooks'):
744 744 org_repo.ui.setconfig('hooks', k, None)
745 745 unbundle = org_repo.getbundle('incoming', common=None,
746 746 heads=None)
747 747
748 748 buf = BytesIO()
749 749 while True:
750 750 chunk = unbundle._stream.read(1024 * 4)
751 751 if not chunk:
752 752 break
753 753 buf.write(chunk)
754 754
755 755 buf.seek(0)
756 756 # replace chunked _stream with data that can do tell() and seek()
757 757 unbundle._stream = buf
758 758
759 759 ui = make_ui('db')
760 760 bundlerepo = InMemoryBundleRepo(ui, path=org_repo.root,
761 761 bundlestream=unbundle)
762 762
763 763 return ''.join(patch.diff(bundlerepo,
764 764 node1=other_repo[other_ref].node(),
765 765 node2=org_repo[org_ref].node(),
766 766 opts=opts))
767 767
768 return '' No newline at end of file
768 return ''
@@ -1,122 +1,122 b''
1 1 import datetime
2 2 import functools
3 3 import decimal
4 4 import imp
5 5
6 6 __all__ = ['json', 'simplejson', 'stdlibjson']
7 7
8 8
9 9 def _is_aware(value):
10 10 """
11 11 Determines if a given datetime.time is aware.
12 12
13 13 The logic is described in Python's docs:
14 14 http://docs.python.org/library/datetime.html#datetime.tzinfo
15 15 """
16 16 return (value.tzinfo is not None
17 17 and value.tzinfo.utcoffset(value) is not None)
18 18
19 19
20 20 def _obj_dump(obj):
21 21 """
22 22 Custom function for dumping objects to JSON, if obj has __json__ attribute
23 23 or method defined it will be used for serialization
24 24
25 25 :param obj:
26 26 """
27 27
28 28 if isinstance(obj, complex):
29 29 return [obj.real, obj.imag]
30 30 # See "Date Time String Format" in the ECMA-262 specification.
31 31 # some code borrowed from django 1.4
32 32 elif isinstance(obj, datetime.datetime):
33 33 r = obj.isoformat()
34 34 if obj.microsecond:
35 35 r = r[:23] + r[26:]
36 36 if r.endswith('+00:00'):
37 37 r = r[:-6] + 'Z'
38 38 return r
39 39 elif isinstance(obj, datetime.date):
40 40 return obj.isoformat()
41 41 elif isinstance(obj, decimal.Decimal):
42 42 return str(obj)
43 43 elif isinstance(obj, datetime.time):
44 44 if _is_aware(obj):
45 45 raise ValueError("JSON can't represent timezone-aware times.")
46 46 r = obj.isoformat()
47 47 if obj.microsecond:
48 48 r = r[:12]
49 49 return r
50 50 elif isinstance(obj, set):
51 51 return list(obj)
52 52 elif hasattr(obj, '__json__'):
53 53 if callable(obj.__json__):
54 54 return obj.__json__()
55 55 else:
56 56 return obj.__json__
57 57 else:
58 58 raise NotImplementedError
59 59
60 60
61 61 # Import simplejson
62 62 try:
63 63 # import simplejson initially
64 64 _sj = imp.load_module('_sj', *imp.find_module('simplejson'))
65 65
66 66 def extended_encode(obj):
67 67 try:
68 68 return _obj_dump(obj)
69 69 except NotImplementedError:
70 70 pass
71 71 raise TypeError("%r is not JSON serializable" % (obj,))
72 72 # we handle decimals our own it makes unified behavior of json vs
73 73 # simplejson
74 74 sj_version = [int(x) for x in _sj.__version__.split('.')]
75 75 major, minor = sj_version[0], sj_version[1]
76 76 if major < 2 or (major == 2 and minor < 1):
77 77 # simplejson < 2.1 doesnt support use_decimal
78 78 _sj.dumps = functools.partial(_sj.dumps,
79 79 default=extended_encode)
80 80 _sj.dump = functools.partial(_sj.dump,
81 81 default=extended_encode)
82 82 else:
83 83 _sj.dumps = functools.partial(_sj.dumps,
84 84 default=extended_encode,
85 85 use_decimal=False)
86 86 _sj.dump = functools.partial(_sj.dump,
87 87 default=extended_encode,
88 88 use_decimal=False)
89 89 simplejson = _sj
90 90
91 91 except ImportError:
92 92 # no simplejson set it to None
93 93 simplejson = None
94 94
95 95
96 96 try:
97 97 # simplejson not found try out regular json module
98 98 _json = imp.load_module('_json', *imp.find_module('json'))
99 99
100 100 # extended JSON encoder for json
101 101 class ExtendedEncoder(_json.JSONEncoder):
102 102 def default(self, obj):
103 103 try:
104 104 return _obj_dump(obj)
105 105 except NotImplementedError:
106 106 pass
107 107 raise TypeError("%r is not JSON serializable" % (obj,))
108 108 # monkey-patch JSON encoder to use extended version
109 109 _json.dumps = functools.partial(_json.dumps, cls=ExtendedEncoder)
110 110 _json.dump = functools.partial(_json.dump, cls=ExtendedEncoder)
111 111
112 112 stdlibjson = _json
113 113 except ImportError:
114 114 stdlibjson = None
115 115
116 116 # set all available json modules
117 117 if simplejson:
118 118 json = _sj
119 119 elif stdlibjson:
120 120 json = _json
121 121 else:
122 raise ImportError('Could not find any json modules') No newline at end of file
122 raise ImportError('Could not find any json modules')
@@ -1,291 +1,291 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Edit repository')} ${c.repo_info.repo_name} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${h.link_to(_('Repositories'),h.url('repos'))}
12 12 &raquo;
13 13 ${_('edit')} &raquo; ${h.link_to(c.repo_info.just_name,h.url('summary_home',repo_name=c.repo_name))}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('admin')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box box-left">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='put')}
27 27 <div class="form">
28 28 <!-- fields -->
29 29 <div class="fields">
30 30 <div class="field">
31 31 <div class="label">
32 32 <label for="repo_name">${_('Name')}:</label>
33 33 </div>
34 34 <div class="input">
35 35 ${h.text('repo_name',class_="medium")}
36 36 </div>
37 37 </div>
38 38 <div class="field">
39 39 <div class="label">
40 40 <label for="clone_uri">${_('Clone uri')}:</label>
41 41 </div>
42 42 <div class="input">
43 43 ${h.text('clone_uri',class_="medium")}
44 44 <span class="help-block">${_('Optional http[s] url from which repository should be cloned.')}</span>
45 45 </div>
46 46 </div>
47 47 <div class="field">
48 48 <div class="label">
49 49 <label for="repo_group">${_('Repository group')}:</label>
50 50 </div>
51 51 <div class="input">
52 52 ${h.select('repo_group','',c.repo_groups,class_="medium")}
53 53 <span class="help-block">${_('Optional select a group to put this repository into.')}</span>
54 54 </div>
55 55 </div>
56 56 <div class="field">
57 57 <div class="label">
58 58 <label for="repo_type">${_('Type')}:</label>
59 59 </div>
60 60 <div class="input">
61 61 ${h.select('repo_type','hg',c.backends,class_="medium")}
62 62 </div>
63 63 </div>
64 64 <div class="field">
65 65 <div class="label">
66 66 <label for="landing_rev">${_('Landing revision')}:</label>
67 67 </div>
68 68 <div class="input">
69 69 ${h.select('landing_rev','',c.landing_revs,class_="medium")}
70 70 <span class="help-block">${_('Default revision for files page, downloads, whoosh and readme')}</span>
71 71 </div>
72 72 </div>
73 73 <div class="field">
74 74 <div class="label label-textarea">
75 75 <label for="description">${_('Description')}:</label>
76 76 </div>
77 77 <div class="textarea text-area editor">
78 78 ${h.textarea('description')}
79 79 <span class="help-block">${_('Keep it short and to the point. Use a README file for longer descriptions.')}</span>
80 80 </div>
81 81 </div>
82 82
83 83 <div class="field">
84 84 <div class="label label-checkbox">
85 85 <label for="private">${_('Private repository')}:</label>
86 86 </div>
87 87 <div class="checkboxes">
88 88 ${h.checkbox('private',value="True")}
89 89 <span class="help-block">${_('Private repositories are only visible to people explicitly added as collaborators.')}</span>
90 90 </div>
91 91 </div>
92 92 <div class="field">
93 93 <div class="label label-checkbox">
94 94 <label for="enable_statistics">${_('Enable statistics')}:</label>
95 95 </div>
96 96 <div class="checkboxes">
97 97 ${h.checkbox('enable_statistics',value="True")}
98 98 <span class="help-block">${_('Enable statistics window on summary page.')}</span>
99 99 </div>
100 100 </div>
101 101 <div class="field">
102 102 <div class="label label-checkbox">
103 103 <label for="enable_downloads">${_('Enable downloads')}:</label>
104 104 </div>
105 105 <div class="checkboxes">
106 106 ${h.checkbox('enable_downloads',value="True")}
107 107 <span class="help-block">${_('Enable download menu on summary page.')}</span>
108 108 </div>
109 109 </div>
110 110 <div class="field">
111 111 <div class="label label-checkbox">
112 112 <label for="enable_locking">${_('Enable locking')}:</label>
113 113 </div>
114 114 <div class="checkboxes">
115 115 ${h.checkbox('enable_locking',value="True")}
116 116 <span class="help-block">${_('Enable lock-by-pulling on repository.')}</span>
117 117 </div>
118 118 </div>
119 119 <div class="field">
120 120 <div class="label">
121 121 <label for="user">${_('Owner')}:</label>
122 122 </div>
123 123 <div class="input input-medium ac">
124 124 <div class="perm_ac">
125 125 ${h.text('user',class_='yui-ac-input')}
126 126 <span class="help-block">${_('Change owner of this repository.')}</span>
127 127 <div id="owner_container"></div>
128 128 </div>
129 129 </div>
130 130 </div>
131 131
132 132 <div class="field">
133 133 <div class="label">
134 134 <label for="input">${_('Permissions')}:</label>
135 135 </div>
136 136 <div class="input">
137 137 <%include file="repo_edit_perms.html"/>
138 138 </div>
139 139
140 140 <div class="buttons">
141 141 ${h.submit('save',_('Save'),class_="ui-btn large")}
142 142 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
143 143 </div>
144 144 </div>
145 145 </div>
146 146 </div>
147 147 ${h.end_form()}
148 148 </div>
149 149
150 150 <div class="box box-right">
151 151 <div class="title">
152 152 <h5>${_('Administration')}</h5>
153 153 </div>
154 154
155 155 <h3>${_('Statistics')}</h3>
156 156 ${h.form(url('repo_stats', repo_name=c.repo_info.repo_name),method='delete')}
157 157 <div class="form">
158 158 <div class="fields">
159 159 ${h.submit('reset_stats_%s' % c.repo_info.repo_name,_('Reset current statistics'),class_="ui-btn",onclick="return confirm('"+_('Confirm to remove current statistics')+"');")}
160 160 <div class="field" style="border:none;color:#888">
161 161 <ul>
162 162 <li>${_('Fetched to rev')}: ${c.stats_revision}/${c.repo_last_rev}</li>
163 163 <li>${_('Stats gathered')}: ${c.stats_percentage}%</li>
164 164 </ul>
165 165 </div>
166 166 </div>
167 167 </div>
168 168 ${h.end_form()}
169 169
170 170 %if c.repo_info.clone_uri:
171 171 <h3>${_('Remote')}</h3>
172 172 ${h.form(url('repo_pull', repo_name=c.repo_info.repo_name),method='put')}
173 173 <div class="form">
174 174 <div class="fields">
175 175 ${h.submit('remote_pull_%s' % c.repo_info.repo_name,_('Pull changes from remote location'),class_="ui-btn",onclick="return confirm('"+_('Confirm to pull changes from remote side')+"');")}
176 176 <div class="field" style="border:none">
177 177 <ul>
178 178 <li><a href="${c.repo_info.clone_uri}">${c.repo_info.clone_uri}</a></li>
179 179 </ul>
180 180 </div>
181 181 </div>
182 182 </div>
183 183 ${h.end_form()}
184 184 %endif
185 185
186 186 <h3>${_('Cache')}</h3>
187 187 ${h.form(url('repo_cache', repo_name=c.repo_info.repo_name),method='delete')}
188 188 <div class="form">
189 189 <div class="fields">
190 190 ${h.submit('reset_cache_%s' % c.repo_info.repo_name,_('Invalidate repository cache'),class_="ui-btn",onclick="return confirm('"+_('Confirm to invalidate repository cache')+"');")}
191 191 <div class="field" style="border:none;color:#888">
192 192 <ul>
193 193 <li>${_('Manually invalidate cache for this repository. On first access repository will be cached again')}
194 194 </li>
195 195 </ul>
196 196 </div>
197 197 <div class="field" style="border:none;">
198 198 ${_('List of cached values')}
199 199 <table>
200 200 <tr>
201 201 <th>${_('Prefix')}</th>
202 202 <th>${_('Key')}</th>
203 203 <th>${_('Active')}</th>
204 204 </tr>
205 205 %for cache in c.repo_info.cache_keys:
206 206 <tr>
207 <td>${cache.prefix or '-'}</td>
207 <td>${cache.prefix or '-'}</td>
208 208 <td>${cache.cache_key}</td>
209 209 <td>${h.bool2icon(cache.cache_active)}</td>
210 210 </tr>
211 211 %endfor
212 212 </table>
213 213 </div>
214 214 </div>
215 215 </div>
216 216 ${h.end_form()}
217 217
218 218 <h3>${_('Public journal')}</h3>
219 219 ${h.form(url('repo_public_journal', repo_name=c.repo_info.repo_name),method='put')}
220 220 <div class="form">
221 221 ${h.hidden('auth_token',str(h.get_token()))}
222 222 <div class="field">
223 223 %if c.in_public_journal:
224 224 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Remove from public journal'),class_="ui-btn")}
225 225 %else:
226 226 ${h.submit('set_public_%s' % c.repo_info.repo_name,_('Add to public journal'),class_="ui-btn")}
227 227 %endif
228 228 </div>
229 229 <div class="field" style="border:none;color:#888">
230 230 <ul>
231 231 <li>${_('All actions made on this repository will be accessible to everyone in public journal')}
232 232 </li>
233 233 </ul>
234 234 </div>
235 235 </div>
236 236 ${h.end_form()}
237 237
238 238 <h3>${_('Locking')}</h3>
239 239 ${h.form(url('repo_locking', repo_name=c.repo_info.repo_name),method='put')}
240 240 <div class="form">
241 241 <div class="fields">
242 242 %if c.repo_info.locked[0]:
243 243 ${h.submit('set_unlock' ,_('Unlock locked repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to unlock repository')+"');")}
244 244 ${'Locked by %s on %s' % (h.person_by_id(c.repo_info.locked[0]),h.fmt_date(h.time_to_datetime(c.repo_info.locked[1])))}
245 245 %else:
246 246 ${h.submit('set_lock',_('lock repo'),class_="ui-btn",onclick="return confirm('"+_('Confirm to lock repository')+"');")}
247 247 ${_('Repository is not locked')}
248 248 %endif
249 249 </div>
250 250 <div class="field" style="border:none;color:#888">
251 251 <ul>
252 252 <li>${_('Force locking on repository. Works only when anonymous access is disabled')}
253 253 </li>
254 254 </ul>
255 255 </div>
256 256 </div>
257 257 ${h.end_form()}
258 258
259 259 <h3>${_('Set as fork of')}</h3>
260 260 ${h.form(url('repo_as_fork', repo_name=c.repo_info.repo_name),method='put')}
261 261 <div class="form">
262 262 <div class="fields">
263 263 ${h.select('id_fork_of','',c.repos_list,class_="medium")}
264 264 ${h.submit('set_as_fork_%s' % c.repo_info.repo_name,_('set'),class_="ui-btn",)}
265 265 </div>
266 266 <div class="field" style="border:none;color:#888">
267 267 <ul>
268 268 <li>${_('''Manually set this repository as a fork of another from the list''')}</li>
269 269 </ul>
270 270 </div>
271 271 </div>
272 272 ${h.end_form()}
273 273
274 274 <h3>${_('Delete')}</h3>
275 275 ${h.form(url('repo', repo_name=c.repo_info.repo_name),method='delete')}
276 276 <div class="form">
277 277 <div class="fields">
278 278 ${h.submit('remove_%s' % c.repo_info.repo_name,_('Remove this repository'),class_="ui-btn red",onclick="return confirm('"+_('Confirm to delete this repository')+"');")}
279 279 </div>
280 280 <div class="field" style="border:none;color:#888">
281 281 <ul>
282 282 <li>${_('''This repository will be renamed in a special way in order to be unaccesible for RhodeCode and VCS systems.
283 283 If you need fully delete it from filesystem please do it manually''')}
284 284 </li>
285 285 </ul>
286 286 </div>
287 287 </div>
288 288 ${h.end_form()}
289 289 </div>
290 290
291 291 </%def>
@@ -1,228 +1,228 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Edit users group')} ${c.users_group.users_group_name} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${h.link_to(_('UsersGroups'),h.url('users_groups'))}
12 12 &raquo;
13 13 ${_('edit')} "${c.users_group.users_group_name}"
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('admin')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box box-left">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 <!-- end box / title -->
27 27 ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')}
28 28 <div class="form">
29 29 <!-- fields -->
30 30 <div class="fields">
31 31 <div class="field">
32 32 <div class="label">
33 33 <label for="users_group_name">${_('Group name')}:</label>
34 34 </div>
35 35 <div class="input">
36 36 ${h.text('users_group_name',class_='small')}
37 37 </div>
38 38 </div>
39 39
40 40 <div class="field">
41 41 <div class="label label-checkbox">
42 42 <label for="users_group_active">${_('Active')}:</label>
43 43 </div>
44 44 <div class="checkboxes">
45 45 ${h.checkbox('users_group_active',value=True)}
46 46 </div>
47 47 </div>
48 48 <div class="field">
49 49 <div class="label">
50 50 <label for="users_group_active">${_('Members')}:</label>
51 51 </div>
52 52 <div class="select">
53 53 <table>
54 54 <tr>
55 55 <td>
56 56 <div>
57 57 <div style="float:left">
58 58 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div>
59 59 ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")}
60 60 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
61 61 ${_('Remove all elements')}
62 62 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
63 63 </div>
64 64 </div>
65 65 <div style="float:left;width:20px;padding-top:50px">
66 66 <img alt="add" id="add_element"
67 67 style="padding:2px;cursor:pointer"
68 68 src="${h.url('/images/icons/arrow_left.png')}"/>
69 69 <br />
70 70 <img alt="remove" id="remove_element"
71 71 style="padding:2px;cursor:pointer"
72 72 src="${h.url('/images/icons/arrow_right.png')}"/>
73 73 </div>
74 74 <div style="float:left">
75 75 <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div>
76 76 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
77 77 <div id="add_all_elements" style="cursor:pointer;text-align:center">
78 78 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
79 79 ${_('Add all elements')}
80 80 </div>
81 81 </div>
82 82 </div>
83 83 </td>
84 84 </tr>
85 85 </table>
86 86 </div>
87 87
88 88 </div>
89 89 <div class="buttons">
90 90 ${h.submit('save',_('save'),class_="ui-btn large")}
91 91 </div>
92 92 </div>
93 93 </div>
94 94 ${h.end_form()}
95 95 </div>
96 96
97 97 <div class="box box-right">
98 98 <!-- box / title -->
99 99 <div class="title">
100 100 <h5>${_('Permissions')}</h5>
101 101 </div>
102 102 ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
103 103 <div class="form">
104 104 <!-- fields -->
105 105 <div class="fields">
106 106 <div class="field">
107 107 <div class="label label-checkbox">
108 108 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
109 109 </div>
110 110 <div class="checkboxes">
111 111 ${h.checkbox('inherit_default_permissions',value=True)}
112 112 </div>
113 113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
114 114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
115 115 </div>
116 116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
117 117 <div class="field">
118 118 <div class="label label-checkbox">
119 119 <label for="create_repo_perm">${_('Create repositories')}:</label>
120 120 </div>
121 121 <div class="checkboxes">
122 122 ${h.checkbox('create_repo_perm',value=True)}
123 123 </div>
124 124 </div>
125 125 <div class="field">
126 126 <div class="label label-checkbox">
127 127 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
128 128 </div>
129 129 <div class="checkboxes">
130 130 ${h.checkbox('fork_repo_perm',value=True)}
131 131 </div>
132 132 </div>
133 133 </div>
134 134 <div class="buttons">
135 135 ${h.submit('save',_('Save'),class_="ui-btn large")}
136 136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
137 137 </div>
138 138 </div>
139 139 </div>
140 140 ${h.end_form()}
141 141 </div>
142 142
143 143 <div class="box box-right">
144 144 <!-- box / title -->
145 145 <div class="title">
146 146 <h5>${_('Group members')}</h5>
147 147 </div>
148 148
149 149 <div class="group_members_wrap">
150 150 % if c.group_members_obj:
151 151 <ul class="group_members">
152 152 %for user in c.group_members_obj:
153 153 <li>
154 154 <div class="group_member">
155 155 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
156 156 <div>${h.link_to(user.username, h.url('edit_user',id=user.user_id))}</div>
157 157 <div>${user.full_name}</div>
158 158 </div>
159 159 </li>
160 160 %endfor
161 161 </ul>
162 162 %else:
163 163 <span class="empty_data">${_('No members yet')}</span>
164 %endif
164 %endif
165 165 </div>
166 166 </div>
167 167
168 168 <div class="box box-left">
169 169 <!-- box / title -->
170 170 <div class="title">
171 171 <h5>${_('Permissions defined for this group')}</h5>
172 172 </div>
173 173 ## permissions overview
174 174 <div id="perms" class="table">
175 175 %for section in sorted(c.users_group.permissions.keys()):
176 176 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
177 177 %if not c.users_group.permissions:
178 178 <span class="empty_data">${_('No permissions set yet')}</span>
179 179 %else:
180 180 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
181 181 <table id="tbl_list_repository">
182 182 <thead>
183 183 <tr>
184 184 <th class="left">${_('Name')}</th>
185 185 <th class="left">${_('Permission')}</th>
186 186 <th class="left">${_('Edit Permission')}</th>
187 187 </thead>
188 188 <tbody>
189 189 %for k in c.users_group.permissions[section]:
190 190 <%
191 191 section_perm = c.users_group.permissions[section].get(k)
192 192 _perm = section_perm.split('.')[-1]
193 193 %>
194 194 <tr>
195 195 <td>
196 196 %if section == 'repositories':
197 197 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
198 198 %elif section == 'repositories_groups':
199 199 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
200 200 %endif
201 201 </td>
202 202 <td>
203 203 <span class="perm_tag ${_perm}">${section_perm}</span>
204 204 </td>
205 205 <td>
206 206 %if section == 'repositories':
207 207 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
208 208 %elif section == 'repositories_groups':
209 209 <a href="${h.url('edit_repos_group',id=k,anchor='permissions_manage')}">${_('edit')}</a>
210 210 %else:
211 211 --
212 212 %endif
213 213 </td>
214 214 </tr>
215 215 %endfor
216 216 </tbody>
217 217 </table>
218 218 </div>
219 219 %endif
220 220 %endfor
221 221 </div>
222 222 </div>
223 223
224 224
225 225 <script type="text/javascript">
226 226 MultiSelectWidget('users_group_members','available_members','edit_users_group');
227 227 </script>
228 228 </%def>
@@ -1,305 +1,305 b''
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4
5 5 <%def name="title()">
6 6 ${_('%s Changelog') % c.repo_name} - ${c.rhodecode_name}
7 7 </%def>
8 8
9 9 <%def name="breadcrumbs_links()">
10 10 ${h.link_to(_(u'Home'),h.url('/'))}
11 11 &raquo;
12 12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 13 &raquo;
14 14 <% size = c.size if c.size <= c.total_cs else c.total_cs %>
15 15 ${_('Changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)}
16 16 </%def>
17 17
18 18 <%def name="page_nav()">
19 19 ${self.menu('changelog')}
20 20 </%def>
21 21
22 22 <%def name="main()">
23 23 <div class="box">
24 24 <!-- box / title -->
25 25 <div class="title">
26 26 ${self.breadcrumbs()}
27 27 </div>
28 28 <div class="table">
29 29 % if c.pagination:
30 30 <div id="graph">
31 31 <div id="graph_nodes">
32 32 <canvas id="graph_canvas"></canvas>
33 33 </div>
34 34 <div id="graph_content">
35 35 <div class="info_box" style="clear: both;padding: 10px 6px;vertical-align: right;text-align: right;">
36 36 <a href="#" class="ui-btn small" id="rev_range_container" style="display:none"></a>
37 37 <a href="#" class="ui-btn small" id="rev_range_clear" style="display:none">${_('Clear selection')}</a>
38 38
39 39 %if c.rhodecode_db_repo.fork:
40 40 <a title="${_('compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}" href="${h.url('compare_url',repo_name=c.repo_name,org_ref_type='branch',org_ref=request.GET.get('branch') or 'default',other_ref_type='branch',other_ref='default',repo=c.rhodecode_db_repo.fork.repo_name)}" class="ui-btn small">${_('Compare fork with parent')}</a>
41 41 %endif
42 42 %if h.is_hg(c.rhodecode_repo):
43 43 <a id="open_new_pr" href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="ui-btn small">${_('Open new pull request')}</a>
44 44 %endif
45 45 </div>
46 46 <div class="container_header">
47 47 ${h.form(h.url.current(),method='get')}
48 48 <div class="info_box" style="float:left">
49 49 ${h.submit('set',_('Show'),class_="ui-btn")}
50 50 ${h.text('size',size=1,value=c.size)}
51 51 ${_('revisions')}
52 52 </div>
53 53 ${h.end_form()}
54 54 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
55 55 </div>
56 56
57 57 %for cnt,cs in enumerate(c.pagination):
58 58 <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
59 59 <div class="left">
60 60 <div>
61 61 ${h.checkbox(cs.raw_id,class_="changeset_range")}
62 62 <span class="tooltip" title="${h.tooltip(h.age(cs.date))}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
63 63 </div>
64 64 <div class="author">
65 65 <div class="gravatar">
66 66 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(cs.author),16)}"/>
67 67 </div>
68 68 <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
69 69 </div>
70 70 <div class="date">${h.fmt_date(cs.date)}</div>
71 71 </div>
72 72 <div class="mid">
73 73 <div class="message">${h.urlify_commit(cs.message, c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
74 74 <div class="expand"><span class="expandtext">&darr; ${_('show more')} &darr;</span></div>
75 75 </div>
76 76 <div class="right">
77 77 <div class="changes">
78 78 <div id="changed_total_${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${h.tooltip(_('Affected number of files, click to show more details'))}">${len(cs.affected_files)}</div>
79 79 <div class="comments-container">
80 80 %if len(c.comments.get(cs.raw_id,[])) > 0:
81 81 <div class="comments-cnt" title="${('comments')}">
82 82 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
83 83 <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div>
84 84 <img src="${h.url('/images/icons/comments.png')}">
85 85 </a>
86 86 </div>
87 87 %endif
88 88 </div>
89 89 <div class="changeset-status-container">
90 90 %if c.statuses.get(cs.raw_id):
91 91 <div title="${_('Changeset status')}" class="changeset-status-lbl">${c.statuses.get(cs.raw_id)[1]}</div>
92 92 <div class="changeset-status-ico">
93 93 %if c.statuses.get(cs.raw_id)[2]:
94 94 <a class="tooltip" title="${_('Click to open associated pull request')}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}"><img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" /></a>
95 95 %else:
96 96 <img src="${h.url('/images/icons/flag_status_%s.png' % c.statuses.get(cs.raw_id)[0])}" />
97 97 %endif
98 98 </div>
99 99 %endif
100 100 </div>
101 101 </div>
102 102 %if cs.parents:
103 103 %for p_cs in reversed(cs.parents):
104 104 <div class="parent">${_('Parent')}
105 105 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
106 106 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
107 107 </div>
108 108 %endfor
109 109 %else:
110 110 <div class="parent">${_('No parents')}</div>
111 111 %endif
112 112
113 113 <span class="logtags">
114 114 %if len(cs.parents)>1:
115 115 <span class="merge">${_('merge')}</span>
116 116 %endif
117 117 %if cs.branch:
118 118 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
119 119 ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
120 120 </span>
121 121 %endif
122 122 %if h.is_hg(c.rhodecode_repo):
123 123 %for book in cs.bookmarks:
124 124 <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
125 125 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
126 126 </span>
127 127 %endfor
128 128 %endif
129 129 %for tag in cs.tags:
130 130 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
131 131 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
132 132 %endfor
133 133 </span>
134 134 </div>
135 135 </div>
136 136
137 137 %endfor
138 138 <div class="pagination-wh pagination-left">
139 139 ${c.pagination.pager('$link_previous ~2~ $link_next')}
140 140 </div>
141 141 </div>
142 142 </div>
143 143
144 144 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
145 145 <script type="text/javascript">
146 146 YAHOO.util.Event.onDOMReady(function(){
147 147
148 148 //Monitor range checkboxes and build a link to changesets
149 149 //ranges
150 150 var checkboxes = YUD.getElementsByClassName('changeset_range');
151 151 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
152 152 YUE.on(checkboxes,'click',function(e){
153 153 var clicked_cb = e.currentTarget;
154 154 var checked_checkboxes = [];
155 155 for (pos in checkboxes){
156 156 if(checkboxes[pos].checked){
157 157 checked_checkboxes.push(checkboxes[pos]);
158 158 }
159 159 }
160 160 if(YUD.get('open_new_pr')){
161 161 if(checked_checkboxes.length>0){
162 162 // modify open pull request to show we have selected cs
163 163 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request for selected changesets'];
164
164
165 165 }else{
166 166 YUD.get('open_new_pr').innerHTML = _TM['Open new pull request'];
167 167 }
168 168 }
169 169
170 170 if(checked_checkboxes.length>1){
171 171 var rev_end = checked_checkboxes[0].name;
172 172 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
173 173
174 174 // now select all checkboxes in the middle.
175 175 var checked = false;
176 176 for (var i=0; i<checkboxes.length; i++){
177 177 var cb = checkboxes[i];
178 178 var rev = cb.name;
179 179
180 180 if (rev == rev_end){
181 181 checked = true;
182 182 }
183 183 if (checked){
184 184 cb.checked = true;
185 185 }
186 186 else{
187 187 cb.checked = false;
188 188 }
189 189 if (rev == rev_start){
190 190 checked = false;
191 191 }
192 192
193 193 }
194 194
195 195 var url = url_tmpl.replace('__REVRANGE__',
196 196 rev_start+'...'+rev_end);
197 197
198 198 var link = _TM['Show selected changes __S -> __E'];
199 199 link = link.replace('__S',rev_start.substr(0,6));
200 200 link = link.replace('__E',rev_end.substr(0,6));
201 201 YUD.get('rev_range_container').href = url;
202 202 YUD.get('rev_range_container').innerHTML = link;
203 203 YUD.setStyle('rev_range_container','display','');
204 204 YUD.setStyle('rev_range_clear','display','');
205 205
206 206 YUD.get('open_new_pr').href += '?rev_start={0}&rev_end={1}'.format(rev_start,rev_end);
207
207
208 208 }
209 209 else{
210 210 YUD.setStyle('rev_range_container','display','none');
211 211 YUD.setStyle('rev_range_clear','display','none');
212 212 }
213 213 });
214 214 YUE.on('rev_range_clear','click',function(e){
215 215 for (var i=0; i<checkboxes.length; i++){
216 216 var cb = checkboxes[i];
217 217 cb.checked = false;
218 218 }
219 219 YUE.preventDefault(e);
220 220 })
221 221 var msgs = YUQ('.message');
222 222 // get first element height
223 223 var el = YUQ('#graph_content .container')[0];
224 224 var row_h = el.clientHeight;
225 225 for(var i=0;i<msgs.length;i++){
226 226 var m = msgs[i];
227 227
228 228 var h = m.clientHeight;
229 229 var pad = YUD.getStyle(m,'padding');
230 230 if(h > row_h){
231 231 var offset = row_h - (h+12);
232 232 YUD.setStyle(m.nextElementSibling,'display','block');
233 233 YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
234 234 };
235 235 }
236 236 YUE.on(YUQ('.expand'),'click',function(e){
237 237 var elem = e.currentTarget.parentNode.parentNode;
238 238 YUD.setStyle(e.currentTarget,'display','none');
239 239 YUD.setStyle(elem,'height','auto');
240 240
241 241 //redraw the graph, line_count and jsdata are global vars
242 242 set_canvas(100);
243 243
244 244 var r = new BranchRenderer();
245 245 r.render(jsdata,100,line_count);
246 246
247 247 })
248 248
249 249 // Fetch changeset details
250 250 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
251 251 var id = e.currentTarget.id;
252 252 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}";
253 253 var url = url.replace('__CS__',id.replace('changed_total_',''));
254 254 ypjax(url,id,function(){tooltip_activate()});
255 255 });
256 256
257 257 // change branch filter
258 258 YUE.on(YUD.get('branch_filter'),'change',function(e){
259 259 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
260 260 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
261 261 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
262 262 var url = url.replace('__BRANCH__',selected_branch);
263 263 if(selected_branch != ''){
264 264 window.location = url;
265 265 }else{
266 266 window.location = url_main;
267 267 }
268 268
269 269 });
270 270
271 271 function set_canvas(width) {
272 272 var c = document.getElementById('graph_nodes');
273 273 var t = document.getElementById('graph_content');
274 274 canvas = document.getElementById('graph_canvas');
275 275 var div_h = t.clientHeight;
276 276 c.style.height=div_h+'px';
277 277 canvas.setAttribute('height',div_h);
278 278 c.style.height=width+'px';
279 279 canvas.setAttribute('width',width);
280 280 };
281 281 var heads = 1;
282 282 var line_count = 0;
283 283 var jsdata = ${c.jsdata|n};
284 284
285 285 for (var i=0;i<jsdata.length;i++) {
286 286 var in_l = jsdata[i][2];
287 287 for (var j in in_l) {
288 288 var m = in_l[j][1];
289 289 if (m > line_count)
290 290 line_count = m;
291 291 }
292 292 }
293 293 set_canvas(100);
294 294
295 295 var r = new BranchRenderer();
296 296 r.render(jsdata,100,line_count);
297 297
298 298 });
299 299 </script>
300 300 %else:
301 301 ${_('There are no changes yet')}
302 302 %endif
303 303 </div>
304 304 </div>
305 305 </%def>
@@ -1,93 +1,93 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${c.repo_name} ${_('Compare')} ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_(u'Home'),h.url('/'))}
10 10 &raquo;
11 11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 12 &raquo;
13 13 ${_('Compare')}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('changelog')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 <div class="table">
27 27 <div id="body" class="diffblock">
28 28 <div class="code-header cv">
29 29 <h3 class="code-header-title">${_('Compare View')}</h3>
30 30 <div>
31 31 ${'%s@%s' % (c.org_repo.repo_name, c.org_ref)} -> ${'%s@%s' % (c.other_repo.repo_name, c.other_ref)} <a href="${c.swap_url}">[swap]</a>
32 32 </div>
33 33 </div>
34 34 </div>
35 35 <div id="changeset_compare_view_content">
36 36 ##CS
37 37 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div>
38 38 <%include file="compare_cs.html" />
39 39
40 40 ## FILES
41 41 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">
42
42
43 43 % if c.limited_diff:
44 44 ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)}
45 45 % else:
46 46 ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}:
47 47 %endif
48
48
49 49 </div>
50 50 <div class="cs_files">
51 51 %if not c.files:
52 52 <span class="empty_data">${_('No files')}</span>
53 53 %endif
54 54 %for fid, change, f, stat in c.files:
55 55 <div class="cs_${change}">
56 56 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
57 57 <div class="changes">${h.fancy_file_stats(stat)}</div>
58 58 </div>
59 59 %endfor
60 60 </div>
61 61 % if c.limited_diff:
62 62 <h5>${_('Changeset was too big and was cut off...')}</h5>
63 % endif
63 % endif
64 64 </div>
65 65 </div>
66 66
67 67 ## diff block
68 68 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
69 69 %for fid, change, f, stat in c.files:
70 70 ${diff_block.diff_block_simple([c.changes[fid]])}
71 71 %endfor
72 72 % if c.limited_diff:
73 73 <h4>${_('Changeset was too big and was cut off...')}</h4>
74 74 % endif
75 75 <script type="text/javascript">
76 76
77 77 YUE.onDOMReady(function(){
78 78
79 79 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
80 80 var act = e.currentTarget.nextElementSibling;
81 81
82 82 if(YUD.hasClass(act,'active')){
83 83 YUD.removeClass(act,'active');
84 84 YUD.setStyle(act,'display','none');
85 85 }else{
86 86 YUD.addClass(act,'active');
87 87 YUD.setStyle(act,'display','');
88 88 }
89 89 });
90 90 })
91 91 </script>
92 92 </div>
93 93 </%def>
@@ -1,161 +1,161 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${_('%s files') % c.repo_name} - ${c.rhodecode_name}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('files_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('files')}
13 13 %if c.file:
14 14 @ r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
15 15 %endif
16 16 </%def>
17 17
18 18 <%def name="page_nav()">
19 19 ${self.menu('files')}
20 20 </%def>
21 21
22 22 <%def name="main()">
23 23 <div class="box">
24 24 <!-- box / title -->
25 25 <div class="title">
26 26 ${self.breadcrumbs()}
27 27 <ul class="links">
28 28 <li>
29 29 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.changeset.branch}</a></span>
30 30 </li>
31 31 </ul>
32 32 </div>
33 33 <div class="table">
34 34 <div id="files_data">
35 35 <%include file='files_ypjax.html'/>
36 36 </div>
37 37 </div>
38 38 </div>
39 39
40 40 <script type="text/javascript">
41 41 var CACHE = {};
42 42 var CACHE_EXPIRE = 5*60*1000; //cache for 5*60s
43 43 //used to construct links from the search list
44 44 var url_base = '${h.url("files_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
45 45 //send the nodelist request to this url
46 46 var node_list_url = '${h.url("files_nodelist_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
47 47 // send the node history requst to this url
48 48 var node_history_url = '${h.url("files_history_home",repo_name=c.repo_name,revision='__REV__',f_path='__FPATH__')}';
49 49
50 50 var ypjax_links = function(){
51 51 YUE.on(YUQ('.ypjax-link'), 'click',function(e){
52 52
53 53 //don't do ypjax on middle click
54 54 if(e.which == 2 || !History.enabled){
55 55 return true;
56 56 }
57 57
58 58 var el = e.currentTarget;
59 59 var url = el.href;
60 60
61 61 var _base_url = '${h.url("files_home",repo_name=c.repo_name,revision='',f_path='')}';
62 62 _base_url = _base_url.replace('//','/')
63 63
64 64 //extract rev and the f_path from url.
65 65 parts = url.split(_base_url)
66 66 if(parts.length != 2){
67 67 return false;
68 68 }
69 69
70 70 var parts2 = parts[1].split('/');
71 71 var rev = parts2.shift(); // pop the first element which is the revision
72 72 var f_path = parts2.join('/');
73 73
74 74 var title = "${_('%s files') % c.repo_name}" + " - " + f_path;
75 75
76 76 var _node_list_url = node_list_url.replace('__REV__',rev).replace('__FPATH__', f_path);
77 77 var _url_base = url_base.replace('__REV__',rev);
78 78
79 79 // Change our States and save some data for handling events
80 80 var data = {url:url,title:title, url_base:_url_base,
81 81 node_list_url:_node_list_url, rev:rev, f_path:f_path};
82 82 History.pushState(data, title, url);
83 83
84 84 //now we're sure that we can do ypjax things
85 85 YUE.preventDefault(e);
86 86 return false;
87 87 });
88 88 }
89 89
90 90 var callbacks = function(State){
91 91 ypjax_links();
92 92 tooltip_activate();
93 93 fileBrowserListeners(State.url, State.data.node_list_url, State.data.url_base);
94
94
95 95 if(YUD.get('hlcode')){
96 96 YUE.on('hlcode', 'mouseup', getSelectionLink);
97 97 }
98 98 //console.log(State);
99 99 if(YUD.get('load_node_history')){
100 100 //remove all listeners due to problems of history state
101 101 YUE.removeListener('load_node_history', 'click');
102 102 YUE.on('load_node_history', 'click', function(e){
103 103 var _url = node_history_url.replace('__REV__',State.data.rev).replace('__FPATH__', State.data.f_path);
104 104 ypjax(_url, 'node_history', function(o){
105 105 tooltip_activate();
106 106 })
107 107 });
108 108 }
109 109 // Inform Google Analytics of the change
110 110 if ( typeof window.pageTracker !== 'undefined' ) {
111 111 window.pageTracker._trackPageview(State.url);
112 112 }
113 113 }
114 114
115 115 YUE.onDOMReady(function(){
116 116 ypjax_links();
117 117 var container = 'files_data';
118 118 //Bind to StateChange Event
119 119 History.Adapter.bind(window,'statechange',function(){
120 120 var State = History.getState();
121 121 cache_key = State.url;
122 122 //check if we have this request in cache maybe ?
123 123 var _cache_obj = CACHE[cache_key];
124 124 var _cur_time = new Date().getTime();
125 125 // get from cache if it's there and not yet expired !
126 126 if(_cache_obj !== undefined && _cache_obj[0] > _cur_time){
127 127 YUD.get(container).innerHTML=_cache_obj[1];
128 128 YUD.setStyle(container,'opacity','1.0');
129 129
130 130 //callbacks after ypjax call
131 131 callbacks(State);
132 132 }
133 133 else{
134 134 ypjax(State.url,container,function(o){
135 135 //callbacks after ypjax call
136 136 callbacks(State);
137 137 if (o !== undefined){
138 138 //store our request in cache
139 139 var _expire_on = new Date().getTime()+CACHE_EXPIRE;
140 140 CACHE[cache_key] = [_expire_on, o.responseText];
141 141 }
142 142 });
143 143 }
144 144 });
145 145
146 146 // init the search filter
147 147 var _State = {
148 148 url: "${h.url.current()}",
149 149 data: {
150 150 node_list_url: node_list_url.replace('__REV__',"${c.changeset.raw_id}").replace('__FPATH__', "${h.safe_unicode(c.file.path)}"),
151 151 url_base: url_base.replace('__REV__',"${c.changeset.raw_id}"),
152 152 rev:"${c.changeset.raw_id}",
153 153 f_path: "${h.safe_unicode(c.file.path)}"
154 154 }
155 155 }
156 156 fileBrowserListeners(_State.url, _State.data.node_list_url, _State.data.url_base);
157 157 });
158 158
159 159 </script>
160 160
161 161 </%def>
@@ -1,25 +1,25 b''
1 1 <dl>
2 2 <dt class="file_history">${_('History')}</dt>
3 3 <dd>
4 4 <div>
5 5 <div style="float:left">
6 6 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
7 7 ${h.hidden('diff2',c.file_changeset.raw_id)}
8 8 ${h.select('diff1',c.file_changeset.raw_id,c.file_history)}
9 9 ${h.submit('diff',_('diff to revision'),class_="ui-btn")}
10 10 ${h.submit('show_rev',_('show at revision'),class_="ui-btn")}
11 11 ${h.hidden('annotate', c.annotate)}
12 12 ${h.end_form()}
13 13 </div>
14 14 <div class="file_author">
15 15 <div class="item">${h.literal(ungettext(u'%s author',u'%s authors',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }</div>
16 16 %for email, user in c.authors:
17 17 <div class="contributor tooltip" style="float:left" title="${h.tooltip(user)}">
18 18 <div class="gravatar" style="margin:1px"><img alt="gravatar" src="${h.gravatar_url(email, 20)}"/> </div>
19 19 </div>
20 20 %endfor
21 21 </div>
22 22 </div>
23 23 <div style="clear:both"></div>
24 24 </dd>
25 </dl> No newline at end of file
25 </dl>
@@ -1,113 +1,113 b''
1 1 <div id="node_history">
2 2 %if c.load_full_history:
3 3 <%include file='files_history_box.html'/>
4 4 %else:
5 5 <div style="padding-bottom:10px">
6 6 <span id="load_node_history" class="ui-btn">${_('Load file history')}</span>
7 7 </div>
8 8 %endif
9 9 </div>
10 10
11 11
12 12 <div id="body" class="codeblock">
13 13 <div class="code-header">
14 14 <div class="stats">
15 15 <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
16 16 <div class="left item"><pre class="tooltip" title="${h.tooltip(h.fmt_date(c.file_changeset.date))}">${h.link_to("r%s:%s" % (c.file_changeset.revision,h.short_id(c.file_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id))}</pre></div>
17 17 <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
18 18 <div class="left item last"><pre>${c.file.mimetype}</pre></div>
19 19 <div class="buttons">
20 20 %if c.annotate:
21 21 ${h.link_to(_('show source'), h.url('files_home', repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
22 22 %else:
23 23 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
24 24 %endif
25 25 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
26 26 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
27 27 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
28 28 % if not c.file.is_binary:
29 29 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
30 30 % endif
31 31 % endif
32 32 </div>
33 33 </div>
34 34 <div class="author">
35 35 <div class="gravatar">
36 36 <img alt="gravatar" src="${h.gravatar_url(h.email_or_none(c.file_changeset.author),16)}"/>
37 37 </div>
38 38 <div title="${c.file_changeset.author}" class="user">${h.person(c.file_changeset.author)}</div>
39 39 </div>
40 40 <div class="commit">${h.urlify_commit(c.file_changeset.message,c.repo_name)}</div>
41 41 </div>
42 42 <div class="code-body">
43 43 %if c.file.is_binary:
44 44 ${_('Binary file (%s)') % c.file.mimetype}
45 45 %else:
46 46 % if c.file.size < c.cut_off_limit:
47 47 %if c.annotate:
48 48 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
49 49 %else:
50 50 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
51 51 %endif
52 52 %else:
53 53 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
54 54 h.url('files_raw_home',repo_name=c.repo_name,revision=c.file_changeset.raw_id,f_path=c.f_path))}
55 55 %endif
56 56 %endif
57 57 </div>
58 58 </div>
59 59
60 60 <script type="text/javascript">
61 61 YUE.onDOMReady(function(){
62 62 function highlight_lines(lines){
63 63 for(pos in lines){
64 64 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
65 65 }
66 66 }
67 67 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
68 68 if (page_highlights.length == 2){
69 69 highlight_ranges = page_highlights[1].split(",");
70 70
71 71 var h_lines = [];
72 72 for (pos in highlight_ranges){
73 73 var _range = highlight_ranges[pos].split('-');
74 74 if(_range.length == 2){
75 75 var start = parseInt(_range[0]);
76 76 var end = parseInt(_range[1]);
77 77 if (start < end){
78 78 for(var i=start;i<=end;i++){
79 79 h_lines.push(i);
80 80 }
81 81 }
82 82 }
83 83 else{
84 84 h_lines.push(parseInt(highlight_ranges[pos]));
85 85 }
86 86 }
87 87 highlight_lines(h_lines);
88 88
89 89 //remember original location
90 90 var old_hash = location.href.substring(location.href.indexOf('#'));
91 91
92 92 // this makes a jump to anchor moved by 3 posstions for padding
93 93 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
94 94
95 95 //sets old anchor
96 96 window.location.hash = old_hash;
97 97
98 98 }
99 99
100 100 // select code link event
101 101 YUE.on('hlcode', 'mouseup', getSelectionLink);
102 102
103 103 //load history of file
104 104 YUE.on('load_node_history', 'click', function(e){
105 105 var _url = node_history_url.replace('__REV__','${c.file_changeset.raw_id}').replace('__FPATH__', '${c.f_path}');
106 106 ypjax(_url, 'node_history', function(o){
107 107 tooltip_activate();
108 108 })
109 });
109 });
110 110
111 111 });
112
112
113 113 </script>
@@ -1,204 +1,204 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('New pull request')}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('New pull request')}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 ${h.form(url('pullrequest', repo_name=c.repo_name), method='post', id='pull_request_form')}
23 23 <div style="float:left;padding:0px 30px 30px 30px">
24 24 <input type="hidden" name="rev_start" value="${request.GET.get('rev_start')}" />
25 25 <input type="hidden" name="rev_end" value="${request.GET.get('rev_end')}" />
26
26
27 27 ##ORG
28 28 <div style="float:left">
29 29 <div class="fork_user">
30 30 <div class="gravatar">
31 31 <img alt="gravatar" src="${h.gravatar_url(c.rhodecode_db_repo.user.email,24)}"/>
32 32 </div>
33 33 <span style="font-size: 20px">
34 34 ${h.select('org_repo','',c.org_repos,class_='refs')}:${h.select('org_ref','',c.org_refs,class_='refs')}
35 35 </span>
36 36 <div style="padding:5px 3px 3px 42px;">${c.rhodecode_db_repo.description}</div>
37 37 </div>
38 38 <div style="clear:both;padding-top: 10px"></div>
39 39 </div>
40 40 <div style="float:left;font-size:24px;padding:0px 20px">
41 41 <img height=32 width=32 src="${h.url('/images/arrow_right_64.png')}"/>
42 42 </div>
43 43
44 44 ##OTHER, most Probably the PARENT OF THIS FORK
45 45 <div style="float:left">
46 46 <div class="fork_user">
47 47 <div class="gravatar">
48 48 <img id="other_repo_gravatar" alt="gravatar" src=""/>
49 49 </div>
50 50 <span style="font-size: 20px">
51 51 ${h.select('other_repo',c.default_pull_request ,c.other_repos,class_='refs')}:${h.select('other_ref',c.default_pull_request_rev,c.default_revs,class_='refs')}
52 52 </span>
53 53 <span style="padding:3px">
54 54 <a id="refresh" href="#" class="tooltip" title="${h.tooltip(_('refresh overview'))}">
55 55 <img style="margin:3px" class="icon" title="${_('Refresh')}" alt="${_('Refresh')}" src="${h.url('/images/icons/arrow_refresh.png')}"/>
56 56 </a>
57 57 </span>
58 58 <div id="other_repo_desc" style="padding:5px 3px 3px 42px;"></div>
59 59 </div>
60 60 <div style="clear:both;padding-top: 10px"></div>
61 61 </div>
62 62 <div style="clear:both;padding-top: 10px"></div>
63 63 ## overview pulled by ajax
64 64 <div style="float:left" id="pull_request_overview"></div>
65 65 <div style="float:left;clear:both;padding:10px 10px 10px 0px;display:none">
66 66 <a id="pull_request_overview_url" href="#">${_('Detailed compare view')}</a>
67 67 </div>
68 68 </div>
69 69 <div style="float:left; border-left:1px dashed #eee">
70 70 <h4>${_('Pull request reviewers')}</h4>
71 71 <div id="reviewers" style="padding:0px 0px 0px 15px">
72 72 ## members goes here !
73 73 <div class="group_members_wrap">
74 74 <ul id="review_members" class="group_members">
75 75 %for member in c.review_members:
76 76 <li id="reviewer_${member.user_id}">
77 77 <div class="reviewers_member">
78 78 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
79 79 <div style="float:left">${member.full_name} (${_('owner')})</div>
80 80 <input type="hidden" value="${member.user_id}" name="review_members" />
81 81 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
82 82 </div>
83 83 </li>
84 84 %endfor
85 85 </ul>
86 86 </div>
87 87
88 88 <div class='ac'>
89 89 <div class="reviewer_ac">
90 90 ${h.text('user', class_='yui-ac-input')}
91 91 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
92 92 <div id="reviewers_container"></div>
93 93 </div>
94 94 </div>
95 95 </div>
96 96 </div>
97 97 <h3>${_('Create new pull request')}</h3>
98 98
99 99 <div class="form">
100 100 <!-- fields -->
101 101
102 102 <div class="fields">
103 103
104 104 <div class="field">
105 105 <div class="label">
106 106 <label for="pullrequest_title">${_('Title')}:</label>
107 107 </div>
108 108 <div class="input">
109 109 ${h.text('pullrequest_title',size=30)}
110 110 </div>
111 111 </div>
112 112
113 113 <div class="field">
114 114 <div class="label label-textarea">
115 115 <label for="pullrequest_desc">${_('description')}:</label>
116 116 </div>
117 117 <div class="textarea text-area editor">
118 118 ${h.textarea('pullrequest_desc',size=30)}
119 119 </div>
120 120 </div>
121 121
122 122 <div class="buttons">
123 123 ${h.submit('save',_('Send pull request'),class_="ui-btn large")}
124 124 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
125 125 </div>
126 126 </div>
127 127 </div>
128 128 ${h.end_form()}
129 129
130 130 </div>
131 131
132 132 <script type="text/javascript">
133 133 var _USERS_AC_DATA = ${c.users_array|n};
134 134 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
135 135 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
136 136
137 137 var other_repos_info = ${c.other_repos_info|n};
138 138
139 139 var loadPreview = function(){
140 140 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none');
141 141 var url = "${h.url('compare_url',
142 142 repo_name='org_repo',
143 143 org_ref_type='org_ref_type', org_ref='org_ref',
144 144 other_ref_type='other_ref_type', other_ref='other_ref',
145 145 repo='other_repo',
146 146 as_form=True, bundle=False,
147 147 rev_start=request.GET.get('rev_start',''),
148 148 rev_end=request.GET.get('rev_end',''))}";
149 149
150 150 var select_refs = YUQ('#pull_request_form select.refs')
151 151 var rev_data = {}; // gather the org/other ref and repo here
152 152 for(var i=0;i<select_refs.length;i++){
153 153 var select_ref = select_refs[i];
154 154 var select_ref_data = select_ref.value.split(':');
155 155 var key = null;
156 156 var val = null;
157 157
158 158 if(select_ref_data.length>1){
159 159 key = select_ref.name+"_type";
160 160 val = select_ref_data[0];
161 161 url = url.replace(key,val);
162 162 rev_data[key] = val;
163 163
164 164 key = select_ref.name;
165 165 val = select_ref_data[1];
166 166 url = url.replace(key,val);
167 167 rev_data[key] = val;
168 168
169 169 }else{
170 170 key = select_ref.name;
171 171 val = select_ref.value;
172 172 url = url.replace(key,val);
173 173 rev_data[key] = val;
174 174 }
175 175 }
176 176
177 177 YUE.on('other_repo', 'change', function(e){
178 178 var repo_name = e.currentTarget.value;
179 179 // replace the <select> of changed repo
180 180 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
181 181 });
182 182
183 183 ypjax(url,'pull_request_overview', function(data){
184 184 var sel_box = YUQ('#pull_request_form #other_repo')[0];
185 185 var repo_name = sel_box.options[sel_box.selectedIndex].value;
186 186 YUD.get('pull_request_overview_url').href = url;
187 187 YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','');
188 188 YUD.get('other_repo_gravatar').src = other_repos_info[repo_name]['gravatar'];
189 189 YUD.get('other_repo_desc').innerHTML = other_repos_info[repo_name]['description'];
190 190 YUD.get('other_ref').innerHTML = other_repos_info[repo_name]['revs'];
191 191 // select back the revision that was just compared
192 192 setSelectValue(YUD.get('other_ref'), rev_data['other_ref']);
193 193 })
194 194 }
195 195 YUE.on('refresh','click',function(e){
196 196 loadPreview()
197 197 })
198 198
199 199 //lazy load overview after 0.5s
200 200 setTimeout(loadPreview, 500)
201 201
202 202 </script>
203 203
204 204 </%def>
@@ -1,217 +1,217 b''
1 1 <%inherit file="/base/base.html"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('Pull request #%s') % c.pull_request.pull_request_id}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${h.link_to(_(u'Home'),h.url('/'))}
9 9 &raquo;
10 10 ${h.link_to(c.repo_name,h.url('changelog_home',repo_name=c.repo_name))}
11 11 &raquo;
12 12 ${_('Pull request #%s') % c.pull_request.pull_request_id}
13 13 </%def>
14 14
15 15 <%def name="main()">
16 16
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 </div>
22 22 %if c.pull_request.is_closed():
23 23 <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))} ${_('with status %s') % h.changeset_status_lbl(c.current_changeset_status)}</div>
24 24 %endif
25 25 <h3>${_('Title')}: ${c.pull_request.title}</h3>
26 26
27 27 <div class="form">
28 28 <div id="summary" class="fields">
29 29 <div class="field">
30 30 <div class="label-summary">
31 31 <label>${_('Status')}:</label>
32 32 </div>
33 33 <div class="input">
34 34 <div class="changeset-status-container" style="float:none;clear:both">
35 35 %if c.current_changeset_status:
36 36 <div title="${_('Pull request status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
37 37 <div class="changeset-status-ico" style="padding:1px 4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>
38 38 %endif
39 39 </div>
40 40 </div>
41 41 </div>
42 42 <div class="field">
43 43 <div class="label-summary">
44 44 <label>${_('Still not reviewed by')}:</label>
45 45 </div>
46 46 <div class="input">
47 47 % if len(c.pull_request_pending_reviewers) > 0:
48 48 <div class="tooltip" title="${h.tooltip(','.join([x.username for x in c.pull_request_pending_reviewers]))}">${ungettext('%d reviewer', '%d reviewers',len(c.pull_request_pending_reviewers)) % len(c.pull_request_pending_reviewers)}</div>
49 49 %else:
50 50 <div>${_('pull request was reviewed by all reviewers')}</div>
51 51 %endif
52 52 </div>
53 53 </div>
54 54 </div>
55 55 </div>
56 56 <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
57 57 <div style="padding:4px 4px 10px 20px">
58 58 <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
59 59 </div>
60 60
61 61 <div style="overflow: auto;">
62 62 ##DIFF
63 63 <div class="table" style="float:left;clear:none">
64 64 <div id="body" class="diffblock">
65 65 <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
66 66 </div>
67 67 <div id="changeset_compare_view_content">
68 68 ##CS
69 69 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${ungettext('Showing %s commit','Showing %s commits', len(c.cs_ranges)) % len(c.cs_ranges)}</div>
70 70 <%include file="/compare/compare_cs.html" />
71
71
72 72 ## FILES
73 73 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">
74
74
75 75 % if c.limited_diff:
76 76 ${ungettext('%s file changed', '%s files changed', len(c.files)) % len(c.files)}
77 77 % else:
78 78 ${ungettext('%s file changed with %s insertions and %s deletions','%s files changed with %s insertions and %s deletions', len(c.files)) % (len(c.files),c.lines_added,c.lines_deleted)}:
79 79 %endif
80
80
81 81 </div>
82 82 <div class="cs_files">
83 83 %if not c.files:
84 84 <span class="empty_data">${_('No files')}</span>
85 85 %endif
86 86 %for fid, change, f, stat in c.files:
87 87 <div class="cs_${change}">
88 88 <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
89 89 <div class="changes">${h.fancy_file_stats(stat)}</div>
90 90 </div>
91 91 %endfor
92 92 </div>
93 93 % if c.limited_diff:
94 94 <h5>${_('Changeset was too big and was cut off...')}</h5>
95 % endif
95 % endif
96 96 </div>
97 97 </div>
98 98 ## REVIEWERS
99 99 <div style="float:left; border-left:1px dashed #eee">
100 100 <h4>${_('Pull request reviewers')}</h4>
101 101 <div id="reviewers" style="padding:0px 0px 5px 10px">
102 102 ## members goes here !
103 103 <div class="group_members_wrap" style="min-height:45px">
104 104 <ul id="review_members" class="group_members">
105 105 %for member,status in c.pull_request_reviewers:
106 106 <li id="reviewer_${member.user_id}">
107 107 <div class="reviewers_member">
108 108 <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
109 109 <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
110 110 </div>
111 111 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
112 112 <div style="float:left">${member.full_name} (${_('owner')})</div>
113 113 <input type="hidden" value="${member.user_id}" name="review_members" />
114 114 %if not c.pull_request.is_closed() and (h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id):
115 115 <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
116 116 %endif
117 117 </div>
118 118 </li>
119 119 %endfor
120 120 </ul>
121 121 </div>
122 122 %if not c.pull_request.is_closed():
123 123 <div class='ac'>
124 124 %if h.HasPermissionAny('hg.admin', 'repository.admin')() or c.pull_request.author.user_id == c.rhodecode_user.user_id:
125 125 <div class="reviewer_ac">
126 126 ${h.text('user', class_='yui-ac-input')}
127 127 <span class="help-block">${_('Add reviewer to this pull request.')}</span>
128 128 <div id="reviewers_container"></div>
129 129 </div>
130 130 <div style="padding:0px 10px">
131 131 <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
132 132 </div>
133 133 %endif
134 134 </div>
135 135 %endif
136 136 </div>
137 137 </div>
138 138 </div>
139 139 <script>
140 140 var _USERS_AC_DATA = ${c.users_array|n};
141 141 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
142 142 AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
143 143 AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
144 144 AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
145 145 </script>
146 146
147 147 ## diff block
148 148 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
149 149 %for fid, change, f, stat in c.files:
150 150 ${diff_block.diff_block_simple([c.changes[fid]])}
151 151 %endfor
152 152 % if c.limited_diff:
153 153 <h4>${_('Changeset was too big and was cut off...')}</h4>
154 154 % endif
155 155
156 156
157 157 ## template for inline comment form
158 158 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
159 159 ${comment.comment_inline_form()}
160 160
161 161 ## render comments and inlines
162 162 ${comment.generate_comments()}
163 163
164 164 % if not c.pull_request.is_closed():
165 165 ## main comment form and it status
166 166 ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
167 167 pull_request_id=c.pull_request.pull_request_id),
168 168 c.current_changeset_status,
169 169 close_btn=True)}
170 170 %endif
171 171
172 172 <script type="text/javascript">
173 173 YUE.onDOMReady(function(){
174 174 PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
175 175
176 176 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
177 177 var show = 'none';
178 178 var target = e.currentTarget;
179 179 if(target.checked){
180 180 var show = ''
181 181 }
182 182 var boxid = YUD.getAttribute(target,'id_for');
183 183 var comments = YUQ('#{0} .inline-comments'.format(boxid));
184 184 for(c in comments){
185 185 YUD.setStyle(comments[c],'display',show);
186 186 }
187 187 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
188 188 for(c in btns){
189 189 YUD.setStyle(btns[c],'display',show);
190 190 }
191 191 })
192 192
193 193 YUE.on(YUQ('.line'),'click',function(e){
194 194 var tr = e.currentTarget;
195 195 injectInlineForm(tr);
196 196 });
197 197
198 198 // inject comments into they proper positions
199 199 var file_comments = YUQ('.inline-comment-placeholder');
200 200 renderInlineComments(file_comments);
201 201
202 202 YUE.on(YUD.get('update_pull_request'),'click',function(e){
203 203
204 204 var reviewers_ids = [];
205 205 var ids = YUQ('#review_members input');
206 206 for(var i=0; i<ids.length;i++){
207 207 var id = ids[i].value
208 208 reviewers_ids.push(id);
209 209 }
210 210 updateReviewers(reviewers_ids);
211 211 })
212 212 })
213 213 </script>
214 214
215 215 </div>
216 216
217 217 </%def>
@@ -1,93 +1,93 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('%s Tags') % c.repo_name} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8
9 9 <%def name="breadcrumbs_links()">
10 10 <input class="q_filter_box" id="q_filter_tags" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
11 11 ${h.link_to(_(u'Home'),h.url('/'))}
12 12 &raquo;
13 13 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
14 14 &raquo;
15 15 ${_('tags')}
16 16 </%def>
17 17
18 18 <%def name="page_nav()">
19 19 ${self.menu('tags')}
20 20 </%def>
21 21 <%def name="main()">
22 22 <div class="box">
23 23 <!-- box / title -->
24 24 <div class="title">
25 25 ${self.breadcrumbs()}
26 26 </div>
27 27 <!-- end box / title -->
28 28 %if c.repo_tags:
29 29 <div class="info_box" id="compare_tags" style="clear: both;padding: 10px 19px;vertical-align: right;text-align: right;"><a href="#" class="ui-btn small">${_('Compare tags')}</a></div>
30 %endif
30 %endif
31 31 <div class="table">
32 32 <%include file='tags_data.html'/>
33 33 </div>
34 34 </div>
35 35 <script type="text/javascript">
36 36 YUE.on('compare_tags','click',function(e){
37 37 YUE.preventDefault(e);
38 38 var org = YUQ('input[name=compare_org]:checked')[0];
39 39 var other = YUQ('input[name=compare_other]:checked')[0];
40 40
41 41 if(org && other){
42 42 var compare_url = "${h.url('compare_url',repo_name=c.repo_name,org_ref_type='tag',org_ref='__ORG__',other_ref_type='tag',other_ref='__OTHER__')}";
43 43 var u = compare_url.replace('__ORG__',org.value)
44 44 .replace('__OTHER__',other.value);
45 45 window.location=u;
46 46 }
47 47 });
48 48
49 49 // main table sorting
50 50 var myColumnDefs = [
51 51 {key:"name",label:"${_('Name')}",sortable:true},
52 52 {key:"date",label:"${_('Date')}",sortable:true,
53 53 sortOptions: { sortFunction: dateSort }},
54 54 {key:"author",label:"${_('Author')}",sortable:true},
55 55 {key:"revision",label:"${_('Revision')}",sortable:true,
56 56 sortOptions: { sortFunction: revisionSort }},
57 {key:"compare",label:"${_('Compare')}",sortable:false,},
57 {key:"compare",label:"${_('Compare')}",sortable:false,},
58 58 ];
59 59
60 60 var myDataSource = new YAHOO.util.DataSource(YUD.get("tags_data"));
61 61
62 62 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
63 63
64 64 myDataSource.responseSchema = {
65 65 fields: [
66 66 {key:"name"},
67 67 {key:"date"},
68 68 {key:"author"},
69 69 {key:"revision"},
70 70 {key:"compare"},
71 71 ]
72 72 };
73 73
74 74 var myDataTable = new YAHOO.widget.DataTable("table_wrap", myColumnDefs, myDataSource,
75 75 {
76 76 sortedBy:{key:"name",dir:"asc"},
77 77 MSG_SORTASC:"${_('Click to sort ascending')}",
78 78 MSG_SORTDESC:"${_('Click to sort descending')}",
79 79 MSG_EMPTY:"${_('No records found.')}",
80 80 MSG_ERROR:"${_('Data error.')}",
81 81 MSG_LOADING:"${_('Loading...')}",
82 82 }
83 83 );
84 84 myDataTable.subscribe('postRenderEvent',function(oArgs) {
85 85 tooltip_activate();
86 86 var func = function(node){
87 87 return node.parentNode.parentNode.parentNode.parentNode.parentNode;
88 88 }
89 89 q_filter('q_filter_tags',YUQ('div.table tr td .logtags .tagtag a'),func);
90 90 });
91 91
92 92 </script>
93 93 </%def>
@@ -1,39 +1,39 b''
1 1 %if c.repo_tags:
2 2 <div id="table_wrap" class="yui-skin-sam">
3 3 <table id="tags_data">
4 4 <thead>
5 5 <tr>
6 6 <th class="left">${_('Name')}</th>
7 7 <th class="left">${_('Date')}</th>
8 8 <th class="left">${_('Author')}</th>
9 9 <th class="left">${_('Revision')}</th>
10 10 <th class="left">${_('Compare')}</th>
11 11 </tr>
12 12 </thead>
13 13 %for cnt,tag in enumerate(c.repo_tags.items()):
14 14 <tr class="parity${cnt%2}">
15 15 <td>
16 16 <span class="logtags">
17 17 <span class="tagtag">${h.link_to(tag[0],
18 18 h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id))}
19 19 </span>
20 20 </span>
21 21 </td>
22 22 <td><span class="tooltip" title="${h.tooltip(h.age(tag[1].date))}">${h.fmt_date(tag[1].date)}</span></td>
23 23 <td title="${tag[1].author}">${h.person(tag[1].author)}</td>
24 24 <td>
25 25 <div>
26 26 <pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id)}">r${tag[1].revision}:${h.short_id(tag[1].raw_id)}</a></pre>
27 27 </div>
28 28 </td>
29 29 <td>
30 30 <input class="branch-compare" type="radio" name="compare_org" value="${tag[0]}"/>
31 31 <input class="branch-compare" type="radio" name="compare_other" value="${tag[0]}"/>
32 </td>
32 </td>
33 33 </tr>
34 34 %endfor
35 35 </table>
36 36 </div>
37 37 %else:
38 38 ${_('There are no tags yet')}
39 39 %endif
@@ -1,347 +1,347 b''
1 1 from rhodecode.tests import *
2 2 from rhodecode.model.repo import RepoModel
3 3 from rhodecode.model.meta import Session
4 4 from rhodecode.model.db import Repository
5 5 from rhodecode.model.scm import ScmModel
6 6 from rhodecode.lib.vcs.backends.base import EmptyChangeset
7 7
8 8
9 9 def _fork_repo(fork_name, vcs_type, parent=None):
10 10 if vcs_type =='hg':
11 11 _REPO = HG_REPO
12 12 elif vcs_type == 'git':
13 13 _REPO = GIT_REPO
14 14
15 15 if parent:
16 16 _REPO = parent
17 17
18 18 form_data = dict(
19 19 repo_name=fork_name,
20 20 repo_name_full=fork_name,
21 21 repo_group=None,
22 22 repo_type=vcs_type,
23 23 description='',
24 24 private=False,
25 25 copy_permissions=False,
26 26 landing_rev='tip',
27 27 update_after_clone=False,
28 28 fork_parent_id=Repository.get_by_repo_name(_REPO),
29 29 )
30 30 repo = RepoModel().create_fork(form_data, cur_user=TEST_USER_ADMIN_LOGIN)
31 31
32 32 Session().commit()
33 33 return Repository.get_by_repo_name(fork_name)
34 34
35 35
36 36 def _commit_change(repo, filename, content, message, vcs_type, parent=None, newfile=False):
37 37 repo = Repository.get_by_repo_name(repo)
38 38 _cs = parent
39 39 if not parent:
40 40 _cs = EmptyChangeset(alias=vcs_type)
41 41
42 42 if newfile:
43 43 cs = ScmModel().create_node(
44 44 repo=repo.scm_instance, repo_name=repo.repo_name,
45 45 cs=_cs, user=TEST_USER_ADMIN_LOGIN,
46 46 author=TEST_USER_ADMIN_LOGIN,
47 47 message=message,
48 48 content=content,
49 49 f_path=filename
50 50 )
51 51 else:
52 52 cs = ScmModel().commit_change(
53 53 repo=repo.scm_instance, repo_name=repo.repo_name,
54 54 cs=parent, user=TEST_USER_ADMIN_LOGIN,
55 55 author=TEST_USER_ADMIN_LOGIN,
56 56 message=message,
57 57 content=content,
58 58 f_path=filename
59 59 )
60 60 return cs
61 61
62 62
63 63 class TestCompareController(TestController):
64 64
65 65 def test_compare_forks_on_branch_extra_commits_hg(self):
66 66 self.log_user()
67 67
68 68 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
69 69 description='diff-test',
70 70 owner=TEST_USER_ADMIN_LOGIN)
71 71 r1_id = repo1.repo_id
72 72 Session().commit()
73 73 #commit something !
74 74 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
75 75 message='commit1', vcs_type='hg', parent=None, newfile=True)
76 76
77 77 #fork this repo
78 78 repo2 = _fork_repo('one-fork', 'hg', parent='one')
79 79 Session().commit()
80 80 r2_id = repo2.repo_id
81 81
82 82 #add two extra commit into fork
83 83 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
84 84 message='commit2', vcs_type='hg', parent=cs0)
85 85
86 86 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
87 87 message='commit3', vcs_type='hg', parent=cs1)
88 88
89 89 rev1 = 'default'
90 90 rev2 = 'default'
91 91 response = self.app.get(url(controller='compare', action='index',
92 92 repo_name=repo2.repo_name,
93 93 org_ref_type="branch",
94 94 org_ref=rev1,
95 95 other_ref_type="branch",
96 96 other_ref=rev2,
97 97 repo=repo1.repo_name
98 98 ))
99 99
100 100 try:
101 101 response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
102 102 response.mustcontain("""Showing 2 commits""")
103 103 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
104 104
105 105 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
106 106 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
107 107
108 108 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
109 109 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
110 110 ## files
111 111 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (repo2.repo_name, rev1, rev2))
112 112
113 113 finally:
114 114 RepoModel().delete(r2_id)
115 115 RepoModel().delete(r1_id)
116
116
117 117
118 118 def test_compare_forks_on_branch_extra_commits_origin_has_incomming_hg(self):
119 119 self.log_user()
120 120
121 121 repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
122 122 description='diff-test',
123 123 owner=TEST_USER_ADMIN_LOGIN)
124 124 r1_id = repo1.repo_id
125 125 Session().commit()
126 126 #commit something !
127 127 cs0 = _commit_change(repo1.repo_name, filename='file1', content='line1\n',
128 128 message='commit1', vcs_type='hg', parent=None, newfile=True)
129 129
130 130 #fork this repo
131 131 repo2 = _fork_repo('one-fork', 'hg', parent='one')
132 132 Session().commit()
133 133
134 134 #now commit something to origin repo
135 135 cs1_prim = _commit_change(repo1.repo_name, filename='file2', content='line1file2\n',
136 136 message='commit2', vcs_type='hg', parent=cs0, newfile=True)
137 137
138 138 r2_id = repo2.repo_id
139 139
140 140 #add two extra commit into fork
141 141 cs1 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\n',
142 142 message='commit2', vcs_type='hg', parent=cs0)
143 143
144 144 cs2 = _commit_change(repo2.repo_name, filename='file1', content='line1\nline2\nline3\n',
145 145 message='commit3', vcs_type='hg', parent=cs1)
146 146
147 147 rev1 = 'default'
148 148 rev2 = 'default'
149 149 response = self.app.get(url(controller='compare', action='index',
150 150 repo_name=repo2.repo_name,
151 151 org_ref_type="branch",
152 152 org_ref=rev1,
153 153 other_ref_type="branch",
154 154 other_ref=rev2,
155 155 repo=repo1.repo_name
156 156 ))
157 157
158 158 try:
159 159 response.mustcontain('%s@%s -> %s@%s' % (repo2.repo_name, rev1, repo1.repo_name, rev2))
160 160 response.mustcontain("""Showing 2 commits""")
161 161 response.mustcontain("""1 file changed with 2 insertions and 0 deletions""")
162 162
163 163 response.mustcontain("""<div class="message tooltip" title="commit2" style="white-space:normal">commit2</div>""")
164 164 response.mustcontain("""<div class="message tooltip" title="commit3" style="white-space:normal">commit3</div>""")
165 165
166 166 response.mustcontain("""<a href="/%s/changeset/%s">r1:%s</a>""" % (repo2.repo_name, cs1.raw_id, cs1.short_id))
167 167 response.mustcontain("""<a href="/%s/changeset/%s">r2:%s</a>""" % (repo2.repo_name, cs2.raw_id, cs2.short_id))
168 168 ## files
169 169 response.mustcontain("""<a href="/%s/compare/branch@%s...branch@%s#C--826e8142e6ba">file1</a>""" % (repo2.repo_name, rev1, rev2))
170 170
171 171 finally:
172 172 RepoModel().delete(r2_id)
173 173 RepoModel().delete(r1_id)
174 174
175 175
176 176 # def test_compare_remote_repos_remote_flag_off(self):
177 177 # self.log_user()
178 178 # _fork_repo(HG_FORK, 'hg')
179 179 #
180 180 # rev1 = '56349e29c2af'
181 181 # rev2 = '7d4bc8ec6be5'
182 182 #
183 183 # response = self.app.get(url(controller='compare', action='index',
184 184 # repo_name=HG_REPO,
185 185 # org_ref_type="rev",
186 186 # org_ref=rev1,
187 187 # other_ref_type="rev",
188 188 # other_ref=rev2,
189 189 # repo=HG_FORK,
190 190 # bundle=False,
191 191 # ))
192 192 #
193 193 # try:
194 194 # response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
195 195 # ## outgoing changesets between those revisions
196 196 #
197 197 # response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
198 198 # response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_REPO))
199 199 # response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_REPO, rev2))
200 200 #
201 201 # ## files
202 202 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2))
203 203 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2))
204 204 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2))
205 205 # finally:
206 206 # RepoModel().delete(HG_FORK)
207 207
208 208
209 209
210 210 #
211 211 # def test_compare_remote_branches_hg(self):
212 212 # self.log_user()
213 213 #
214 214 # _fork_repo(HG_FORK, 'hg')
215 215 #
216 216 # rev1 = '56349e29c2af'
217 217 # rev2 = '7d4bc8ec6be5'
218 218 #
219 219 # response = self.app.get(url(controller='compare', action='index',
220 220 # repo_name=HG_REPO,
221 221 # org_ref_type="rev",
222 222 # org_ref=rev1,
223 223 # other_ref_type="rev",
224 224 # other_ref=rev2,
225 225 # repo=HG_FORK,
226 226 # ))
227 227 #
228 228 # try:
229 229 # response.mustcontain('%s@%s -> %s@%s' % (HG_REPO, rev1, HG_FORK, rev2))
230 230 # ## outgoing changesets between those revisions
231 231 #
232 232 # response.mustcontain("""<a href="/%s/changeset/2dda4e345facb0ccff1a191052dd1606dba6781d">r4:2dda4e345fac</a>""" % (HG_REPO))
233 233 # response.mustcontain("""<a href="/%s/changeset/6fff84722075f1607a30f436523403845f84cd9e">r5:6fff84722075</a>""" % (HG_REPO))
234 234 # response.mustcontain("""<a href="/%s/changeset/7d4bc8ec6be56c0f10425afb40b6fc315a4c25e7">r6:%s</a>""" % (HG_REPO, rev2))
235 235 #
236 236 # ## files
237 237 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--9c390eb52cd6">vcs/backends/hg.py</a>""" % (HG_REPO, rev1, rev2))
238 238 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--41b41c1f2796">vcs/backends/__init__.py</a>""" % (HG_REPO, rev1, rev2))
239 239 # response.mustcontain("""<a href="/%s/compare/rev@%s...rev@%s#C--2f574d260608">vcs/backends/base.py</a>""" % (HG_REPO, rev1, rev2))
240 240 # finally:
241 241 # RepoModel().delete(HG_FORK)
242 242 #
243 243 # def test_org_repo_new_commits_after_forking_simple_diff(self):
244 244 # self.log_user()
245 245 #
246 246 # repo1 = RepoModel().create_repo(repo_name='one', repo_type='hg',
247 247 # description='diff-test',
248 248 # owner=TEST_USER_ADMIN_LOGIN)
249 249 #
250 250 # Session().commit()
251 251 # r1_id = repo1.repo_id
252 252 # r1_name = repo1.repo_name
253 253 #
254 254 # #commit something initially !
255 255 # cs0 = ScmModel().create_node(
256 256 # repo=repo1.scm_instance, repo_name=r1_name,
257 257 # cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
258 258 # author=TEST_USER_ADMIN_LOGIN,
259 259 # message='commit1',
260 260 # content='line1',
261 261 # f_path='file1'
262 262 # )
263 263 # Session().commit()
264 264 # self.assertEqual(repo1.scm_instance.revisions, [cs0.raw_id])
265 265 # #fork the repo1
266 266 # repo2 = RepoModel().create_repo(repo_name='one-fork', repo_type='hg',
267 267 # description='compare-test',
268 268 # clone_uri=repo1.repo_full_path,
269 269 # owner=TEST_USER_ADMIN_LOGIN, fork_of='one')
270 270 # Session().commit()
271 271 # self.assertEqual(repo2.scm_instance.revisions, [cs0.raw_id])
272 272 # r2_id = repo2.repo_id
273 273 # r2_name = repo2.repo_name
274 274 #
275 275 # #make 3 new commits in fork
276 276 # cs1 = ScmModel().create_node(
277 277 # repo=repo2.scm_instance, repo_name=r2_name,
278 278 # cs=repo2.scm_instance[-1], user=TEST_USER_ADMIN_LOGIN,
279 279 # author=TEST_USER_ADMIN_LOGIN,
280 280 # message='commit1-fork',
281 281 # content='file1-line1-from-fork',
282 282 # f_path='file1-fork'
283 283 # )
284 284 # cs2 = ScmModel().create_node(
285 285 # repo=repo2.scm_instance, repo_name=r2_name,
286 286 # cs=cs1, user=TEST_USER_ADMIN_LOGIN,
287 287 # author=TEST_USER_ADMIN_LOGIN,
288 288 # message='commit2-fork',
289 289 # content='file2-line1-from-fork',
290 290 # f_path='file2-fork'
291 291 # )
292 292 # cs3 = ScmModel().create_node(
293 293 # repo=repo2.scm_instance, repo_name=r2_name,
294 294 # cs=cs2, user=TEST_USER_ADMIN_LOGIN,
295 295 # author=TEST_USER_ADMIN_LOGIN,
296 296 # message='commit3-fork',
297 297 # content='file3-line1-from-fork',
298 298 # f_path='file3-fork'
299 299 # )
300 300 #
301 301 # #compare !
302 302 # rev1 = 'default'
303 303 # rev2 = 'default'
304 304 # response = self.app.get(url(controller='compare', action='index',
305 305 # repo_name=r2_name,
306 306 # org_ref_type="branch",
307 307 # org_ref=rev1,
308 308 # other_ref_type="branch",
309 309 # other_ref=rev2,
310 310 # repo=r1_name,
311 311 # bundle=False,
312 312 # ))
313 313 #
314 314 # try:
315 315 # #response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
316 316 #
317 317 # #add new commit into parent !
318 318 # cs0 = ScmModel().create_node(
319 319 # repo=repo1.scm_instance, repo_name=r1_name,
320 320 # cs=EmptyChangeset(alias='hg'), user=TEST_USER_ADMIN_LOGIN,
321 321 # author=TEST_USER_ADMIN_LOGIN,
322 322 # message='commit2',
323 323 # content='line1',
324 324 # f_path='file2'
325 325 # )
326 326 # #compare !
327 327 # rev1 = 'default'
328 328 # rev2 = 'default'
329 329 # response = self.app.get(url(controller='compare', action='index',
330 330 # repo_name=r2_name,
331 331 # org_ref_type="branch",
332 332 # org_ref=rev1,
333 333 # other_ref_type="branch",
334 334 # other_ref=rev2,
335 335 # repo=r1_name,
336 336 # bundle=False
337 337 # ))
338 338 #
339 339 # response.mustcontain('%s@%s -> %s@%s' % (r2_name, rev1, r1_name, rev2))
340 340 # response.mustcontain("""file1-line1-from-fork""")
341 341 # response.mustcontain("""file2-line1-from-fork""")
342 342 # response.mustcontain("""file3-line1-from-fork""")
343 343 # self.assertFalse("""<a href="#">file2</a>""" in response.body) # new commit from parent
344 344 # self.assertFalse("""line1-from-new-parent""" in response.body)
345 345 # finally:
346 346 # RepoModel().delete(r2_id)
347 347 # RepoModel().delete(r1_id)
General Comments 0
You need to be logged in to leave comments. Login now