##// END OF EJS Templates
wrap_paragraph was to slow for toolip generation and was removed.
marcink -
r1352:79041f2d beta
parent child Browse files
Show More
@@ -1,696 +1,699 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
10
11 from datetime import datetime
11 from datetime import datetime
12 from pygments.formatters import HtmlFormatter
12 from pygments.formatters import HtmlFormatter
13 from pygments import highlight as code_highlight
13 from pygments import highlight as code_highlight
14 from pylons import url, request, config
14 from pylons import url, request, config
15 from pylons.i18n.translation import _, ungettext
15 from pylons.i18n.translation import _, ungettext
16
16
17 from webhelpers.html import literal, HTML, escape
17 from webhelpers.html import literal, HTML, escape
18 from webhelpers.html.tools import *
18 from webhelpers.html.tools import *
19 from webhelpers.html.builder import make_tag
19 from webhelpers.html.builder import make_tag
20 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
20 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
21 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
21 end_form, file, form, hidden, image, javascript_link, link_to, link_to_if, \
22 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
22 link_to_unless, ol, required_legend, select, stylesheet_link, submit, text, \
23 password, textarea, title, ul, xml_declaration, radio
23 password, textarea, title, ul, xml_declaration, radio
24 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
24 from webhelpers.html.tools import auto_link, button_to, highlight, js_obfuscate, \
25 mail_to, strip_links, strip_tags, tag_re
25 mail_to, strip_links, strip_tags, tag_re
26 from webhelpers.number import format_byte_size, format_bit_size
26 from webhelpers.number import format_byte_size, format_bit_size
27 from webhelpers.pylonslib import Flash as _Flash
27 from webhelpers.pylonslib import Flash as _Flash
28 from webhelpers.pylonslib.secure_form import secure_form
28 from webhelpers.pylonslib.secure_form import secure_form
29 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
29 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
30 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
30 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
31 replace_whitespace, urlify, truncate, wrap_paragraphs
31 replace_whitespace, urlify, truncate, wrap_paragraphs
32 from webhelpers.date import time_ago_in_words
32 from webhelpers.date import time_ago_in_words
33 from webhelpers.paginate import Page
33 from webhelpers.paginate import Page
34 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
34 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
35 convert_boolean_attrs, NotGiven
35 convert_boolean_attrs, NotGiven
36
36
37 from vcs.utils.annotate import annotate_highlight
37 from vcs.utils.annotate import annotate_highlight
38 from rhodecode.lib.utils import repo_name_slug
38 from rhodecode.lib.utils import repo_name_slug
39 from rhodecode.lib import str2bool, safe_unicode
39 from rhodecode.lib import str2bool, safe_unicode
40
40
41 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
41 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
42 """
42 """
43 Reset button
43 Reset button
44 """
44 """
45 _set_input_attrs(attrs, type, name, value)
45 _set_input_attrs(attrs, type, name, value)
46 _set_id_attr(attrs, id, name)
46 _set_id_attr(attrs, id, name)
47 convert_boolean_attrs(attrs, ["disabled"])
47 convert_boolean_attrs(attrs, ["disabled"])
48 return HTML.input(**attrs)
48 return HTML.input(**attrs)
49
49
50 reset = _reset
50 reset = _reset
51
51
52
52
53 def get_token():
53 def get_token():
54 """Return the current authentication token, creating one if one doesn't
54 """Return the current authentication token, creating one if one doesn't
55 already exist.
55 already exist.
56 """
56 """
57 token_key = "_authentication_token"
57 token_key = "_authentication_token"
58 from pylons import session
58 from pylons import session
59 if not token_key in session:
59 if not token_key in session:
60 try:
60 try:
61 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
61 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
62 except AttributeError: # Python < 2.4
62 except AttributeError: # Python < 2.4
63 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
63 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
64 session[token_key] = token
64 session[token_key] = token
65 if hasattr(session, 'save'):
65 if hasattr(session, 'save'):
66 session.save()
66 session.save()
67 return session[token_key]
67 return session[token_key]
68
68
69 class _GetError(object):
69 class _GetError(object):
70 """Get error from form_errors, and represent it as span wrapped error
70 """Get error from form_errors, and represent it as span wrapped error
71 message
71 message
72
72
73 :param field_name: field to fetch errors for
73 :param field_name: field to fetch errors for
74 :param form_errors: form errors dict
74 :param form_errors: form errors dict
75 """
75 """
76
76
77 def __call__(self, field_name, form_errors):
77 def __call__(self, field_name, form_errors):
78 tmpl = """<span class="error_msg">%s</span>"""
78 tmpl = """<span class="error_msg">%s</span>"""
79 if form_errors and form_errors.has_key(field_name):
79 if form_errors and form_errors.has_key(field_name):
80 return literal(tmpl % form_errors.get(field_name))
80 return literal(tmpl % form_errors.get(field_name))
81
81
82 get_error = _GetError()
82 get_error = _GetError()
83
83
84 class _ToolTip(object):
84 class _ToolTip(object):
85
85
86 def __call__(self, tooltip_title, trim_at=50):
86 def __call__(self, tooltip_title, trim_at=50):
87 """Special function just to wrap our text into nice formatted
87 """Special function just to wrap our text into nice formatted
88 autowrapped text
88 autowrapped text
89
89
90 :param tooltip_title:
90 :param tooltip_title:
91 """
91 """
92
92
93 return wrap_paragraphs(escape(tooltip_title), trim_at)\
93 return escape(tooltip_title)
94 .replace('\n', '<br/>')
95
94
96 def activate(self):
95 def activate(self):
97 """Adds tooltip mechanism to the given Html all tooltips have to have
96 """Adds tooltip mechanism to the given Html all tooltips have to have
98 set class `tooltip` and set attribute `tooltip_title`.
97 set class `tooltip` and set attribute `tooltip_title`.
99 Then a tooltip will be generated based on that. All with yui js tooltip
98 Then a tooltip will be generated based on that. All with yui js tooltip
100 """
99 """
101
100
102 js = '''
101 js = '''
103 YAHOO.util.Event.onDOMReady(function(){
102 YAHOO.util.Event.onDOMReady(function(){
104 function toolTipsId(){
103 function toolTipsId(){
105 var ids = [];
104 var ids = [];
106 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
105 var tts = YAHOO.util.Dom.getElementsByClassName('tooltip');
107
106
108 for (var i = 0; i < tts.length; i++) {
107 for (var i = 0; i < tts.length; i++) {
109 //if element doesn't not have and id autogenerate one for tooltip
108 //if element doesn't not have and id autogenerate one for tooltip
110
109
111 if (!tts[i].id){
110 if (!tts[i].id){
112 tts[i].id='tt'+i*100;
111 tts[i].id='tt'+i*100;
113 }
112 }
114 ids.push(tts[i].id);
113 ids.push(tts[i].id);
115 }
114 }
116 return ids
115 return ids
117 };
116 };
118 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
117 var myToolTips = new YAHOO.widget.Tooltip("tooltip", {
119 context: [[toolTipsId()],"tl","bl",null,[0,5]],
118 context: [[toolTipsId()],"tl","bl",null,[0,5]],
120 monitorresize:false,
119 monitorresize:false,
121 xyoffset :[0,0],
120 xyoffset :[0,0],
122 autodismissdelay:300000,
121 autodismissdelay:300000,
123 hidedelay:5,
122 hidedelay:5,
124 showdelay:20,
123 showdelay:20,
125 });
124 });
126
125
127 });
126 });
128 '''
127 '''
129 return literal(js)
128 return literal(js)
130
129
131 tooltip = _ToolTip()
130 tooltip = _ToolTip()
132
131
133 class _FilesBreadCrumbs(object):
132 class _FilesBreadCrumbs(object):
134
133
135 def __call__(self, repo_name, rev, paths):
134 def __call__(self, repo_name, rev, paths):
136 if isinstance(paths, str):
135 if isinstance(paths, str):
137 paths = safe_unicode(paths)
136 paths = safe_unicode(paths)
138 url_l = [link_to(repo_name, url('files_home',
137 url_l = [link_to(repo_name, url('files_home',
139 repo_name=repo_name,
138 repo_name=repo_name,
140 revision=rev, f_path=''))]
139 revision=rev, f_path=''))]
141 paths_l = paths.split('/')
140 paths_l = paths.split('/')
142 for cnt, p in enumerate(paths_l):
141 for cnt, p in enumerate(paths_l):
143 if p != '':
142 if p != '':
144 url_l.append(link_to(p, url('files_home',
143 url_l.append(link_to(p, url('files_home',
145 repo_name=repo_name,
144 repo_name=repo_name,
146 revision=rev,
145 revision=rev,
147 f_path='/'.join(paths_l[:cnt + 1]))))
146 f_path='/'.join(paths_l[:cnt + 1]))))
148
147
149 return literal('/'.join(url_l))
148 return literal('/'.join(url_l))
150
149
151 files_breadcrumbs = _FilesBreadCrumbs()
150 files_breadcrumbs = _FilesBreadCrumbs()
152
151
153 class CodeHtmlFormatter(HtmlFormatter):
152 class CodeHtmlFormatter(HtmlFormatter):
154 """My code Html Formatter for source codes
153 """My code Html Formatter for source codes
155 """
154 """
156
155
157 def wrap(self, source, outfile):
156 def wrap(self, source, outfile):
158 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
157 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
159
158
160 def _wrap_code(self, source):
159 def _wrap_code(self, source):
161 for cnt, it in enumerate(source):
160 for cnt, it in enumerate(source):
162 i, t = it
161 i, t = it
163 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
162 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
164 yield i, t
163 yield i, t
165
164
166 def _wrap_tablelinenos(self, inner):
165 def _wrap_tablelinenos(self, inner):
167 dummyoutfile = StringIO.StringIO()
166 dummyoutfile = StringIO.StringIO()
168 lncount = 0
167 lncount = 0
169 for t, line in inner:
168 for t, line in inner:
170 if t:
169 if t:
171 lncount += 1
170 lncount += 1
172 dummyoutfile.write(line)
171 dummyoutfile.write(line)
173
172
174 fl = self.linenostart
173 fl = self.linenostart
175 mw = len(str(lncount + fl - 1))
174 mw = len(str(lncount + fl - 1))
176 sp = self.linenospecial
175 sp = self.linenospecial
177 st = self.linenostep
176 st = self.linenostep
178 la = self.lineanchors
177 la = self.lineanchors
179 aln = self.anchorlinenos
178 aln = self.anchorlinenos
180 nocls = self.noclasses
179 nocls = self.noclasses
181 if sp:
180 if sp:
182 lines = []
181 lines = []
183
182
184 for i in range(fl, fl + lncount):
183 for i in range(fl, fl + lncount):
185 if i % st == 0:
184 if i % st == 0:
186 if i % sp == 0:
185 if i % sp == 0:
187 if aln:
186 if aln:
188 lines.append('<a href="#%s%d" class="special">%*d</a>' %
187 lines.append('<a href="#%s%d" class="special">%*d</a>' %
189 (la, i, mw, i))
188 (la, i, mw, i))
190 else:
189 else:
191 lines.append('<span class="special">%*d</span>' % (mw, i))
190 lines.append('<span class="special">%*d</span>' % (mw, i))
192 else:
191 else:
193 if aln:
192 if aln:
194 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
193 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
195 else:
194 else:
196 lines.append('%*d' % (mw, i))
195 lines.append('%*d' % (mw, i))
197 else:
196 else:
198 lines.append('')
197 lines.append('')
199 ls = '\n'.join(lines)
198 ls = '\n'.join(lines)
200 else:
199 else:
201 lines = []
200 lines = []
202 for i in range(fl, fl + lncount):
201 for i in range(fl, fl + lncount):
203 if i % st == 0:
202 if i % st == 0:
204 if aln:
203 if aln:
205 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
204 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
206 else:
205 else:
207 lines.append('%*d' % (mw, i))
206 lines.append('%*d' % (mw, i))
208 else:
207 else:
209 lines.append('')
208 lines.append('')
210 ls = '\n'.join(lines)
209 ls = '\n'.join(lines)
211
210
212 # in case you wonder about the seemingly redundant <div> here: since the
211 # in case you wonder about the seemingly redundant <div> here: since the
213 # content in the other cell also is wrapped in a div, some browsers in
212 # content in the other cell also is wrapped in a div, some browsers in
214 # some configurations seem to mess up the formatting...
213 # some configurations seem to mess up the formatting...
215 if nocls:
214 if nocls:
216 yield 0, ('<table class="%stable">' % self.cssclass +
215 yield 0, ('<table class="%stable">' % self.cssclass +
217 '<tr><td><div class="linenodiv" '
216 '<tr><td><div class="linenodiv" '
218 'style="background-color: #f0f0f0; padding-right: 10px">'
217 'style="background-color: #f0f0f0; padding-right: 10px">'
219 '<pre style="line-height: 125%">' +
218 '<pre style="line-height: 125%">' +
220 ls + '</pre></div></td><td id="hlcode" class="code">')
219 ls + '</pre></div></td><td id="hlcode" class="code">')
221 else:
220 else:
222 yield 0, ('<table class="%stable">' % self.cssclass +
221 yield 0, ('<table class="%stable">' % self.cssclass +
223 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
222 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
224 ls + '</pre></div></td><td id="hlcode" class="code">')
223 ls + '</pre></div></td><td id="hlcode" class="code">')
225 yield 0, dummyoutfile.getvalue()
224 yield 0, dummyoutfile.getvalue()
226 yield 0, '</td></tr></table>'
225 yield 0, '</td></tr></table>'
227
226
228
227
229 def pygmentize(filenode, **kwargs):
228 def pygmentize(filenode, **kwargs):
230 """pygmentize function using pygments
229 """pygmentize function using pygments
231
230
232 :param filenode:
231 :param filenode:
233 """
232 """
234
233
235 return literal(code_highlight(filenode.content,
234 return literal(code_highlight(filenode.content,
236 filenode.lexer, CodeHtmlFormatter(**kwargs)))
235 filenode.lexer, CodeHtmlFormatter(**kwargs)))
237
236
238 def pygmentize_annotation(repo_name, filenode, **kwargs):
237 def pygmentize_annotation(repo_name, filenode, **kwargs):
239 """pygmentize function for annotation
238 """pygmentize function for annotation
240
239
241 :param filenode:
240 :param filenode:
242 """
241 """
243
242
244 color_dict = {}
243 color_dict = {}
245 def gen_color(n=10000):
244 def gen_color(n=10000):
246 """generator for getting n of evenly distributed colors using
245 """generator for getting n of evenly distributed colors using
247 hsv color and golden ratio. It always return same order of colors
246 hsv color and golden ratio. It always return same order of colors
248
247
249 :returns: RGB tuple
248 :returns: RGB tuple
250 """
249 """
251 import colorsys
250 import colorsys
252 golden_ratio = 0.618033988749895
251 golden_ratio = 0.618033988749895
253 h = 0.22717784590367374
252 h = 0.22717784590367374
254
253
255 for _ in xrange(n):
254 for _ in xrange(n):
256 h += golden_ratio
255 h += golden_ratio
257 h %= 1
256 h %= 1
258 HSV_tuple = [h, 0.95, 0.95]
257 HSV_tuple = [h, 0.95, 0.95]
259 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
258 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
260 yield map(lambda x:str(int(x * 256)), RGB_tuple)
259 yield map(lambda x:str(int(x * 256)), RGB_tuple)
261
260
262 cgenerator = gen_color()
261 cgenerator = gen_color()
263
262
264 def get_color_string(cs):
263 def get_color_string(cs):
265 if color_dict.has_key(cs):
264 if color_dict.has_key(cs):
266 col = color_dict[cs]
265 col = color_dict[cs]
267 else:
266 else:
268 col = color_dict[cs] = cgenerator.next()
267 col = color_dict[cs] = cgenerator.next()
269 return "color: rgb(%s)! important;" % (', '.join(col))
268 return "color: rgb(%s)! important;" % (', '.join(col))
270
269
271 def url_func(repo_name):
270 def url_func(repo_name):
271
272 def _url_func(changeset):
272 def _url_func(changeset):
273 tooltip_html = "<div style='font-size:0.8em'><b>Author:</b>" + \
273 author = changeset.author
274 " %s<br/><b>Date:</b> %s</b><br/><b>Message:</b> %s<br/></div>"
274 date = changeset.date
275 message = tooltip(changeset.message)
275
276
276 tooltip_html = tooltip_html % (changeset.author,
277 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
277 changeset.date,
278 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
278 tooltip(changeset.message))
279 "</b> %s<br/></div>")
280
281 tooltip_html = tooltip_html % (author, date, message)
279 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
282 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
280 short_id(changeset.raw_id))
283 short_id(changeset.raw_id))
281 uri = link_to(
284 uri = link_to(
282 lnk_format,
285 lnk_format,
283 url('changeset_home', repo_name=repo_name,
286 url('changeset_home', repo_name=repo_name,
284 revision=changeset.raw_id),
287 revision=changeset.raw_id),
285 style=get_color_string(changeset.raw_id),
288 style=get_color_string(changeset.raw_id),
286 class_='tooltip',
289 class_='tooltip',
287 title=tooltip_html
290 title=tooltip_html
288 )
291 )
289
292
290 uri += '\n'
293 uri += '\n'
291 return uri
294 return uri
292 return _url_func
295 return _url_func
293
296
294 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
297 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
295
298
296 def get_changeset_safe(repo, rev):
299 def get_changeset_safe(repo, rev):
297 from vcs.backends.base import BaseRepository
300 from vcs.backends.base import BaseRepository
298 from vcs.exceptions import RepositoryError
301 from vcs.exceptions import RepositoryError
299 if not isinstance(repo, BaseRepository):
302 if not isinstance(repo, BaseRepository):
300 raise Exception('You must pass an Repository '
303 raise Exception('You must pass an Repository '
301 'object as first argument got %s', type(repo))
304 'object as first argument got %s', type(repo))
302
305
303 try:
306 try:
304 cs = repo.get_changeset(rev)
307 cs = repo.get_changeset(rev)
305 except RepositoryError:
308 except RepositoryError:
306 from rhodecode.lib.utils import EmptyChangeset
309 from rhodecode.lib.utils import EmptyChangeset
307 cs = EmptyChangeset()
310 cs = EmptyChangeset()
308 return cs
311 return cs
309
312
310
313
311 def is_following_repo(repo_name, user_id):
314 def is_following_repo(repo_name, user_id):
312 from rhodecode.model.scm import ScmModel
315 from rhodecode.model.scm import ScmModel
313 return ScmModel().is_following_repo(repo_name, user_id)
316 return ScmModel().is_following_repo(repo_name, user_id)
314
317
315 flash = _Flash()
318 flash = _Flash()
316
319
317
320
318 #==============================================================================
321 #==============================================================================
319 # MERCURIAL FILTERS available via h.
322 # MERCURIAL FILTERS available via h.
320 #==============================================================================
323 #==============================================================================
321 from mercurial import util
324 from mercurial import util
322 from mercurial.templatefilters import person as _person
325 from mercurial.templatefilters import person as _person
323 from rhodecode.lib import credentials_hidder, age as _age
326 from rhodecode.lib import credentials_hidder, age as _age
324
327
325
328
326
329
327 age = lambda x:_age(x)
330 age = lambda x:_age(x)
328 capitalize = lambda x: x.capitalize()
331 capitalize = lambda x: x.capitalize()
329 email = util.email
332 email = util.email
330 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
333 email_or_none = lambda x: util.email(x) if util.email(x) != x else None
331 person = lambda x: _person(x)
334 person = lambda x: _person(x)
332 short_id = lambda x: x[:12]
335 short_id = lambda x: x[:12]
333 hide_credentials = lambda x: ''.join(credentials_hidder(x))
336 hide_credentials = lambda x: ''.join(credentials_hidder(x))
334
337
335 def bool2icon(value):
338 def bool2icon(value):
336 """Returns True/False values represented as small html image of true/false
339 """Returns True/False values represented as small html image of true/false
337 icons
340 icons
338
341
339 :param value: bool value
342 :param value: bool value
340 """
343 """
341
344
342 if value is True:
345 if value is True:
343 return HTML.tag('img', src=url("/images/icons/accept.png"),
346 return HTML.tag('img', src=url("/images/icons/accept.png"),
344 alt=_('True'))
347 alt=_('True'))
345
348
346 if value is False:
349 if value is False:
347 return HTML.tag('img', src=url("/images/icons/cancel.png"),
350 return HTML.tag('img', src=url("/images/icons/cancel.png"),
348 alt=_('False'))
351 alt=_('False'))
349
352
350 return value
353 return value
351
354
352
355
353 def action_parser(user_log, feed=False):
356 def action_parser(user_log, feed=False):
354 """This helper will action_map the specified string action into translated
357 """This helper will action_map the specified string action into translated
355 fancy names with icons and links
358 fancy names with icons and links
356
359
357 :param user_log: user log instance
360 :param user_log: user log instance
358 :param feed: use output for feeds (no html and fancy icons)
361 :param feed: use output for feeds (no html and fancy icons)
359 """
362 """
360
363
361 action = user_log.action
364 action = user_log.action
362 action_params = ' '
365 action_params = ' '
363
366
364 x = action.split(':')
367 x = action.split(':')
365
368
366 if len(x) > 1:
369 if len(x) > 1:
367 action, action_params = x
370 action, action_params = x
368
371
369 def get_cs_links():
372 def get_cs_links():
370 revs_limit = 5 #display this amount always
373 revs_limit = 5 #display this amount always
371 revs_top_limit = 50 #show upto this amount of changesets hidden
374 revs_top_limit = 50 #show upto this amount of changesets hidden
372 revs = action_params.split(',')
375 revs = action_params.split(',')
373 repo_name = user_log.repository.repo_name
376 repo_name = user_log.repository.repo_name
374
377
375 from rhodecode.model.scm import ScmModel
378 from rhodecode.model.scm import ScmModel
376 repo, dbrepo = ScmModel().get(repo_name, retval='repo',
379 repo, dbrepo = ScmModel().get(repo_name, retval='repo',
377 invalidation_list=[])
380 invalidation_list=[])
378
381
379 message = lambda rev: get_changeset_safe(repo, rev).message
382 message = lambda rev: get_changeset_safe(repo, rev).message
380
383
381 cs_links = " " + ', '.join ([link_to(rev,
384 cs_links = " " + ', '.join ([link_to(rev,
382 url('changeset_home',
385 url('changeset_home',
383 repo_name=repo_name,
386 repo_name=repo_name,
384 revision=rev), title=tooltip(message(rev)),
387 revision=rev), title=tooltip(message(rev)),
385 class_='tooltip') for rev in revs[:revs_limit] ])
388 class_='tooltip') for rev in revs[:revs_limit] ])
386
389
387 compare_view = (' <div class="compare_view tooltip" title="%s">'
390 compare_view = (' <div class="compare_view tooltip" title="%s">'
388 '<a href="%s">%s</a> '
391 '<a href="%s">%s</a> '
389 '</div>' % (_('Show all combined changesets %s->%s' \
392 '</div>' % (_('Show all combined changesets %s->%s' \
390 % (revs[0], revs[-1])),
393 % (revs[0], revs[-1])),
391 url('changeset_home', repo_name=repo_name,
394 url('changeset_home', repo_name=repo_name,
392 revision='%s...%s' % (revs[0], revs[-1])
395 revision='%s...%s' % (revs[0], revs[-1])
393 ),
396 ),
394 _('compare view'))
397 _('compare view'))
395 )
398 )
396
399
397 if len(revs) > revs_limit:
400 if len(revs) > revs_limit:
398 uniq_id = revs[0]
401 uniq_id = revs[0]
399 html_tmpl = ('<span> %s '
402 html_tmpl = ('<span> %s '
400 '<a class="show_more" id="_%s" href="#more">%s</a> '
403 '<a class="show_more" id="_%s" href="#more">%s</a> '
401 '%s</span>')
404 '%s</span>')
402 if not feed:
405 if not feed:
403 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
406 cs_links += html_tmpl % (_('and'), uniq_id, _('%s more') \
404 % (len(revs) - revs_limit),
407 % (len(revs) - revs_limit),
405 _('revisions'))
408 _('revisions'))
406
409
407 if not feed:
410 if not feed:
408 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
411 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
409 else:
412 else:
410 html_tmpl = '<span id="%s"> %s </span>'
413 html_tmpl = '<span id="%s"> %s </span>'
411
414
412 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
415 cs_links += html_tmpl % (uniq_id, ', '.join([link_to(rev,
413 url('changeset_home',
416 url('changeset_home',
414 repo_name=repo_name, revision=rev),
417 repo_name=repo_name, revision=rev),
415 title=message(rev), class_='tooltip')
418 title=message(rev), class_='tooltip')
416 for rev in revs[revs_limit:revs_top_limit]]))
419 for rev in revs[revs_limit:revs_top_limit]]))
417 if len(revs) > 1:
420 if len(revs) > 1:
418 cs_links += compare_view
421 cs_links += compare_view
419 return cs_links
422 return cs_links
420
423
421 def get_fork_name():
424 def get_fork_name():
422 repo_name = action_params
425 repo_name = action_params
423 return _('fork name ') + str(link_to(action_params, url('summary_home',
426 return _('fork name ') + str(link_to(action_params, url('summary_home',
424 repo_name=repo_name,)))
427 repo_name=repo_name,)))
425
428
426 action_map = {'user_deleted_repo':(_('[deleted] repository'), None),
429 action_map = {'user_deleted_repo':(_('[deleted] repository'), None),
427 'user_created_repo':(_('[created] repository'), None),
430 'user_created_repo':(_('[created] repository'), None),
428 'user_forked_repo':(_('[forked] repository'), get_fork_name),
431 'user_forked_repo':(_('[forked] repository'), get_fork_name),
429 'user_updated_repo':(_('[updated] repository'), None),
432 'user_updated_repo':(_('[updated] repository'), None),
430 'admin_deleted_repo':(_('[delete] repository'), None),
433 'admin_deleted_repo':(_('[delete] repository'), None),
431 'admin_created_repo':(_('[created] repository'), None),
434 'admin_created_repo':(_('[created] repository'), None),
432 'admin_forked_repo':(_('[forked] repository'), None),
435 'admin_forked_repo':(_('[forked] repository'), None),
433 'admin_updated_repo':(_('[updated] repository'), None),
436 'admin_updated_repo':(_('[updated] repository'), None),
434 'push':(_('[pushed] into'), get_cs_links),
437 'push':(_('[pushed] into'), get_cs_links),
435 'push_local':(_('[committed via RhodeCode] into'), get_cs_links),
438 'push_local':(_('[committed via RhodeCode] into'), get_cs_links),
436 'push_remote':(_('[pulled from remote] into'), get_cs_links),
439 'push_remote':(_('[pulled from remote] into'), get_cs_links),
437 'pull':(_('[pulled] from'), None),
440 'pull':(_('[pulled] from'), None),
438 'started_following_repo':(_('[started following] repository'), None),
441 'started_following_repo':(_('[started following] repository'), None),
439 'stopped_following_repo':(_('[stopped following] repository'), None),
442 'stopped_following_repo':(_('[stopped following] repository'), None),
440 }
443 }
441
444
442 action_str = action_map.get(action, action)
445 action_str = action_map.get(action, action)
443 if feed:
446 if feed:
444 action = action_str[0].replace('[', '').replace(']', '')
447 action = action_str[0].replace('[', '').replace(']', '')
445 else:
448 else:
446 action = action_str[0].replace('[', '<span class="journal_highlight">')\
449 action = action_str[0].replace('[', '<span class="journal_highlight">')\
447 .replace(']', '</span>')
450 .replace(']', '</span>')
448
451
449 action_params_func = lambda :""
452 action_params_func = lambda :""
450
453
451 if callable(action_str[1]):
454 if callable(action_str[1]):
452 action_params_func = action_str[1]
455 action_params_func = action_str[1]
453
456
454 return [literal(action), action_params_func]
457 return [literal(action), action_params_func]
455
458
456 def action_parser_icon(user_log):
459 def action_parser_icon(user_log):
457 action = user_log.action
460 action = user_log.action
458 action_params = None
461 action_params = None
459 x = action.split(':')
462 x = action.split(':')
460
463
461 if len(x) > 1:
464 if len(x) > 1:
462 action, action_params = x
465 action, action_params = x
463
466
464 tmpl = """<img src="%s%s" alt="%s"/>"""
467 tmpl = """<img src="%s%s" alt="%s"/>"""
465 map = {'user_deleted_repo':'database_delete.png',
468 map = {'user_deleted_repo':'database_delete.png',
466 'user_created_repo':'database_add.png',
469 'user_created_repo':'database_add.png',
467 'user_forked_repo':'arrow_divide.png',
470 'user_forked_repo':'arrow_divide.png',
468 'user_updated_repo':'database_edit.png',
471 'user_updated_repo':'database_edit.png',
469 'admin_deleted_repo':'database_delete.png',
472 'admin_deleted_repo':'database_delete.png',
470 'admin_created_repo':'database_add.png',
473 'admin_created_repo':'database_add.png',
471 'admin_forked_repo':'arrow_divide.png',
474 'admin_forked_repo':'arrow_divide.png',
472 'admin_updated_repo':'database_edit.png',
475 'admin_updated_repo':'database_edit.png',
473 'push':'script_add.png',
476 'push':'script_add.png',
474 'push_local':'script_edit.png',
477 'push_local':'script_edit.png',
475 'push_remote':'connect.png',
478 'push_remote':'connect.png',
476 'pull':'down_16.png',
479 'pull':'down_16.png',
477 'started_following_repo':'heart_add.png',
480 'started_following_repo':'heart_add.png',
478 'stopped_following_repo':'heart_delete.png',
481 'stopped_following_repo':'heart_delete.png',
479 }
482 }
480 return literal(tmpl % ((url('/images/icons/')),
483 return literal(tmpl % ((url('/images/icons/')),
481 map.get(action, action), action))
484 map.get(action, action), action))
482
485
483
486
484 #==============================================================================
487 #==============================================================================
485 # PERMS
488 # PERMS
486 #==============================================================================
489 #==============================================================================
487 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
490 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
488 HasRepoPermissionAny, HasRepoPermissionAll
491 HasRepoPermissionAny, HasRepoPermissionAll
489
492
490 #==============================================================================
493 #==============================================================================
491 # GRAVATAR URL
494 # GRAVATAR URL
492 #==============================================================================
495 #==============================================================================
493
496
494 def gravatar_url(email_address, size=30):
497 def gravatar_url(email_address, size=30):
495 if not str2bool(config['app_conf'].get('use_gravatar')) or \
498 if not str2bool(config['app_conf'].get('use_gravatar')) or \
496 email_address == 'anonymous@rhodecode.org':
499 email_address == 'anonymous@rhodecode.org':
497 return "/images/user%s.png" % size
500 return "/images/user%s.png" % size
498
501
499 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
502 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
500 default = 'identicon'
503 default = 'identicon'
501 baseurl_nossl = "http://www.gravatar.com/avatar/"
504 baseurl_nossl = "http://www.gravatar.com/avatar/"
502 baseurl_ssl = "https://secure.gravatar.com/avatar/"
505 baseurl_ssl = "https://secure.gravatar.com/avatar/"
503 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
506 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
504
507
505 if isinstance(email_address, unicode):
508 if isinstance(email_address, unicode):
506 #hashlib crashes on unicode items
509 #hashlib crashes on unicode items
507 email_address = email_address.encode('utf8', 'replace')
510 email_address = email_address.encode('utf8', 'replace')
508 # construct the url
511 # construct the url
509 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
512 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
510 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
513 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
511
514
512 return gravatar_url
515 return gravatar_url
513
516
514
517
515 #==============================================================================
518 #==============================================================================
516 # REPO PAGER, PAGER FOR REPOSITORY
519 # REPO PAGER, PAGER FOR REPOSITORY
517 #==============================================================================
520 #==============================================================================
518 class RepoPage(Page):
521 class RepoPage(Page):
519
522
520 def __init__(self, collection, page=1, items_per_page=20,
523 def __init__(self, collection, page=1, items_per_page=20,
521 item_count=None, url=None, branch_name=None, **kwargs):
524 item_count=None, url=None, branch_name=None, **kwargs):
522
525
523 """Create a "RepoPage" instance. special pager for paging
526 """Create a "RepoPage" instance. special pager for paging
524 repository
527 repository
525 """
528 """
526 self._url_generator = url
529 self._url_generator = url
527
530
528 # Safe the kwargs class-wide so they can be used in the pager() method
531 # Safe the kwargs class-wide so they can be used in the pager() method
529 self.kwargs = kwargs
532 self.kwargs = kwargs
530
533
531 # Save a reference to the collection
534 # Save a reference to the collection
532 self.original_collection = collection
535 self.original_collection = collection
533
536
534 self.collection = collection
537 self.collection = collection
535
538
536 # The self.page is the number of the current page.
539 # The self.page is the number of the current page.
537 # The first page has the number 1!
540 # The first page has the number 1!
538 try:
541 try:
539 self.page = int(page) # make it int() if we get it as a string
542 self.page = int(page) # make it int() if we get it as a string
540 except (ValueError, TypeError):
543 except (ValueError, TypeError):
541 self.page = 1
544 self.page = 1
542
545
543 self.items_per_page = items_per_page
546 self.items_per_page = items_per_page
544
547
545 # Unless the user tells us how many items the collections has
548 # Unless the user tells us how many items the collections has
546 # we calculate that ourselves.
549 # we calculate that ourselves.
547 if item_count is not None:
550 if item_count is not None:
548 self.item_count = item_count
551 self.item_count = item_count
549 else:
552 else:
550 self.item_count = len(self.collection)
553 self.item_count = len(self.collection)
551
554
552 # Compute the number of the first and last available page
555 # Compute the number of the first and last available page
553 if self.item_count > 0:
556 if self.item_count > 0:
554 self.first_page = 1
557 self.first_page = 1
555 self.page_count = ((self.item_count - 1) / self.items_per_page) + 1
558 self.page_count = ((self.item_count - 1) / self.items_per_page) + 1
556 self.last_page = self.first_page + self.page_count - 1
559 self.last_page = self.first_page + self.page_count - 1
557
560
558 # Make sure that the requested page number is the range of valid pages
561 # Make sure that the requested page number is the range of valid pages
559 if self.page > self.last_page:
562 if self.page > self.last_page:
560 self.page = self.last_page
563 self.page = self.last_page
561 elif self.page < self.first_page:
564 elif self.page < self.first_page:
562 self.page = self.first_page
565 self.page = self.first_page
563
566
564 # Note: the number of items on this page can be less than
567 # Note: the number of items on this page can be less than
565 # items_per_page if the last page is not full
568 # items_per_page if the last page is not full
566 self.first_item = max(0, (self.item_count) - (self.page * items_per_page))
569 self.first_item = max(0, (self.item_count) - (self.page * items_per_page))
567 self.last_item = ((self.item_count - 1) - items_per_page * (self.page - 1))
570 self.last_item = ((self.item_count - 1) - items_per_page * (self.page - 1))
568
571
569 iterator = self.collection.get_changesets(start=self.first_item,
572 iterator = self.collection.get_changesets(start=self.first_item,
570 end=self.last_item,
573 end=self.last_item,
571 reverse=True,
574 reverse=True,
572 branch_name=branch_name)
575 branch_name=branch_name)
573 self.items = list(iterator)
576 self.items = list(iterator)
574
577
575 # Links to previous and next page
578 # Links to previous and next page
576 if self.page > self.first_page:
579 if self.page > self.first_page:
577 self.previous_page = self.page - 1
580 self.previous_page = self.page - 1
578 else:
581 else:
579 self.previous_page = None
582 self.previous_page = None
580
583
581 if self.page < self.last_page:
584 if self.page < self.last_page:
582 self.next_page = self.page + 1
585 self.next_page = self.page + 1
583 else:
586 else:
584 self.next_page = None
587 self.next_page = None
585
588
586 # No items available
589 # No items available
587 else:
590 else:
588 self.first_page = None
591 self.first_page = None
589 self.page_count = 0
592 self.page_count = 0
590 self.last_page = None
593 self.last_page = None
591 self.first_item = None
594 self.first_item = None
592 self.last_item = None
595 self.last_item = None
593 self.previous_page = None
596 self.previous_page = None
594 self.next_page = None
597 self.next_page = None
595 self.items = []
598 self.items = []
596
599
597 # This is a subclass of the 'list' type. Initialise the list now.
600 # This is a subclass of the 'list' type. Initialise the list now.
598 list.__init__(self, self.items)
601 list.__init__(self, self.items)
599
602
600
603
601 def changed_tooltip(nodes):
604 def changed_tooltip(nodes):
602 """
605 """
603 Generates a html string for changed nodes in changeset page.
606 Generates a html string for changed nodes in changeset page.
604 It limits the output to 30 entries
607 It limits the output to 30 entries
605
608
606 :param nodes: LazyNodesGenerator
609 :param nodes: LazyNodesGenerator
607 """
610 """
608 if nodes:
611 if nodes:
609 pref = ': <br/> '
612 pref = ': <br/> '
610 suf = ''
613 suf = ''
611 if len(nodes) > 30:
614 if len(nodes) > 30:
612 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
615 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
613 return literal(pref + '<br/> '.join([safe_unicode(x.path)
616 return literal(pref + '<br/> '.join([safe_unicode(x.path)
614 for x in nodes[:30]]) + suf)
617 for x in nodes[:30]]) + suf)
615 else:
618 else:
616 return ': ' + _('No Files')
619 return ': ' + _('No Files')
617
620
618
621
619
622
620 def repo_link(groups_and_repos):
623 def repo_link(groups_and_repos):
621 """
624 """
622 Makes a breadcrumbs link to repo within a group
625 Makes a breadcrumbs link to repo within a group
623 joins &raquo; on each group to create a fancy link
626 joins &raquo; on each group to create a fancy link
624
627
625 ex::
628 ex::
626 group >> subgroup >> repo
629 group >> subgroup >> repo
627
630
628 :param groups_and_repos:
631 :param groups_and_repos:
629 """
632 """
630 groups, repo_name = groups_and_repos
633 groups, repo_name = groups_and_repos
631
634
632 if not groups:
635 if not groups:
633 return repo_name
636 return repo_name
634 else:
637 else:
635 def make_link(group):
638 def make_link(group):
636 return link_to(group.group_name, url('repos_group',
639 return link_to(group.group_name, url('repos_group',
637 id=group.group_id))
640 id=group.group_id))
638 return literal(' &raquo; '.join(map(make_link, groups)) + \
641 return literal(' &raquo; '.join(map(make_link, groups)) + \
639 " &raquo; " + repo_name)
642 " &raquo; " + repo_name)
640
643
641
644
642 def fancy_file_stats(stats):
645 def fancy_file_stats(stats):
643 """
646 """
644 Displays a fancy two colored bar for number of added/deleted
647 Displays a fancy two colored bar for number of added/deleted
645 lines of code on file
648 lines of code on file
646
649
647 :param stats: two element list of added/deleted lines of code
650 :param stats: two element list of added/deleted lines of code
648 """
651 """
649
652
650 a, d, t = stats[0], stats[1], stats[0] + stats[1]
653 a, d, t = stats[0], stats[1], stats[0] + stats[1]
651 width = 100
654 width = 100
652 unit = float(width) / (t or 1)
655 unit = float(width) / (t or 1)
653
656
654 # needs > 9% of width to be visible or 0 to be hidden
657 # needs > 9% of width to be visible or 0 to be hidden
655 a_p = max(9, unit * a) if a > 0 else 0
658 a_p = max(9, unit * a) if a > 0 else 0
656 d_p = max(9, unit * d) if d > 0 else 0
659 d_p = max(9, unit * d) if d > 0 else 0
657 p_sum = a_p + d_p
660 p_sum = a_p + d_p
658
661
659 if p_sum > width:
662 if p_sum > width:
660 #adjust the percentage to be == 100% since we adjusted to 9
663 #adjust the percentage to be == 100% since we adjusted to 9
661 if a_p > d_p:
664 if a_p > d_p:
662 a_p = a_p - (p_sum - width)
665 a_p = a_p - (p_sum - width)
663 else:
666 else:
664 d_p = d_p - (p_sum - width)
667 d_p = d_p - (p_sum - width)
665
668
666 a_v = a if a > 0 else ''
669 a_v = a if a > 0 else ''
667 d_v = d if d > 0 else ''
670 d_v = d if d > 0 else ''
668
671
669
672
670 def cgen(l_type):
673 def cgen(l_type):
671 mapping = {'tr':'top-right-rounded-corner',
674 mapping = {'tr':'top-right-rounded-corner',
672 'tl':'top-left-rounded-corner',
675 'tl':'top-left-rounded-corner',
673 'br':'bottom-right-rounded-corner',
676 'br':'bottom-right-rounded-corner',
674 'bl':'bottom-left-rounded-corner'}
677 'bl':'bottom-left-rounded-corner'}
675 map_getter = lambda x:mapping[x]
678 map_getter = lambda x:mapping[x]
676
679
677 if l_type == 'a' and d_v:
680 if l_type == 'a' and d_v:
678 #case when added and deleted are present
681 #case when added and deleted are present
679 return ' '.join(map(map_getter, ['tl', 'bl']))
682 return ' '.join(map(map_getter, ['tl', 'bl']))
680
683
681 if l_type == 'a' and not d_v:
684 if l_type == 'a' and not d_v:
682 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
685 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
683
686
684 if l_type == 'd' and a_v:
687 if l_type == 'd' and a_v:
685 return ' '.join(map(map_getter, ['tr', 'br']))
688 return ' '.join(map(map_getter, ['tr', 'br']))
686
689
687 if l_type == 'd' and not a_v:
690 if l_type == 'd' and not a_v:
688 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
691 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
689
692
690
693
691
694
692 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
695 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
693 a_p, a_v)
696 a_p, a_v)
694 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
697 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
695 d_p, d_v)
698 d_p, d_v)
696 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
699 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
General Comments 0
You need to be logged in to leave comments. Login now