##// END OF EJS Templates
fixes issue #320....
marcink -
r1764:39b49c99 beta
parent child Browse files
Show More
@@ -1,677 +1,711 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 import random
6 import random
7 import hashlib
7 import hashlib
8 import StringIO
8 import StringIO
9 import urllib
9 import urllib
10 import math
10 import math
11
11
12 from datetime import datetime
12 from datetime import datetime
13 from pygments.formatters.html import HtmlFormatter
13 from pygments.formatters.html import HtmlFormatter
14 from pygments import highlight as code_highlight
14 from pygments import highlight as code_highlight
15 from pylons import url, request, config
15 from pylons import url, request, config
16 from pylons.i18n.translation import _, ungettext
16 from pylons.i18n.translation import _, ungettext
17
17
18 from webhelpers.html import literal, HTML, escape
18 from webhelpers.html import literal, HTML, escape
19 from webhelpers.html.tools import *
19 from webhelpers.html.tools import *
20 from webhelpers.html.builder import make_tag
20 from webhelpers.html.builder import make_tag
21 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
21 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
22 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
22 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
23 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
23 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
24 password, textarea, title, ul, xml_declaration, radio
24 password, textarea, title, ul, xml_declaration, radio
25 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
25 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
26 mail_to, strip_links, strip_tags, tag_re
26 mail_to, strip_links, strip_tags, tag_re
27 from webhelpers.number import format_byte_size, format_bit_size
27 from webhelpers.number import format_byte_size, format_bit_size
28 from webhelpers.pylonslib import Flash as _Flash
28 from webhelpers.pylonslib import Flash as _Flash
29 from webhelpers.pylonslib.secure_form import secure_form
29 from webhelpers.pylonslib.secure_form import secure_form
30 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
30 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
31 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
31 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
32 replace_whitespace, urlify, truncate, wrap_paragraphs
32 replace_whitespace, urlify, truncate, wrap_paragraphs
33 from webhelpers.date import time_ago_in_words
33 from webhelpers.date import time_ago_in_words
34 from webhelpers.paginate import Page
34 from webhelpers.paginate import Page
35 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
35 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
36 convert_boolean_attrs, NotGiven, _make_safe_id_component
36 convert_boolean_attrs, NotGiven, _make_safe_id_component
37
37
38 from rhodecode.lib.annotate import annotate_highlight
38 from rhodecode.lib.annotate import annotate_highlight
39 from rhodecode.lib.utils import repo_name_slug
39 from rhodecode.lib.utils import repo_name_slug
40 from rhodecode.lib import str2bool, safe_unicode, safe_str, get_changeset_safe
40 from rhodecode.lib import str2bool, safe_unicode, safe_str, get_changeset_safe
41
42 from rhodecode.lib.markup_renderer import MarkupRenderer
41 from rhodecode.lib.markup_renderer import MarkupRenderer
43
42
44 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
43 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
45 """
44 """
46 Reset button
45 Reset button
47 """
46 """
48 _set_input_attrs(attrs, type, name, value)
47 _set_input_attrs(attrs, type, name, value)
49 _set_id_attr(attrs, id, name)
48 _set_id_attr(attrs, id, name)
50 convert_boolean_attrs(attrs, ["disabled"])
49 convert_boolean_attrs(attrs, ["disabled"])
51 return HTML.input(**attrs)
50 return HTML.input(**attrs)
52
51
53 reset = _reset
52 reset = _reset
54 safeid = _make_safe_id_component
53 safeid = _make_safe_id_component
55
54
56 def get_token():
55 def get_token():
57 """Return the current authentication token, creating one if one doesn't
56 """Return the current authentication token, creating one if one doesn't
58 already exist.
57 already exist.
59 """
58 """
60 token_key = "_authentication_token"
59 token_key = "_authentication_token"
61 from pylons import session
60 from pylons import session
62 if not token_key in session:
61 if not token_key in session:
63 try:
62 try:
64 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
63 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
65 except AttributeError: # Python < 2.4
64 except AttributeError: # Python < 2.4
66 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
65 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
67 session[token_key] = token
66 session[token_key] = token
68 if hasattr(session, 'save'):
67 if hasattr(session, 'save'):
69 session.save()
68 session.save()
70 return session[token_key]
69 return session[token_key]
71
70
72 class _GetError(object):
71 class _GetError(object):
73 """Get error from form_errors, and represent it as span wrapped error
72 """Get error from form_errors, and represent it as span wrapped error
74 message
73 message
75
74
76 :param field_name: field to fetch errors for
75 :param field_name: field to fetch errors for
77 :param form_errors: form errors dict
76 :param form_errors: form errors dict
78 """
77 """
79
78
80 def __call__(self, field_name, form_errors):
79 def __call__(self, field_name, form_errors):
81 tmpl = """<span class="error_msg">%s</span>"""
80 tmpl = """<span class="error_msg">%s</span>"""
82 if form_errors and form_errors.has_key(field_name):
81 if form_errors and form_errors.has_key(field_name):
83 return literal(tmpl % form_errors.get(field_name))
82 return literal(tmpl % form_errors.get(field_name))
84
83
85 get_error = _GetError()
84 get_error = _GetError()
86
85
87 class _ToolTip(object):
86 class _ToolTip(object):
88
87
89 def __call__(self, tooltip_title, trim_at=50):
88 def __call__(self, tooltip_title, trim_at=50):
90 """Special function just to wrap our text into nice formatted
89 """Special function just to wrap our text into nice formatted
91 autowrapped text
90 autowrapped text
92
91
93 :param tooltip_title:
92 :param tooltip_title:
94 """
93 """
95 return escape(tooltip_title)
94 return escape(tooltip_title)
96 tooltip = _ToolTip()
95 tooltip = _ToolTip()
97
96
98 class _FilesBreadCrumbs(object):
97 class _FilesBreadCrumbs(object):
99
98
100 def __call__(self, repo_name, rev, paths):
99 def __call__(self, repo_name, rev, paths):
101 if isinstance(paths, str):
100 if isinstance(paths, str):
102 paths = safe_unicode(paths)
101 paths = safe_unicode(paths)
103 url_l = [link_to(repo_name, url('files_home',
102 url_l = [link_to(repo_name, url('files_home',
104 repo_name=repo_name,
103 repo_name=repo_name,
105 revision=rev, f_path=''))]
104 revision=rev, f_path=''))]
106 paths_l = paths.split('/')
105 paths_l = paths.split('/')
107 for cnt, p in enumerate(paths_l):
106 for cnt, p in enumerate(paths_l):
108 if p != '':
107 if p != '':
109 url_l.append(link_to(p, url('files_home',
108 url_l.append(link_to(p, url('files_home',
110 repo_name=repo_name,
109 repo_name=repo_name,
111 revision=rev,
110 revision=rev,
112 f_path='/'.join(paths_l[:cnt + 1]))))
111 f_path='/'.join(paths_l[:cnt + 1]))))
113
112
114 return literal('/'.join(url_l))
113 return literal('/'.join(url_l))
115
114
116 files_breadcrumbs = _FilesBreadCrumbs()
115 files_breadcrumbs = _FilesBreadCrumbs()
117
116
118 class CodeHtmlFormatter(HtmlFormatter):
117 class CodeHtmlFormatter(HtmlFormatter):
119 """My code Html Formatter for source codes
118 """My code Html Formatter for source codes
120 """
119 """
121
120
122 def wrap(self, source, outfile):
121 def wrap(self, source, outfile):
123 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
122 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
124
123
125 def _wrap_code(self, source):
124 def _wrap_code(self, source):
126 for cnt, it in enumerate(source):
125 for cnt, it in enumerate(source):
127 i, t = it
126 i, t = it
128 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
127 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
129 yield i, t
128 yield i, t
130
129
131 def _wrap_tablelinenos(self, inner):
130 def _wrap_tablelinenos(self, inner):
132 dummyoutfile = StringIO.StringIO()
131 dummyoutfile = StringIO.StringIO()
133 lncount = 0
132 lncount = 0
134 for t, line in inner:
133 for t, line in inner:
135 if t:
134 if t:
136 lncount += 1
135 lncount += 1
137 dummyoutfile.write(line)
136 dummyoutfile.write(line)
138
137
139 fl = self.linenostart
138 fl = self.linenostart
140 mw = len(str(lncount + fl - 1))
139 mw = len(str(lncount + fl - 1))
141 sp = self.linenospecial
140 sp = self.linenospecial
142 st = self.linenostep
141 st = self.linenostep
143 la = self.lineanchors
142 la = self.lineanchors
144 aln = self.anchorlinenos
143 aln = self.anchorlinenos
145 nocls = self.noclasses
144 nocls = self.noclasses
146 if sp:
145 if sp:
147 lines = []
146 lines = []
148
147
149 for i in range(fl, fl + lncount):
148 for i in range(fl, fl + lncount):
150 if i % st == 0:
149 if i % st == 0:
151 if i % sp == 0:
150 if i % sp == 0:
152 if aln:
151 if aln:
153 lines.append('<a href="#%s%d" class="special">%*d</a>' %
152 lines.append('<a href="#%s%d" class="special">%*d</a>' %
154 (la, i, mw, i))
153 (la, i, mw, i))
155 else:
154 else:
156 lines.append('<span class="special">%*d</span>' % (mw, i))
155 lines.append('<span class="special">%*d</span>' % (mw, i))
157 else:
156 else:
158 if aln:
157 if aln:
159 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
158 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
160 else:
159 else:
161 lines.append('%*d' % (mw, i))
160 lines.append('%*d' % (mw, i))
162 else:
161 else:
163 lines.append('')
162 lines.append('')
164 ls = '\n'.join(lines)
163 ls = '\n'.join(lines)
165 else:
164 else:
166 lines = []
165 lines = []
167 for i in range(fl, fl + lncount):
166 for i in range(fl, fl + lncount):
168 if i % st == 0:
167 if i % st == 0:
169 if aln:
168 if aln:
170 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
169 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
171 else:
170 else:
172 lines.append('%*d' % (mw, i))
171 lines.append('%*d' % (mw, i))
173 else:
172 else:
174 lines.append('')
173 lines.append('')
175 ls = '\n'.join(lines)
174 ls = '\n'.join(lines)
176
175
177 # in case you wonder about the seemingly redundant <div> here: since the
176 # in case you wonder about the seemingly redundant <div> here: since the
178 # content in the other cell also is wrapped in a div, some browsers in
177 # content in the other cell also is wrapped in a div, some browsers in
179 # some configurations seem to mess up the formatting...
178 # some configurations seem to mess up the formatting...
180 if nocls:
179 if nocls:
181 yield 0, ('<table class="%stable">' % self.cssclass +
180 yield 0, ('<table class="%stable">' % self.cssclass +
182 '<tr><td><div class="linenodiv" '
181 '<tr><td><div class="linenodiv" '
183 'style="background-color: #f0f0f0; padding-right: 10px">'
182 'style="background-color: #f0f0f0; padding-right: 10px">'
184 '<pre style="line-height: 125%">' +
183 '<pre style="line-height: 125%">' +
185 ls + '</pre></div></td><td id="hlcode" class="code">')
184 ls + '</pre></div></td><td id="hlcode" class="code">')
186 else:
185 else:
187 yield 0, ('<table class="%stable">' % self.cssclass +
186 yield 0, ('<table class="%stable">' % self.cssclass +
188 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
187 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
189 ls + '</pre></div></td><td id="hlcode" class="code">')
188 ls + '</pre></div></td><td id="hlcode" class="code">')
190 yield 0, dummyoutfile.getvalue()
189 yield 0, dummyoutfile.getvalue()
191 yield 0, '</td></tr></table>'
190 yield 0, '</td></tr></table>'
192
191
193
192
194 def pygmentize(filenode, **kwargs):
193 def pygmentize(filenode, **kwargs):
195 """pygmentize function using pygments
194 """pygmentize function using pygments
196
195
197 :param filenode:
196 :param filenode:
198 """
197 """
199
198
200 return literal(code_highlight(filenode.content,
199 return literal(code_highlight(filenode.content,
201 filenode.lexer, CodeHtmlFormatter(**kwargs)))
200 filenode.lexer, CodeHtmlFormatter(**kwargs)))
202
201
203 def pygmentize_annotation(repo_name, filenode, **kwargs):
202 def pygmentize_annotation(repo_name, filenode, **kwargs):
204 """pygmentize function for annotation
203 """pygmentize function for annotation
205
204
206 :param filenode:
205 :param filenode:
207 """
206 """
208
207
209 color_dict = {}
208 color_dict = {}
210 def gen_color(n=10000):
209 def gen_color(n=10000):
211 """generator for getting n of evenly distributed colors using
210 """generator for getting n of evenly distributed colors using
212 hsv color and golden ratio. It always return same order of colors
211 hsv color and golden ratio. It always return same order of colors
213
212
214 :returns: RGB tuple
213 :returns: RGB tuple
215 """
214 """
216
215
217 def hsv_to_rgb(h, s, v):
216 def hsv_to_rgb(h, s, v):
218 if s == 0.0: return v, v, v
217 if s == 0.0: return v, v, v
219 i = int(h * 6.0) # XXX assume int() truncates!
218 i = int(h * 6.0) # XXX assume int() truncates!
220 f = (h * 6.0) - i
219 f = (h * 6.0) - i
221 p = v * (1.0 - s)
220 p = v * (1.0 - s)
222 q = v * (1.0 - s * f)
221 q = v * (1.0 - s * f)
223 t = v * (1.0 - s * (1.0 - f))
222 t = v * (1.0 - s * (1.0 - f))
224 i = i % 6
223 i = i % 6
225 if i == 0: return v, t, p
224 if i == 0: return v, t, p
226 if i == 1: return q, v, p
225 if i == 1: return q, v, p
227 if i == 2: return p, v, t
226 if i == 2: return p, v, t
228 if i == 3: return p, q, v
227 if i == 3: return p, q, v
229 if i == 4: return t, p, v
228 if i == 4: return t, p, v
230 if i == 5: return v, p, q
229 if i == 5: return v, p, q
231
230
232 golden_ratio = 0.618033988749895
231 golden_ratio = 0.618033988749895
233 h = 0.22717784590367374
232 h = 0.22717784590367374
234
233
235 for _ in xrange(n):
234 for _ in xrange(n):
236 h += golden_ratio
235 h += golden_ratio
237 h %= 1
236 h %= 1
238 HSV_tuple = [h, 0.95, 0.95]
237 HSV_tuple = [h, 0.95, 0.95]
239 RGB_tuple = hsv_to_rgb(*HSV_tuple)
238 RGB_tuple = hsv_to_rgb(*HSV_tuple)
240 yield map(lambda x:str(int(x * 256)), RGB_tuple)
239 yield map(lambda x:str(int(x * 256)), RGB_tuple)
241
240
242 cgenerator = gen_color()
241 cgenerator = gen_color()
243
242
244 def get_color_string(cs):
243 def get_color_string(cs):
245 if color_dict.has_key(cs):
244 if color_dict.has_key(cs):
246 col = color_dict[cs]
245 col = color_dict[cs]
247 else:
246 else:
248 col = color_dict[cs] = cgenerator.next()
247 col = color_dict[cs] = cgenerator.next()
249 return "color: rgb(%s)! important;" % (', '.join(col))
248 return "color: rgb(%s)! important;" % (', '.join(col))
250
249
251 def url_func(repo_name):
250 def url_func(repo_name):
252
251
253 def _url_func(changeset):
252 def _url_func(changeset):
254 author = changeset.author
253 author = changeset.author
255 date = changeset.date
254 date = changeset.date
256 message = tooltip(changeset.message)
255 message = tooltip(changeset.message)
257
256
258 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
257 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
259 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
258 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
260 "</b> %s<br/></div>")
259 "</b> %s<br/></div>")
261
260
262 tooltip_html = tooltip_html % (author, date, message)
261 tooltip_html = tooltip_html % (author, date, message)
263 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
262 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
264 short_id(changeset.raw_id))
263 short_id(changeset.raw_id))
265 uri = link_to(
264 uri = link_to(
266 lnk_format,
265 lnk_format,
267 url('changeset_home', repo_name=repo_name,
266 url('changeset_home', repo_name=repo_name,
268 revision=changeset.raw_id),
267 revision=changeset.raw_id),
269 style=get_color_string(changeset.raw_id),
268 style=get_color_string(changeset.raw_id),
270 class_='tooltip',
269 class_='tooltip',
271 title=tooltip_html
270 title=tooltip_html
272 )
271 )
273
272
274 uri += '\n'
273 uri += '\n'
275 return uri
274 return uri
276 return _url_func
275 return _url_func
277
276
278 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
277 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
279
278
280 def is_following_repo(repo_name, user_id):
279 def is_following_repo(repo_name, user_id):
281 from rhodecode.model.scm import ScmModel
280 from rhodecode.model.scm import ScmModel
282 return ScmModel().is_following_repo(repo_name, user_id)
281 return ScmModel().is_following_repo(repo_name, user_id)
283
282
284 flash = _Flash()
283 flash = _Flash()
285
284
286 #==============================================================================
285 #==============================================================================
287 # SCM FILTERS available via h.
286 # SCM FILTERS available via h.
288 #==============================================================================
287 #==============================================================================
289 from vcs.utils import author_name, author_email
288 from vcs.utils import author_name, author_email
290 from rhodecode.lib import credentials_filter, age as _age
289 from rhodecode.lib import credentials_filter, age as _age
290 from rhodecode.model.db import User
291
291
292 age = lambda x:_age(x)
292 age = lambda x:_age(x)
293 capitalize = lambda x: x.capitalize()
293 capitalize = lambda x: x.capitalize()
294 email = author_email
294 email = author_email
295 email_or_none = lambda x: email(x) if email(x) != x else None
296 person = lambda x: author_name(x)
295 person = lambda x: author_name(x)
297 short_id = lambda x: x[:12]
296 short_id = lambda x: x[:12]
298 hide_credentials = lambda x: ''.join(credentials_filter(x))
297 hide_credentials = lambda x: ''.join(credentials_filter(x))
299
298
299
300 def email_or_none(x):
301 if email(x) != '':
302 return email(x)
303
304 # See if it contains a username we can get an email from
305 user = User.get_by_username(author_name(x), case_insensitive=True,
306 cache=True)
307 if user is not None:
308 return user.email
309
310 # No valid email, not a valid user in the system, none!
311 return None
312
313 def person(x):
314 # Valid email in the attribute passed, see if they're in the system
315
316 # attr to return from fetched user
317 person_getter = lambda usr: usr.username
318
319 if email(x) != '':
320 user = User.get_by_email(email(x), case_insensitive=True, cache=True)
321 if user is not None:
322 return person_getter(user)
323 return email(x)
324
325 # Maybe it's a username?
326 user = User.get_by_username(author_name(x), case_insensitive=True,
327 cache=True)
328 if user is not None:
329 return person_getter(user)
330
331 # Still nothing? Just pass back the author name then
332 return author_name(x)
333
300 def bool2icon(value):
334 def bool2icon(value):
301 """Returns True/False values represented as small html image of true/false
335 """Returns True/False values represented as small html image of true/false
302 icons
336 icons
303
337
304 :param value: bool value
338 :param value: bool value
305 """
339 """
306
340
307 if value is True:
341 if value is True:
308 return HTML.tag('img', src=url("/images/icons/accept.png"),
342 return HTML.tag('img', src=url("/images/icons/accept.png"),
309 alt=_('True'))
343 alt=_('True'))
310
344
311 if value is False:
345 if value is False:
312 return HTML.tag('img', src=url("/images/icons/cancel.png"),
346 return HTML.tag('img', src=url("/images/icons/cancel.png"),
313 alt=_('False'))
347 alt=_('False'))
314
348
315 return value
349 return value
316
350
317
351
318 def action_parser(user_log, feed=False):
352 def action_parser(user_log, feed=False):
319 """This helper will action_map the specified string action into translated
353 """This helper will action_map the specified string action into translated
320 fancy names with icons and links
354 fancy names with icons and links
321
355
322 :param user_log: user log instance
356 :param user_log: user log instance
323 :param feed: use output for feeds (no html and fancy icons)
357 :param feed: use output for feeds (no html and fancy icons)
324 """
358 """
325
359
326 action = user_log.action
360 action = user_log.action
327 action_params = ' '
361 action_params = ' '
328
362
329 x = action.split(':')
363 x = action.split(':')
330
364
331 if len(x) > 1:
365 if len(x) > 1:
332 action, action_params = x
366 action, action_params = x
333
367
334 def get_cs_links():
368 def get_cs_links():
335 revs_limit = 3 #display this amount always
369 revs_limit = 3 #display this amount always
336 revs_top_limit = 50 #show upto this amount of changesets hidden
370 revs_top_limit = 50 #show upto this amount of changesets hidden
337 revs = action_params.split(',')
371 revs = action_params.split(',')
338 repo_name = user_log.repository.repo_name
372 repo_name = user_log.repository.repo_name
339
373
340 from rhodecode.model.scm import ScmModel
374 from rhodecode.model.scm import ScmModel
341 repo = user_log.repository.scm_instance
375 repo = user_log.repository.scm_instance
342
376
343 message = lambda rev: get_changeset_safe(repo, rev).message
377 message = lambda rev: get_changeset_safe(repo, rev).message
344 cs_links = []
378 cs_links = []
345 cs_links.append(" " + ', '.join ([link_to(rev,
379 cs_links.append(" " + ', '.join ([link_to(rev,
346 url('changeset_home',
380 url('changeset_home',
347 repo_name=repo_name,
381 repo_name=repo_name,
348 revision=rev), title=tooltip(message(rev)),
382 revision=rev), title=tooltip(message(rev)),
349 class_='tooltip') for rev in revs[:revs_limit] ]))
383 class_='tooltip') for rev in revs[:revs_limit] ]))
350
384
351 compare_view = (' <div class="compare_view tooltip" title="%s">'
385 compare_view = (' <div class="compare_view tooltip" title="%s">'
352 '<a href="%s">%s</a> '
386 '<a href="%s">%s</a> '
353 '</div>' % (_('Show all combined changesets %s->%s' \
387 '</div>' % (_('Show all combined changesets %s->%s' \
354 % (revs[0], revs[-1])),
388 % (revs[0], revs[-1])),
355 url('changeset_home', repo_name=repo_name,
389 url('changeset_home', repo_name=repo_name,
356 revision='%s...%s' % (revs[0], revs[-1])
390 revision='%s...%s' % (revs[0], revs[-1])
357 ),
391 ),
358 _('compare view'))
392 _('compare view'))
359 )
393 )
360
394
361 if len(revs) > revs_limit:
395 if len(revs) > revs_limit:
362 uniq_id = revs[0]
396 uniq_id = revs[0]
363 html_tmpl = ('<span> %s '
397 html_tmpl = ('<span> %s '
364 '<a class="show_more" id="_%s" href="#more">%s</a> '
398 '<a class="show_more" id="_%s" href="#more">%s</a> '
365 '%s</span>')
399 '%s</span>')
366 if not feed:
400 if not feed:
367 cs_links.append(html_tmpl % (_('and'), uniq_id, _('%s more') \
401 cs_links.append(html_tmpl % (_('and'), uniq_id, _('%s more') \
368 % (len(revs) - revs_limit),
402 % (len(revs) - revs_limit),
369 _('revisions')))
403 _('revisions')))
370
404
371 if not feed:
405 if not feed:
372 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
406 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
373 else:
407 else:
374 html_tmpl = '<span id="%s"> %s </span>'
408 html_tmpl = '<span id="%s"> %s </span>'
375
409
376 cs_links.append(html_tmpl % (uniq_id, ', '.join([link_to(rev,
410 cs_links.append(html_tmpl % (uniq_id, ', '.join([link_to(rev,
377 url('changeset_home',
411 url('changeset_home',
378 repo_name=repo_name, revision=rev),
412 repo_name=repo_name, revision=rev),
379 title=message(rev), class_='tooltip')
413 title=message(rev), class_='tooltip')
380 for rev in revs[revs_limit:revs_top_limit]])))
414 for rev in revs[revs_limit:revs_top_limit]])))
381 if len(revs) > 1:
415 if len(revs) > 1:
382 cs_links.append(compare_view)
416 cs_links.append(compare_view)
383 return ''.join(cs_links)
417 return ''.join(cs_links)
384
418
385 def get_fork_name():
419 def get_fork_name():
386 repo_name = action_params
420 repo_name = action_params
387 return _('fork name ') + str(link_to(action_params, url('summary_home',
421 return _('fork name ') + str(link_to(action_params, url('summary_home',
388 repo_name=repo_name,)))
422 repo_name=repo_name,)))
389
423
390 action_map = {'user_deleted_repo':(_('[deleted] repository'), None),
424 action_map = {'user_deleted_repo':(_('[deleted] repository'), None),
391 'user_created_repo':(_('[created] repository'), None),
425 'user_created_repo':(_('[created] repository'), None),
392 'user_created_fork':(_('[created] repository as fork'), None),
426 'user_created_fork':(_('[created] repository as fork'), None),
393 'user_forked_repo':(_('[forked] repository'), get_fork_name),
427 'user_forked_repo':(_('[forked] repository'), get_fork_name),
394 'user_updated_repo':(_('[updated] repository'), None),
428 'user_updated_repo':(_('[updated] repository'), None),
395 'admin_deleted_repo':(_('[delete] repository'), None),
429 'admin_deleted_repo':(_('[delete] repository'), None),
396 'admin_created_repo':(_('[created] repository'), None),
430 'admin_created_repo':(_('[created] repository'), None),
397 'admin_forked_repo':(_('[forked] repository'), None),
431 'admin_forked_repo':(_('[forked] repository'), None),
398 'admin_updated_repo':(_('[updated] repository'), None),
432 'admin_updated_repo':(_('[updated] repository'), None),
399 'push':(_('[pushed] into'), get_cs_links),
433 'push':(_('[pushed] into'), get_cs_links),
400 'push_local':(_('[committed via RhodeCode] into'), get_cs_links),
434 'push_local':(_('[committed via RhodeCode] into'), get_cs_links),
401 'push_remote':(_('[pulled from remote] into'), get_cs_links),
435 'push_remote':(_('[pulled from remote] into'), get_cs_links),
402 'pull':(_('[pulled] from'), None),
436 'pull':(_('[pulled] from'), None),
403 'started_following_repo':(_('[started following] repository'), None),
437 'started_following_repo':(_('[started following] repository'), None),
404 'stopped_following_repo':(_('[stopped following] repository'), None),
438 'stopped_following_repo':(_('[stopped following] repository'), None),
405 }
439 }
406
440
407 action_str = action_map.get(action, action)
441 action_str = action_map.get(action, action)
408 if feed:
442 if feed:
409 action = action_str[0].replace('[', '').replace(']', '')
443 action = action_str[0].replace('[', '').replace(']', '')
410 else:
444 else:
411 action = action_str[0].replace('[', '<span class="journal_highlight">')\
445 action = action_str[0].replace('[', '<span class="journal_highlight">')\
412 .replace(']', '</span>')
446 .replace(']', '</span>')
413
447
414 action_params_func = lambda :""
448 action_params_func = lambda :""
415
449
416 if callable(action_str[1]):
450 if callable(action_str[1]):
417 action_params_func = action_str[1]
451 action_params_func = action_str[1]
418
452
419 return [literal(action), action_params_func]
453 return [literal(action), action_params_func]
420
454
421 def action_parser_icon(user_log):
455 def action_parser_icon(user_log):
422 action = user_log.action
456 action = user_log.action
423 action_params = None
457 action_params = None
424 x = action.split(':')
458 x = action.split(':')
425
459
426 if len(x) > 1:
460 if len(x) > 1:
427 action, action_params = x
461 action, action_params = x
428
462
429 tmpl = """<img src="%s%s" alt="%s"/>"""
463 tmpl = """<img src="%s%s" alt="%s"/>"""
430 map = {'user_deleted_repo':'database_delete.png',
464 map = {'user_deleted_repo':'database_delete.png',
431 'user_created_repo':'database_add.png',
465 'user_created_repo':'database_add.png',
432 'user_created_fork':'arrow_divide.png',
466 'user_created_fork':'arrow_divide.png',
433 'user_forked_repo':'arrow_divide.png',
467 'user_forked_repo':'arrow_divide.png',
434 'user_updated_repo':'database_edit.png',
468 'user_updated_repo':'database_edit.png',
435 'admin_deleted_repo':'database_delete.png',
469 'admin_deleted_repo':'database_delete.png',
436 'admin_created_repo':'database_add.png',
470 'admin_created_repo':'database_add.png',
437 'admin_forked_repo':'arrow_divide.png',
471 'admin_forked_repo':'arrow_divide.png',
438 'admin_updated_repo':'database_edit.png',
472 'admin_updated_repo':'database_edit.png',
439 'push':'script_add.png',
473 'push':'script_add.png',
440 'push_local':'script_edit.png',
474 'push_local':'script_edit.png',
441 'push_remote':'connect.png',
475 'push_remote':'connect.png',
442 'pull':'down_16.png',
476 'pull':'down_16.png',
443 'started_following_repo':'heart_add.png',
477 'started_following_repo':'heart_add.png',
444 'stopped_following_repo':'heart_delete.png',
478 'stopped_following_repo':'heart_delete.png',
445 }
479 }
446 return literal(tmpl % ((url('/images/icons/')),
480 return literal(tmpl % ((url('/images/icons/')),
447 map.get(action, action), action))
481 map.get(action, action), action))
448
482
449
483
450 #==============================================================================
484 #==============================================================================
451 # PERMS
485 # PERMS
452 #==============================================================================
486 #==============================================================================
453 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
487 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
454 HasRepoPermissionAny, HasRepoPermissionAll
488 HasRepoPermissionAny, HasRepoPermissionAll
455
489
456 #==============================================================================
490 #==============================================================================
457 # GRAVATAR URL
491 # GRAVATAR URL
458 #==============================================================================
492 #==============================================================================
459
493
460 def gravatar_url(email_address, size=30):
494 def gravatar_url(email_address, size=30):
461 if (not str2bool(config['app_conf'].get('use_gravatar')) or
495 if (not str2bool(config['app_conf'].get('use_gravatar')) or
462 not email_address or email_address == 'anonymous@rhodecode.org'):
496 not email_address or email_address == 'anonymous@rhodecode.org'):
463 return url("/images/user%s.png" % size)
497 return url("/images/user%s.png" % size)
464
498
465 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
499 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
466 default = 'identicon'
500 default = 'identicon'
467 baseurl_nossl = "http://www.gravatar.com/avatar/"
501 baseurl_nossl = "http://www.gravatar.com/avatar/"
468 baseurl_ssl = "https://secure.gravatar.com/avatar/"
502 baseurl_ssl = "https://secure.gravatar.com/avatar/"
469 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
503 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
470
504
471 if isinstance(email_address, unicode):
505 if isinstance(email_address, unicode):
472 #hashlib crashes on unicode items
506 #hashlib crashes on unicode items
473 email_address = safe_str(email_address)
507 email_address = safe_str(email_address)
474 # construct the url
508 # construct the url
475 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
509 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
476 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
510 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
477
511
478 return gravatar_url
512 return gravatar_url
479
513
480
514
481 #==============================================================================
515 #==============================================================================
482 # REPO PAGER, PAGER FOR REPOSITORY
516 # REPO PAGER, PAGER FOR REPOSITORY
483 #==============================================================================
517 #==============================================================================
484 class RepoPage(Page):
518 class RepoPage(Page):
485
519
486 def __init__(self, collection, page=1, items_per_page=20,
520 def __init__(self, collection, page=1, items_per_page=20,
487 item_count=None, url=None, **kwargs):
521 item_count=None, url=None, **kwargs):
488
522
489 """Create a "RepoPage" instance. special pager for paging
523 """Create a "RepoPage" instance. special pager for paging
490 repository
524 repository
491 """
525 """
492 self._url_generator = url
526 self._url_generator = url
493
527
494 # Safe the kwargs class-wide so they can be used in the pager() method
528 # Safe the kwargs class-wide so they can be used in the pager() method
495 self.kwargs = kwargs
529 self.kwargs = kwargs
496
530
497 # Save a reference to the collection
531 # Save a reference to the collection
498 self.original_collection = collection
532 self.original_collection = collection
499
533
500 self.collection = collection
534 self.collection = collection
501
535
502 # The self.page is the number of the current page.
536 # The self.page is the number of the current page.
503 # The first page has the number 1!
537 # The first page has the number 1!
504 try:
538 try:
505 self.page = int(page) # make it int() if we get it as a string
539 self.page = int(page) # make it int() if we get it as a string
506 except (ValueError, TypeError):
540 except (ValueError, TypeError):
507 self.page = 1
541 self.page = 1
508
542
509 self.items_per_page = items_per_page
543 self.items_per_page = items_per_page
510
544
511 # Unless the user tells us how many items the collections has
545 # Unless the user tells us how many items the collections has
512 # we calculate that ourselves.
546 # we calculate that ourselves.
513 if item_count is not None:
547 if item_count is not None:
514 self.item_count = item_count
548 self.item_count = item_count
515 else:
549 else:
516 self.item_count = len(self.collection)
550 self.item_count = len(self.collection)
517
551
518 # Compute the number of the first and last available page
552 # Compute the number of the first and last available page
519 if self.item_count > 0:
553 if self.item_count > 0:
520 self.first_page = 1
554 self.first_page = 1
521 self.page_count = int(math.ceil(float(self.item_count) /
555 self.page_count = int(math.ceil(float(self.item_count) /
522 self.items_per_page))
556 self.items_per_page))
523 self.last_page = self.first_page + self.page_count - 1
557 self.last_page = self.first_page + self.page_count - 1
524
558
525 # Make sure that the requested page number is the range of valid pages
559 # Make sure that the requested page number is the range of valid pages
526 if self.page > self.last_page:
560 if self.page > self.last_page:
527 self.page = self.last_page
561 self.page = self.last_page
528 elif self.page < self.first_page:
562 elif self.page < self.first_page:
529 self.page = self.first_page
563 self.page = self.first_page
530
564
531 # Note: the number of items on this page can be less than
565 # Note: the number of items on this page can be less than
532 # items_per_page if the last page is not full
566 # items_per_page if the last page is not full
533 self.first_item = max(0, (self.item_count) - (self.page *
567 self.first_item = max(0, (self.item_count) - (self.page *
534 items_per_page))
568 items_per_page))
535 self.last_item = ((self.item_count - 1) - items_per_page *
569 self.last_item = ((self.item_count - 1) - items_per_page *
536 (self.page - 1))
570 (self.page - 1))
537
571
538 self.items = list(self.collection[self.first_item:self.last_item + 1])
572 self.items = list(self.collection[self.first_item:self.last_item + 1])
539
573
540
574
541 # Links to previous and next page
575 # Links to previous and next page
542 if self.page > self.first_page:
576 if self.page > self.first_page:
543 self.previous_page = self.page - 1
577 self.previous_page = self.page - 1
544 else:
578 else:
545 self.previous_page = None
579 self.previous_page = None
546
580
547 if self.page < self.last_page:
581 if self.page < self.last_page:
548 self.next_page = self.page + 1
582 self.next_page = self.page + 1
549 else:
583 else:
550 self.next_page = None
584 self.next_page = None
551
585
552 # No items available
586 # No items available
553 else:
587 else:
554 self.first_page = None
588 self.first_page = None
555 self.page_count = 0
589 self.page_count = 0
556 self.last_page = None
590 self.last_page = None
557 self.first_item = None
591 self.first_item = None
558 self.last_item = None
592 self.last_item = None
559 self.previous_page = None
593 self.previous_page = None
560 self.next_page = None
594 self.next_page = None
561 self.items = []
595 self.items = []
562
596
563 # This is a subclass of the 'list' type. Initialise the list now.
597 # This is a subclass of the 'list' type. Initialise the list now.
564 list.__init__(self, reversed(self.items))
598 list.__init__(self, reversed(self.items))
565
599
566
600
567 def changed_tooltip(nodes):
601 def changed_tooltip(nodes):
568 """
602 """
569 Generates a html string for changed nodes in changeset page.
603 Generates a html string for changed nodes in changeset page.
570 It limits the output to 30 entries
604 It limits the output to 30 entries
571
605
572 :param nodes: LazyNodesGenerator
606 :param nodes: LazyNodesGenerator
573 """
607 """
574 if nodes:
608 if nodes:
575 pref = ': <br/> '
609 pref = ': <br/> '
576 suf = ''
610 suf = ''
577 if len(nodes) > 30:
611 if len(nodes) > 30:
578 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
612 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
579 return literal(pref + '<br/> '.join([safe_unicode(x.path)
613 return literal(pref + '<br/> '.join([safe_unicode(x.path)
580 for x in nodes[:30]]) + suf)
614 for x in nodes[:30]]) + suf)
581 else:
615 else:
582 return ': ' + _('No Files')
616 return ': ' + _('No Files')
583
617
584
618
585
619
586 def repo_link(groups_and_repos):
620 def repo_link(groups_and_repos):
587 """
621 """
588 Makes a breadcrumbs link to repo within a group
622 Makes a breadcrumbs link to repo within a group
589 joins &raquo; on each group to create a fancy link
623 joins &raquo; on each group to create a fancy link
590
624
591 ex::
625 ex::
592 group >> subgroup >> repo
626 group >> subgroup >> repo
593
627
594 :param groups_and_repos:
628 :param groups_and_repos:
595 """
629 """
596 groups, repo_name = groups_and_repos
630 groups, repo_name = groups_and_repos
597
631
598 if not groups:
632 if not groups:
599 return repo_name
633 return repo_name
600 else:
634 else:
601 def make_link(group):
635 def make_link(group):
602 return link_to(group.name, url('repos_group_home',
636 return link_to(group.name, url('repos_group_home',
603 group_name=group.group_name))
637 group_name=group.group_name))
604 return literal(' &raquo; '.join(map(make_link, groups)) + \
638 return literal(' &raquo; '.join(map(make_link, groups)) + \
605 " &raquo; " + repo_name)
639 " &raquo; " + repo_name)
606
640
607 def fancy_file_stats(stats):
641 def fancy_file_stats(stats):
608 """
642 """
609 Displays a fancy two colored bar for number of added/deleted
643 Displays a fancy two colored bar for number of added/deleted
610 lines of code on file
644 lines of code on file
611
645
612 :param stats: two element list of added/deleted lines of code
646 :param stats: two element list of added/deleted lines of code
613 """
647 """
614
648
615 a, d, t = stats[0], stats[1], stats[0] + stats[1]
649 a, d, t = stats[0], stats[1], stats[0] + stats[1]
616 width = 100
650 width = 100
617 unit = float(width) / (t or 1)
651 unit = float(width) / (t or 1)
618
652
619 # needs > 9% of width to be visible or 0 to be hidden
653 # needs > 9% of width to be visible or 0 to be hidden
620 a_p = max(9, unit * a) if a > 0 else 0
654 a_p = max(9, unit * a) if a > 0 else 0
621 d_p = max(9, unit * d) if d > 0 else 0
655 d_p = max(9, unit * d) if d > 0 else 0
622 p_sum = a_p + d_p
656 p_sum = a_p + d_p
623
657
624 if p_sum > width:
658 if p_sum > width:
625 #adjust the percentage to be == 100% since we adjusted to 9
659 #adjust the percentage to be == 100% since we adjusted to 9
626 if a_p > d_p:
660 if a_p > d_p:
627 a_p = a_p - (p_sum - width)
661 a_p = a_p - (p_sum - width)
628 else:
662 else:
629 d_p = d_p - (p_sum - width)
663 d_p = d_p - (p_sum - width)
630
664
631 a_v = a if a > 0 else ''
665 a_v = a if a > 0 else ''
632 d_v = d if d > 0 else ''
666 d_v = d if d > 0 else ''
633
667
634
668
635 def cgen(l_type):
669 def cgen(l_type):
636 mapping = {'tr':'top-right-rounded-corner',
670 mapping = {'tr':'top-right-rounded-corner',
637 'tl':'top-left-rounded-corner',
671 'tl':'top-left-rounded-corner',
638 'br':'bottom-right-rounded-corner',
672 'br':'bottom-right-rounded-corner',
639 'bl':'bottom-left-rounded-corner'}
673 'bl':'bottom-left-rounded-corner'}
640 map_getter = lambda x:mapping[x]
674 map_getter = lambda x:mapping[x]
641
675
642 if l_type == 'a' and d_v:
676 if l_type == 'a' and d_v:
643 #case when added and deleted are present
677 #case when added and deleted are present
644 return ' '.join(map(map_getter, ['tl', 'bl']))
678 return ' '.join(map(map_getter, ['tl', 'bl']))
645
679
646 if l_type == 'a' and not d_v:
680 if l_type == 'a' and not d_v:
647 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
681 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
648
682
649 if l_type == 'd' and a_v:
683 if l_type == 'd' and a_v:
650 return ' '.join(map(map_getter, ['tr', 'br']))
684 return ' '.join(map(map_getter, ['tr', 'br']))
651
685
652 if l_type == 'd' and not a_v:
686 if l_type == 'd' and not a_v:
653 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
687 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
654
688
655
689
656
690
657 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
691 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
658 a_p, a_v)
692 a_p, a_v)
659 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
693 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
660 d_p, d_v)
694 d_p, d_v)
661 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
695 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
662
696
663
697
664 def urlify_text(text):
698 def urlify_text(text):
665 import re
699 import re
666
700
667 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
701 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
668
702
669 def url_func(match_obj):
703 def url_func(match_obj):
670 url_full = match_obj.groups()[0]
704 url_full = match_obj.groups()[0]
671 return '<a href="%(url)s">%(url)s</a>' % ({'url':url_full})
705 return '<a href="%(url)s">%(url)s</a>' % ({'url':url_full})
672
706
673 return literal(url_pat.sub(url_func, text))
707 return literal(url_pat.sub(url_func, text))
674
708
675
709
676 def rst(source):
710 def rst(source):
677 return literal('<div class="rst-block">%s</div>' % MarkupRenderer.rst(source))
711 return literal('<div class="rst-block">%s</div>' % MarkupRenderer.rst(source))
@@ -1,189 +1,188 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
6 ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
7 </%def>
7 </%def>
8
8
9 <%def name="breadcrumbs_links()">
9 <%def name="breadcrumbs_links()">
10 ${h.link_to(u'Home',h.url('/'))}
10 ${h.link_to(u'Home',h.url('/'))}
11 &raquo;
11 &raquo;
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 &raquo;
13 &raquo;
14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
14 ${_('Changelog')} - ${_('showing ')} ${c.size if c.size <= c.total_cs else c.total_cs} ${_('out of')} ${c.total_cs} ${_('revisions')}
15 </%def>
15 </%def>
16
16
17 <%def name="page_nav()">
17 <%def name="page_nav()">
18 ${self.menu('changelog')}
18 ${self.menu('changelog')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <div class="box">
22 <div class="box">
23 <!-- box / title -->
23 <!-- box / title -->
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27 <div class="table">
27 <div class="table">
28 % if c.pagination:
28 % if c.pagination:
29 <div id="graph">
29 <div id="graph">
30 <div id="graph_nodes">
30 <div id="graph_nodes">
31 <canvas id="graph_canvas"></canvas>
31 <canvas id="graph_canvas"></canvas>
32 </div>
32 </div>
33 <div id="graph_content">
33 <div id="graph_content">
34 <div class="container_header">
34 <div class="container_header">
35 ${h.form(h.url.current(),method='get')}
35 ${h.form(h.url.current(),method='get')}
36 <div class="info_box" style="float:left">
36 <div class="info_box" style="float:left">
37 ${h.submit('set',_('Show'),class_="ui-btn")}
37 ${h.submit('set',_('Show'),class_="ui-btn")}
38 ${h.text('size',size=1,value=c.size)}
38 ${h.text('size',size=1,value=c.size)}
39 <span class="rev">${_('revisions')}</span>
39 <span class="rev">${_('revisions')}</span>
40 </div>
40 </div>
41 ${h.end_form()}
41 ${h.end_form()}
42 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
42 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
43 <div id="rev_range_container" style="display:none"></div>
43 <div id="rev_range_container" style="display:none"></div>
44 </div>
44 </div>
45
45
46 %for cnt,cs in enumerate(c.pagination):
46 %for cnt,cs in enumerate(c.pagination):
47 <div id="chg_${cnt+1}" class="container">
47 <div id="chg_${cnt+1}" class="container">
48 <div class="left">
48 <div class="left">
49 <div class="date">
49 <div class="date">
50 ${h.checkbox(cs.short_id,class_="changeset_range")}
50 ${h.checkbox(cs.short_id,class_="changeset_range")}
51 <span>${_('commit')} ${cs.revision}: ${h.short_id(cs.raw_id)}@${cs.date}</span>
51 <span>${_('commit')} ${cs.revision}: ${h.short_id(cs.raw_id)}@${cs.date}</span>
52 </div>
52 </div>
53 <div class="author">
53 <div class="author">
54 <div class="gravatar">
54 <div class="gravatar">
55 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),16)}"/>
55 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),16)}"/>
56 </div>
56 </div>
57 <div title="${h.email_or_none(cs.author)}" class="user">${h.person(cs.author)}</div>
57 <div title="${cs.author}" class="user">${h.person(cs.author)}</div>
58 ##<span><a href="mailto:${h.email_or_none(cs.author)}">${h.email_or_none(cs.author)}</a></span><br/>
59 </div>
58 </div>
60 <div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
59 <div class="message">${h.link_to(h.wrap_paragraphs(cs.message),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
61 </div>
60 </div>
62 <div class="right">
61 <div class="right">
63 <div id="${cs.raw_id}_changes_info" class="changes">
62 <div id="${cs.raw_id}_changes_info" class="changes">
64 <span id="${cs.raw_id}" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</span>
63 <span id="${cs.raw_id}" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</span>
65 </div>
64 </div>
66 %if len(cs.parents)>1:
65 %if len(cs.parents)>1:
67 <div class="merge">${_('merge')}</div>
66 <div class="merge">${_('merge')}</div>
68 %endif
67 %endif
69 %if cs.parents:
68 %if cs.parents:
70 %for p_cs in reversed(cs.parents):
69 %for p_cs in reversed(cs.parents):
71 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
70 <div class="parent">${_('Parent')} ${p_cs.revision}: ${h.link_to(h.short_id(p_cs.raw_id),
72 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
71 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}
73 </div>
72 </div>
74 %endfor
73 %endfor
75 %else:
74 %else:
76 <div class="parent">${_('No parents')}</div>
75 <div class="parent">${_('No parents')}</div>
77 %endif
76 %endif
78
77
79 <span class="logtags">
78 <span class="logtags">
80 %if cs.branch:
79 %if cs.branch:
81 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
80 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
82 ${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
81 ${h.link_to(cs.branch,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
83 %endif
82 %endif
84 %for tag in cs.tags:
83 %for tag in cs.tags:
85 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
84 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
86 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
85 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
87 %endfor
86 %endfor
88 </span>
87 </span>
89 </div>
88 </div>
90 </div>
89 </div>
91
90
92 %endfor
91 %endfor
93 <div class="pagination-wh pagination-left">
92 <div class="pagination-wh pagination-left">
94 ${c.pagination.pager('$link_previous ~2~ $link_next')}
93 ${c.pagination.pager('$link_previous ~2~ $link_next')}
95 </div>
94 </div>
96 </div>
95 </div>
97 </div>
96 </div>
98
97
99 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
98 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
100 <script type="text/javascript">
99 <script type="text/javascript">
101 YAHOO.util.Event.onDOMReady(function(){
100 YAHOO.util.Event.onDOMReady(function(){
102
101
103 //Monitor range checkboxes and build a link to changesets
102 //Monitor range checkboxes and build a link to changesets
104 //ranges
103 //ranges
105 var checkboxes = YUD.getElementsByClassName('changeset_range');
104 var checkboxes = YUD.getElementsByClassName('changeset_range');
106 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
105 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
107 YUE.on(checkboxes,'click',function(e){
106 YUE.on(checkboxes,'click',function(e){
108 var checked_checkboxes = [];
107 var checked_checkboxes = [];
109 for (pos in checkboxes){
108 for (pos in checkboxes){
110 if(checkboxes[pos].checked){
109 if(checkboxes[pos].checked){
111 checked_checkboxes.push(checkboxes[pos]);
110 checked_checkboxes.push(checkboxes[pos]);
112 }
111 }
113 }
112 }
114 if(checked_checkboxes.length>1){
113 if(checked_checkboxes.length>1){
115 var rev_end = checked_checkboxes[0].name;
114 var rev_end = checked_checkboxes[0].name;
116 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
115 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
117
116
118 var url = url_tmpl.replace('__REVRANGE__',
117 var url = url_tmpl.replace('__REVRANGE__',
119 rev_start+'...'+rev_end);
118 rev_start+'...'+rev_end);
120
119
121 var link = "<a href="+url+">${_('Show selected changes __S -> __E')}</a>"
120 var link = "<a href="+url+">${_('Show selected changes __S -> __E')}</a>"
122 link = link.replace('__S',rev_start);
121 link = link.replace('__S',rev_start);
123 link = link.replace('__E',rev_end);
122 link = link.replace('__E',rev_end);
124 YUD.get('rev_range_container').innerHTML = link;
123 YUD.get('rev_range_container').innerHTML = link;
125 YUD.setStyle('rev_range_container','display','');
124 YUD.setStyle('rev_range_container','display','');
126 }
125 }
127 else{
126 else{
128 YUD.setStyle('rev_range_container','display','none');
127 YUD.setStyle('rev_range_container','display','none');
129
128
130 }
129 }
131 });
130 });
132
131
133 // Fetch changeset details
132 // Fetch changeset details
134 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
133 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
135 var id = e.currentTarget.id
134 var id = e.currentTarget.id
136 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"
135 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"
137 var url = url.replace('__CS__',id);
136 var url = url.replace('__CS__',id);
138 ypjax(url,id+'_changes_info',function(){tooltip_activate()});
137 ypjax(url,id+'_changes_info',function(){tooltip_activate()});
139 });
138 });
140
139
141 // change branch filter
140 // change branch filter
142 YUE.on(YUD.get('branch_filter'),'change',function(e){
141 YUE.on(YUD.get('branch_filter'),'change',function(e){
143 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
142 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
144 console.log(selected_branch);
143 console.log(selected_branch);
145 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
144 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
146 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
145 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
147 var url = url.replace('__BRANCH__',selected_branch);
146 var url = url.replace('__BRANCH__',selected_branch);
148 if(selected_branch != ''){
147 if(selected_branch != ''){
149 window.location = url;
148 window.location = url;
150 }else{
149 }else{
151 window.location = url_main;
150 window.location = url_main;
152 }
151 }
153
152
154 });
153 });
155
154
156 function set_canvas(heads) {
155 function set_canvas(heads) {
157 var c = document.getElementById('graph_nodes');
156 var c = document.getElementById('graph_nodes');
158 var t = document.getElementById('graph_content');
157 var t = document.getElementById('graph_content');
159 canvas = document.getElementById('graph_canvas');
158 canvas = document.getElementById('graph_canvas');
160 var div_h = t.clientHeight;
159 var div_h = t.clientHeight;
161 c.style.height=div_h+'px';
160 c.style.height=div_h+'px';
162 canvas.setAttribute('height',div_h);
161 canvas.setAttribute('height',div_h);
163 c.style.height=max_w+'px';
162 c.style.height=max_w+'px';
164 canvas.setAttribute('width',max_w);
163 canvas.setAttribute('width',max_w);
165 };
164 };
166 var heads = 1;
165 var heads = 1;
167 var max_heads = 0;
166 var max_heads = 0;
168 var jsdata = ${c.jsdata|n};
167 var jsdata = ${c.jsdata|n};
169
168
170 for( var i=0;i<jsdata.length;i++){
169 for( var i=0;i<jsdata.length;i++){
171 var m = Math.max.apply(Math, jsdata[i][1]);
170 var m = Math.max.apply(Math, jsdata[i][1]);
172 if (m>max_heads){
171 if (m>max_heads){
173 max_heads = m;
172 max_heads = m;
174 }
173 }
175 }
174 }
176 var max_w = Math.max(100,max_heads*25);
175 var max_w = Math.max(100,max_heads*25);
177 set_canvas(max_w);
176 set_canvas(max_w);
178
177
179 var r = new BranchRenderer();
178 var r = new BranchRenderer();
180 r.render(jsdata,max_w);
179 r.render(jsdata,max_w);
181
180
182 });
181 });
183 </script>
182 </script>
184 %else:
183 %else:
185 ${_('There are no changes yet')}
184 ${_('There are no changes yet')}
186 %endif
185 %endif
187 </div>
186 </div>
188 </div>
187 </div>
189 </%def> No newline at end of file
188 </%def>
@@ -1,136 +1,136 b''
1 <%inherit file="/base/base.html"/>
1 <%inherit file="/base/base.html"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('File annotate')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('File annotate')} - ${c.rhodecode_name}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${h.link_to(u'Home',h.url('/'))}
8 ${h.link_to(u'Home',h.url('/'))}
9 &raquo;
9 &raquo;
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('annotate')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
12 ${_('annotate')} @ R${c.cs.revision}:${h.short_id(c.cs.raw_id)}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('files')}
16 ${self.menu('files')}
17 </%def>
17 </%def>
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box">
19 <div class="box">
20 <!-- box / title -->
20 <!-- box / title -->
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 <ul class="links">
23 <ul class="links">
24 <li>
24 <li>
25 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.cs.branch}</a></span>
25 <span style="text-transform: uppercase;"><a href="#">${_('branch')}: ${c.cs.branch}</a></span>
26 </li>
26 </li>
27 </ul>
27 </ul>
28 </div>
28 </div>
29 <div class="table">
29 <div class="table">
30 <div id="files_data">
30 <div id="files_data">
31 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
31 <h3 class="files_location">${_('Location')}: ${h.files_breadcrumbs(c.repo_name,c.cs.revision,c.file.path)}</h3>
32 <dl>
32 <dl>
33 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
33 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
34 <dd>
34 <dd>
35 <div>
35 <div>
36 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
36 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
37 ${h.hidden('diff2',c.file.last_changeset.raw_id)}
37 ${h.hidden('diff2',c.file.last_changeset.raw_id)}
38 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
38 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
39 ${h.submit('diff','diff to revision',class_="ui-btn")}
39 ${h.submit('diff','diff to revision',class_="ui-btn")}
40 ${h.submit('show_rev','show at revision',class_="ui-btn")}
40 ${h.submit('show_rev','show at revision',class_="ui-btn")}
41 ${h.end_form()}
41 ${h.end_form()}
42 </div>
42 </div>
43 </dd>
43 </dd>
44 </dl>
44 </dl>
45 <div id="body" class="codeblock">
45 <div id="body" class="codeblock">
46 <div class="code-header">
46 <div class="code-header">
47 <div class="stats">
47 <div class="stats">
48 <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div>
48 <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div>
49 <div class="left item">${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</div>
49 <div class="left item">${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</div>
50 <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
50 <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
51 <div class="left item last">${c.file.mimetype}</div>
51 <div class="left item last">${c.file.mimetype}</div>
52 <div class="buttons">
52 <div class="buttons">
53 ${h.link_to(_('show source'),h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
53 ${h.link_to(_('show source'),h.url('files_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
54 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
54 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
55 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
55 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
56 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
56 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
57 % if not c.file.is_binary:
57 % if not c.file.is_binary:
58 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
58 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.cs.raw_id,f_path=c.f_path),class_="ui-btn")}
59 % endif
59 % endif
60 % endif
60 % endif
61 </div>
61 </div>
62 </div>
62 </div>
63 <div class="author">
63 <div class="author">
64 <div class="gravatar">
64 <div class="gravatar">
65 <img alt="gravatar" src="${h.gravatar_url(h.email(c.cs.author),16)}"/>
65 <img alt="gravatar" src="${h.gravatar_url(h.email(c.cs.author),16)}"/>
66 </div>
66 </div>
67 <div title="${h.email_or_none(c.cs.author)}" class="user">${h.person(c.cs.author)}</div>
67 <div title="${c.cs.author}" class="user">${h.person(c.cs.author)}</div>
68 </div>
68 </div>
69 <div class="commit">${c.file.last_changeset.message}</div>
69 <div class="commit">${c.file.last_changeset.message}</div>
70 </div>
70 </div>
71 <div class="code-body">
71 <div class="code-body">
72 %if c.file.is_binary:
72 %if c.file.is_binary:
73 ${_('Binary file (%s)') % c.file.mimetype}
73 ${_('Binary file (%s)') % c.file.mimetype}
74 %else:
74 %else:
75 % if c.file.size < c.cut_off_limit:
75 % if c.file.size < c.cut_off_limit:
76 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
76 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
77 %else:
77 %else:
78 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
78 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
79 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path))}
79 h.url('files_raw_home',repo_name=c.repo_name,revision=c.cs.revision,f_path=c.f_path))}
80 %endif
80 %endif
81 <script type="text/javascript">
81 <script type="text/javascript">
82 function highlight_lines(lines){
82 function highlight_lines(lines){
83 for(pos in lines){
83 for(pos in lines){
84 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
84 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
85 }
85 }
86 }
86 }
87 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
87 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
88 if (page_highlights.length == 2){
88 if (page_highlights.length == 2){
89 highlight_ranges = page_highlights[1].split(",");
89 highlight_ranges = page_highlights[1].split(",");
90
90
91 var h_lines = [];
91 var h_lines = [];
92 for (pos in highlight_ranges){
92 for (pos in highlight_ranges){
93 var _range = highlight_ranges[pos].split('-');
93 var _range = highlight_ranges[pos].split('-');
94 if(_range.length == 2){
94 if(_range.length == 2){
95 var start = parseInt(_range[0]);
95 var start = parseInt(_range[0]);
96 var end = parseInt(_range[1]);
96 var end = parseInt(_range[1]);
97 if (start < end){
97 if (start < end){
98 for(var i=start;i<=end;i++){
98 for(var i=start;i<=end;i++){
99 h_lines.push(i);
99 h_lines.push(i);
100 }
100 }
101 }
101 }
102 }
102 }
103 else{
103 else{
104 h_lines.push(parseInt(highlight_ranges[pos]));
104 h_lines.push(parseInt(highlight_ranges[pos]));
105 }
105 }
106 }
106 }
107 highlight_lines(h_lines);
107 highlight_lines(h_lines);
108
108
109 //remember original location
109 //remember original location
110 var old_hash = location.href.substring(location.href.indexOf('#'));
110 var old_hash = location.href.substring(location.href.indexOf('#'));
111
111
112 // this makes a jump to anchor moved by 3 posstions for padding
112 // this makes a jump to anchor moved by 3 posstions for padding
113 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
113 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
114
114
115 //sets old anchor
115 //sets old anchor
116 window.location.hash = old_hash;
116 window.location.hash = old_hash;
117
117
118 }
118 }
119 </script>
119 </script>
120 %endif
120 %endif
121 </div>
121 </div>
122 </div>
122 </div>
123 <script type="text/javascript">
123 <script type="text/javascript">
124 YAHOO.util.Event.onDOMReady(function(){
124 YAHOO.util.Event.onDOMReady(function(){
125 YUE.on('show_rev','click',function(e){
125 YUE.on('show_rev','click',function(e){
126 YAHOO.util.Event.preventDefault(e);
126 YAHOO.util.Event.preventDefault(e);
127 var cs = YAHOO.util.Dom.get('diff1').value;
127 var cs = YAHOO.util.Dom.get('diff1').value;
128 var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
128 var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
129 window.location = url;
129 window.location = url;
130 });
130 });
131 });
131 });
132 </script>
132 </script>
133 </div>
133 </div>
134 </div>
134 </div>
135 </div>
135 </div>
136 </%def>
136 </%def>
@@ -1,109 +1,111 b''
1 <%def name="file_class(node)">
1 <%def name="file_class(node)">
2 %if node.is_file():
2 %if node.is_file():
3 <%return "browser-file" %>
3 <%return "browser-file" %>
4 %else:
4 %else:
5 <%return "browser-dir"%>
5 <%return "browser-dir"%>
6 %endif
6 %endif
7 </%def>
7 </%def>
8 <div id="body" class="browserblock">
8 <div id="body" class="browserblock">
9 <div class="browser-header">
9 <div class="browser-header">
10 <div class="browser-nav">
10 <div class="browser-nav">
11 ${h.form(h.url.current())}
11 ${h.form(h.url.current())}
12 <div class="info_box">
12 <div class="info_box">
13 <span class="rev">${_('view')}@rev</span>
13 <span class="rev">${_('view')}@rev</span>
14 <a class="ui-btn" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
14 <a class="ui-btn" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
15 ${h.text('at_rev',value=c.changeset.revision,size=5)}
15 ${h.text('at_rev',value=c.changeset.revision,size=5)}
16 <a class="ui-btn" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
16 <a class="ui-btn" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
17 ## ${h.submit('view',_('view'),class_="ui-btn")}
17 ## ${h.submit('view',_('view'),class_="ui-btn")}
18 </div>
18 </div>
19 ${h.end_form()}
19 ${h.end_form()}
20 </div>
20 </div>
21 <div class="browser-branch">
21 <div class="browser-branch">
22 ${h.checkbox('stay_at_branch',c.changeset.branch,c.changeset.branch==c.branch)}
22 ${h.checkbox('stay_at_branch',c.changeset.branch,c.changeset.branch==c.branch)}
23 <label>${_('follow current branch')}</label>
23 <label>${_('follow current branch')}</label>
24 </div>
24 </div>
25 <div class="browser-search">
25 <div class="browser-search">
26 <div id="search_activate_id" class="search_activate">
26 <div id="search_activate_id" class="search_activate">
27 <a class="ui-btn" id="filter_activate" href="#">${_('search file list')}</a>
27 <a class="ui-btn" id="filter_activate" href="#">${_('search file list')}</a>
28 </div>
28 </div>
29 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
29 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
30 <div id="add_node_id" class="add_node">
30 <div id="add_node_id" class="add_node">
31 <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
31 <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
32 </div>
32 </div>
33 % endif
33 % endif
34 <div>
34 <div>
35 <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div>
35 <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div>
36 <div id="node_filter_box" style="display:none">
36 <div id="node_filter_box" style="display:none">
37 ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}/<input type="text" value="type to search..." name="filter" size="25" id="node_filter" autocomplete="off">
37 ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}/<input type="text" value="type to search..." name="filter" size="25" id="node_filter" autocomplete="off">
38 </div>
38 </div>
39 </div>
39 </div>
40 </div>
40 </div>
41 </div>
41 </div>
42
42
43 <div class="browser-body">
43 <div class="browser-body">
44 <table class="code-browser">
44 <table class="code-browser">
45 <thead>
45 <thead>
46 <tr>
46 <tr>
47 <th>${_('Name')}</th>
47 <th>${_('Name')}</th>
48 <th>${_('Size')}</th>
48 <th>${_('Size')}</th>
49 <th>${_('Mimetype')}</th>
49 <th>${_('Mimetype')}</th>
50 <th>${_('Revision')}</th>
50 <th>${_('Revision')}</th>
51 <th>${_('Last modified')}</th>
51 <th>${_('Last modified')}</th>
52 <th>${_('Last commiter')}</th>
52 <th>${_('Last commiter')}</th>
53 </tr>
53 </tr>
54 </thead>
54 </thead>
55
55
56 <tbody id="tbody">
56 <tbody id="tbody">
57 %if c.file.parent:
57 %if c.file.parent:
58 <tr class="parity0">
58 <tr class="parity0">
59 <td>
59 <td>
60 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.parent.path),class_="browser-dir ypjax-link")}
60 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.parent.path),class_="browser-dir ypjax-link")}
61 </td>
61 </td>
62 <td></td>
62 <td></td>
63 <td></td>
63 <td></td>
64 <td></td>
64 <td></td>
65 <td></td>
65 <td></td>
66 <td></td>
66 <td></td>
67 </tr>
67 </tr>
68 %endif
68 %endif
69
69
70 %for cnt,node in enumerate(c.file):
70 %for cnt,node in enumerate(c.file):
71 <tr class="parity${cnt%2}">
71 <tr class="parity${cnt%2}">
72 <td>
72 <td>
73 ${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
73 ${h.link_to(node.name,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
74 </td>
74 </td>
75 <td>
75 <td>
76 %if node.is_file():
76 %if node.is_file():
77 ${h.format_byte_size(node.size,binary=True)}
77 ${h.format_byte_size(node.size,binary=True)}
78 %endif
78 %endif
79 </td>
79 </td>
80 <td>
80 <td>
81 %if node.is_file():
81 %if node.is_file():
82 ${node.mimetype}
82 ${node.mimetype}
83 %endif
83 %endif
84 </td>
84 </td>
85 <td>
85 <td>
86 %if node.is_file():
86 %if node.is_file():
87 <span class="tooltip" title="${node.last_changeset.message}">
87 <span class="tooltip" title="${node.last_changeset.message}">
88 ${'r%s:%s' % (node.last_changeset.revision,node.last_changeset.short_id)}</span>
88 ${'r%s:%s' % (node.last_changeset.revision,node.last_changeset.short_id)}</span>
89 %endif
89 %endif
90 </td>
90 </td>
91 <td>
91 <td>
92 %if node.is_file():
92 %if node.is_file():
93 <span class="tooltip" title="${node.last_changeset.date}">
93 <span class="tooltip" title="${node.last_changeset.date}">
94 ${h.age(node.last_changeset.date)}</span>
94 ${h.age(node.last_changeset.date)}</span>
95 %endif
95 %endif
96 </td>
96 </td>
97 <td>
97 <td>
98 %if node.is_file():
98 %if node.is_file():
99 ${node.last_changeset.author}
99 <span title="${node.last_changeset.author}">
100 ${h.person(node.last_changeset.author)}
101 </span>
100 %endif
102 %endif
101 </td>
103 </td>
102 </tr>
104 </tr>
103 %endfor
105 %endfor
104 </tbody>
106 </tbody>
105 <tbody id="tbody_filtered" style="display:none">
107 <tbody id="tbody_filtered" style="display:none">
106 </tbody>
108 </tbody>
107 </table>
109 </table>
108 </div>
110 </div>
109 </div> No newline at end of file
111 </div>
@@ -1,105 +1,105 b''
1 <dl>
1 <dl>
2 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
2 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
3 <dd>
3 <dd>
4 <div>
4 <div>
5 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
5 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
6 ${h.hidden('diff2',c.file.last_changeset.raw_id)}
6 ${h.hidden('diff2',c.file.last_changeset.raw_id)}
7 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
7 ${h.select('diff1',c.file.last_changeset.raw_id,c.file_history)}
8 ${h.submit('diff','diff to revision',class_="ui-btn")}
8 ${h.submit('diff','diff to revision',class_="ui-btn")}
9 ${h.submit('show_rev','show at revision',class_="ui-btn")}
9 ${h.submit('show_rev','show at revision',class_="ui-btn")}
10 ${h.end_form()}
10 ${h.end_form()}
11 </div>
11 </div>
12 </dd>
12 </dd>
13 </dl>
13 </dl>
14
14
15
15
16 <div id="body" class="codeblock">
16 <div id="body" class="codeblock">
17 <div class="code-header">
17 <div class="code-header">
18 <div class="stats">
18 <div class="stats">
19 <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div>
19 <div class="left"><img src="${h.url('/images/icons/file.png')}"/></div>
20 <div class="left item">${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</div>
20 <div class="left item">${h.link_to("r%s:%s" % (c.file.last_changeset.revision,h.short_id(c.file.last_changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.last_changeset.raw_id))}</div>
21 <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
21 <div class="left item">${h.format_byte_size(c.file.size,binary=True)}</div>
22 <div class="left item last">${c.file.mimetype}</div>
22 <div class="left item last">${c.file.mimetype}</div>
23 <div class="buttons">
23 <div class="buttons">
24 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
24 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
25 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
25 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
26 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
26 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
27 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
27 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
28 % if not c.file.is_binary:
28 % if not c.file.is_binary:
29 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
29 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
30 % endif
30 % endif
31 % endif
31 % endif
32 </div>
32 </div>
33 </div>
33 </div>
34 <div class="author">
34 <div class="author">
35 <div class="gravatar">
35 <div class="gravatar">
36 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),16)}"/>
36 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),16)}"/>
37 </div>
37 </div>
38 <div title="${h.email_or_none(c.changeset.author)}" class="user">${h.person(c.changeset.author)}</div>
38 <div title="${c.changeset.author}" class="user">${h.person(c.changeset.author)}</div>
39 </div>
39 </div>
40 <div class="commit">${c.file.last_changeset.message}</div>
40 <div class="commit">${c.file.last_changeset.message}</div>
41 </div>
41 </div>
42 <div class="code-body">
42 <div class="code-body">
43 %if c.file.is_binary:
43 %if c.file.is_binary:
44 ${_('Binary file (%s)') % c.file.mimetype}
44 ${_('Binary file (%s)') % c.file.mimetype}
45 %else:
45 %else:
46 % if c.file.size < c.cut_off_limit:
46 % if c.file.size < c.cut_off_limit:
47 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
47 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
48 %else:
48 %else:
49 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
49 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
50 h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
50 h.url('files_raw_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path))}
51 %endif
51 %endif
52 <script type="text/javascript">
52 <script type="text/javascript">
53 function highlight_lines(lines){
53 function highlight_lines(lines){
54 for(pos in lines){
54 for(pos in lines){
55 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
55 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
56 }
56 }
57 }
57 }
58 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
58 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
59 if (page_highlights.length == 2){
59 if (page_highlights.length == 2){
60 highlight_ranges = page_highlights[1].split(",");
60 highlight_ranges = page_highlights[1].split(",");
61
61
62 var h_lines = [];
62 var h_lines = [];
63 for (pos in highlight_ranges){
63 for (pos in highlight_ranges){
64 var _range = highlight_ranges[pos].split('-');
64 var _range = highlight_ranges[pos].split('-');
65 if(_range.length == 2){
65 if(_range.length == 2){
66 var start = parseInt(_range[0]);
66 var start = parseInt(_range[0]);
67 var end = parseInt(_range[1]);
67 var end = parseInt(_range[1]);
68 if (start < end){
68 if (start < end){
69 for(var i=start;i<=end;i++){
69 for(var i=start;i<=end;i++){
70 h_lines.push(i);
70 h_lines.push(i);
71 }
71 }
72 }
72 }
73 }
73 }
74 else{
74 else{
75 h_lines.push(parseInt(highlight_ranges[pos]));
75 h_lines.push(parseInt(highlight_ranges[pos]));
76 }
76 }
77 }
77 }
78 highlight_lines(h_lines);
78 highlight_lines(h_lines);
79
79
80 //remember original location
80 //remember original location
81 var old_hash = location.href.substring(location.href.indexOf('#'));
81 var old_hash = location.href.substring(location.href.indexOf('#'));
82
82
83 // this makes a jump to anchor moved by 3 posstions for padding
83 // this makes a jump to anchor moved by 3 posstions for padding
84 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
84 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
85
85
86 //sets old anchor
86 //sets old anchor
87 window.location.hash = old_hash;
87 window.location.hash = old_hash;
88
88
89 }
89 }
90 </script>
90 </script>
91 %endif
91 %endif
92 </div>
92 </div>
93 </div>
93 </div>
94
94
95 <script type="text/javascript">
95 <script type="text/javascript">
96 YUE.onDOMReady(function(){
96 YUE.onDOMReady(function(){
97 YUE.on('show_rev','click',function(e){
97 YUE.on('show_rev','click',function(e){
98 YUE.preventDefault(e);
98 YUE.preventDefault(e);
99 var cs = YUD.get('diff1').value;
99 var cs = YUD.get('diff1').value;
100 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
100 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
101 window.location = url;
101 window.location = url;
102 });
102 });
103 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
103 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
104 });
104 });
105 </script>
105 </script>
General Comments 0
You need to be logged in to leave comments. Login now