##// END OF EJS Templates
Links in summary page are now active
marcink -
r1438:002f4f46 beta
parent child Browse files
Show More
@@ -1,661 +1,673 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 import HtmlFormatter
13 from pygments.formatters 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
36 convert_boolean_attrs, NotGiven
37
37
38 from vcs.utils.annotate import annotate_highlight
38 from vcs.utils.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
40 from rhodecode.lib import str2bool, safe_unicode, safe_str
41
41
42 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
42 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
43 """
43 """
44 Reset button
44 Reset button
45 """
45 """
46 _set_input_attrs(attrs, type, name, value)
46 _set_input_attrs(attrs, type, name, value)
47 _set_id_attr(attrs, id, name)
47 _set_id_attr(attrs, id, name)
48 convert_boolean_attrs(attrs, ["disabled"])
48 convert_boolean_attrs(attrs, ["disabled"])
49 return HTML.input(**attrs)
49 return HTML.input(**attrs)
50
50
51 reset = _reset
51 reset = _reset
52
52
53
53
54 def get_token():
54 def get_token():
55 """Return the current authentication token, creating one if one doesn't
55 """Return the current authentication token, creating one if one doesn't
56 already exist.
56 already exist.
57 """
57 """
58 token_key = "_authentication_token"
58 token_key = "_authentication_token"
59 from pylons import session
59 from pylons import session
60 if not token_key in session:
60 if not token_key in session:
61 try:
61 try:
62 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
62 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
63 except AttributeError: # Python < 2.4
63 except AttributeError: # Python < 2.4
64 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
64 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
65 session[token_key] = token
65 session[token_key] = token
66 if hasattr(session, 'save'):
66 if hasattr(session, 'save'):
67 session.save()
67 session.save()
68 return session[token_key]
68 return session[token_key]
69
69
70 class _GetError(object):
70 class _GetError(object):
71 """Get error from form_errors, and represent it as span wrapped error
71 """Get error from form_errors, and represent it as span wrapped error
72 message
72 message
73
73
74 :param field_name: field to fetch errors for
74 :param field_name: field to fetch errors for
75 :param form_errors: form errors dict
75 :param form_errors: form errors dict
76 """
76 """
77
77
78 def __call__(self, field_name, form_errors):
78 def __call__(self, field_name, form_errors):
79 tmpl = """<span class="error_msg">%s</span>"""
79 tmpl = """<span class="error_msg">%s</span>"""
80 if form_errors and form_errors.has_key(field_name):
80 if form_errors and form_errors.has_key(field_name):
81 return literal(tmpl % form_errors.get(field_name))
81 return literal(tmpl % form_errors.get(field_name))
82
82
83 get_error = _GetError()
83 get_error = _GetError()
84
84
85 class _ToolTip(object):
85 class _ToolTip(object):
86
86
87 def __call__(self, tooltip_title, trim_at=50):
87 def __call__(self, tooltip_title, trim_at=50):
88 """Special function just to wrap our text into nice formatted
88 """Special function just to wrap our text into nice formatted
89 autowrapped text
89 autowrapped text
90
90
91 :param tooltip_title:
91 :param tooltip_title:
92 """
92 """
93 return escape(tooltip_title)
93 return escape(tooltip_title)
94 tooltip = _ToolTip()
94 tooltip = _ToolTip()
95
95
96 class _FilesBreadCrumbs(object):
96 class _FilesBreadCrumbs(object):
97
97
98 def __call__(self, repo_name, rev, paths):
98 def __call__(self, repo_name, rev, paths):
99 if isinstance(paths, str):
99 if isinstance(paths, str):
100 paths = safe_unicode(paths)
100 paths = safe_unicode(paths)
101 url_l = [link_to(repo_name, url('files_home',
101 url_l = [link_to(repo_name, url('files_home',
102 repo_name=repo_name,
102 repo_name=repo_name,
103 revision=rev, f_path=''))]
103 revision=rev, f_path=''))]
104 paths_l = paths.split('/')
104 paths_l = paths.split('/')
105 for cnt, p in enumerate(paths_l):
105 for cnt, p in enumerate(paths_l):
106 if p != '':
106 if p != '':
107 url_l.append(link_to(p, url('files_home',
107 url_l.append(link_to(p, url('files_home',
108 repo_name=repo_name,
108 repo_name=repo_name,
109 revision=rev,
109 revision=rev,
110 f_path='/'.join(paths_l[:cnt + 1]))))
110 f_path='/'.join(paths_l[:cnt + 1]))))
111
111
112 return literal('/'.join(url_l))
112 return literal('/'.join(url_l))
113
113
114 files_breadcrumbs = _FilesBreadCrumbs()
114 files_breadcrumbs = _FilesBreadCrumbs()
115
115
116 class CodeHtmlFormatter(HtmlFormatter):
116 class CodeHtmlFormatter(HtmlFormatter):
117 """My code Html Formatter for source codes
117 """My code Html Formatter for source codes
118 """
118 """
119
119
120 def wrap(self, source, outfile):
120 def wrap(self, source, outfile):
121 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
121 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
122
122
123 def _wrap_code(self, source):
123 def _wrap_code(self, source):
124 for cnt, it in enumerate(source):
124 for cnt, it in enumerate(source):
125 i, t = it
125 i, t = it
126 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
126 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
127 yield i, t
127 yield i, t
128
128
129 def _wrap_tablelinenos(self, inner):
129 def _wrap_tablelinenos(self, inner):
130 dummyoutfile = StringIO.StringIO()
130 dummyoutfile = StringIO.StringIO()
131 lncount = 0
131 lncount = 0
132 for t, line in inner:
132 for t, line in inner:
133 if t:
133 if t:
134 lncount += 1
134 lncount += 1
135 dummyoutfile.write(line)
135 dummyoutfile.write(line)
136
136
137 fl = self.linenostart
137 fl = self.linenostart
138 mw = len(str(lncount + fl - 1))
138 mw = len(str(lncount + fl - 1))
139 sp = self.linenospecial
139 sp = self.linenospecial
140 st = self.linenostep
140 st = self.linenostep
141 la = self.lineanchors
141 la = self.lineanchors
142 aln = self.anchorlinenos
142 aln = self.anchorlinenos
143 nocls = self.noclasses
143 nocls = self.noclasses
144 if sp:
144 if sp:
145 lines = []
145 lines = []
146
146
147 for i in range(fl, fl + lncount):
147 for i in range(fl, fl + lncount):
148 if i % st == 0:
148 if i % st == 0:
149 if i % sp == 0:
149 if i % sp == 0:
150 if aln:
150 if aln:
151 lines.append('<a href="#%s%d" class="special">%*d</a>' %
151 lines.append('<a href="#%s%d" class="special">%*d</a>' %
152 (la, i, mw, i))
152 (la, i, mw, i))
153 else:
153 else:
154 lines.append('<span class="special">%*d</span>' % (mw, i))
154 lines.append('<span class="special">%*d</span>' % (mw, i))
155 else:
155 else:
156 if aln:
156 if aln:
157 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
157 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
158 else:
158 else:
159 lines.append('%*d' % (mw, i))
159 lines.append('%*d' % (mw, i))
160 else:
160 else:
161 lines.append('')
161 lines.append('')
162 ls = '\n'.join(lines)
162 ls = '\n'.join(lines)
163 else:
163 else:
164 lines = []
164 lines = []
165 for i in range(fl, fl + lncount):
165 for i in range(fl, fl + lncount):
166 if i % st == 0:
166 if i % st == 0:
167 if aln:
167 if aln:
168 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
168 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
169 else:
169 else:
170 lines.append('%*d' % (mw, i))
170 lines.append('%*d' % (mw, i))
171 else:
171 else:
172 lines.append('')
172 lines.append('')
173 ls = '\n'.join(lines)
173 ls = '\n'.join(lines)
174
174
175 # in case you wonder about the seemingly redundant <div> here: since the
175 # in case you wonder about the seemingly redundant <div> here: since the
176 # content in the other cell also is wrapped in a div, some browsers in
176 # content in the other cell also is wrapped in a div, some browsers in
177 # some configurations seem to mess up the formatting...
177 # some configurations seem to mess up the formatting...
178 if nocls:
178 if nocls:
179 yield 0, ('<table class="%stable">' % self.cssclass +
179 yield 0, ('<table class="%stable">' % self.cssclass +
180 '<tr><td><div class="linenodiv" '
180 '<tr><td><div class="linenodiv" '
181 'style="background-color: #f0f0f0; padding-right: 10px">'
181 'style="background-color: #f0f0f0; padding-right: 10px">'
182 '<pre style="line-height: 125%">' +
182 '<pre style="line-height: 125%">' +
183 ls + '</pre></div></td><td id="hlcode" class="code">')
183 ls + '</pre></div></td><td id="hlcode" class="code">')
184 else:
184 else:
185 yield 0, ('<table class="%stable">' % self.cssclass +
185 yield 0, ('<table class="%stable">' % self.cssclass +
186 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
186 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
187 ls + '</pre></div></td><td id="hlcode" class="code">')
187 ls + '</pre></div></td><td id="hlcode" class="code">')
188 yield 0, dummyoutfile.getvalue()
188 yield 0, dummyoutfile.getvalue()
189 yield 0, '</td></tr></table>'
189 yield 0, '</td></tr></table>'
190
190
191
191
192 def pygmentize(filenode, **kwargs):
192 def pygmentize(filenode, **kwargs):
193 """pygmentize function using pygments
193 """pygmentize function using pygments
194
194
195 :param filenode:
195 :param filenode:
196 """
196 """
197
197
198 return literal(code_highlight(filenode.content,
198 return literal(code_highlight(filenode.content,
199 filenode.lexer, CodeHtmlFormatter(**kwargs)))
199 filenode.lexer, CodeHtmlFormatter(**kwargs)))
200
200
201 def pygmentize_annotation(repo_name, filenode, **kwargs):
201 def pygmentize_annotation(repo_name, filenode, **kwargs):
202 """pygmentize function for annotation
202 """pygmentize function for annotation
203
203
204 :param filenode:
204 :param filenode:
205 """
205 """
206
206
207 color_dict = {}
207 color_dict = {}
208 def gen_color(n=10000):
208 def gen_color(n=10000):
209 """generator for getting n of evenly distributed colors using
209 """generator for getting n of evenly distributed colors using
210 hsv color and golden ratio. It always return same order of colors
210 hsv color and golden ratio. It always return same order of colors
211
211
212 :returns: RGB tuple
212 :returns: RGB tuple
213 """
213 """
214 import colorsys
214 import colorsys
215 golden_ratio = 0.618033988749895
215 golden_ratio = 0.618033988749895
216 h = 0.22717784590367374
216 h = 0.22717784590367374
217
217
218 for _ in xrange(n):
218 for _ in xrange(n):
219 h += golden_ratio
219 h += golden_ratio
220 h %= 1
220 h %= 1
221 HSV_tuple = [h, 0.95, 0.95]
221 HSV_tuple = [h, 0.95, 0.95]
222 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
222 RGB_tuple = colorsys.hsv_to_rgb(*HSV_tuple)
223 yield map(lambda x:str(int(x * 256)), RGB_tuple)
223 yield map(lambda x:str(int(x * 256)), RGB_tuple)
224
224
225 cgenerator = gen_color()
225 cgenerator = gen_color()
226
226
227 def get_color_string(cs):
227 def get_color_string(cs):
228 if color_dict.has_key(cs):
228 if color_dict.has_key(cs):
229 col = color_dict[cs]
229 col = color_dict[cs]
230 else:
230 else:
231 col = color_dict[cs] = cgenerator.next()
231 col = color_dict[cs] = cgenerator.next()
232 return "color: rgb(%s)! important;" % (', '.join(col))
232 return "color: rgb(%s)! important;" % (', '.join(col))
233
233
234 def url_func(repo_name):
234 def url_func(repo_name):
235
235
236 def _url_func(changeset):
236 def _url_func(changeset):
237 author = changeset.author
237 author = changeset.author
238 date = changeset.date
238 date = changeset.date
239 message = tooltip(changeset.message)
239 message = tooltip(changeset.message)
240
240
241 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
241 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
242 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
242 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
243 "</b> %s<br/></div>")
243 "</b> %s<br/></div>")
244
244
245 tooltip_html = tooltip_html % (author, date, message)
245 tooltip_html = tooltip_html % (author, date, message)
246 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
246 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
247 short_id(changeset.raw_id))
247 short_id(changeset.raw_id))
248 uri = link_to(
248 uri = link_to(
249 lnk_format,
249 lnk_format,
250 url('changeset_home', repo_name=repo_name,
250 url('changeset_home', repo_name=repo_name,
251 revision=changeset.raw_id),
251 revision=changeset.raw_id),
252 style=get_color_string(changeset.raw_id),
252 style=get_color_string(changeset.raw_id),
253 class_='tooltip',
253 class_='tooltip',
254 title=tooltip_html
254 title=tooltip_html
255 )
255 )
256
256
257 uri += '\n'
257 uri += '\n'
258 return uri
258 return uri
259 return _url_func
259 return _url_func
260
260
261 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
261 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
262
262
263 def get_changeset_safe(repo, rev):
263 def get_changeset_safe(repo, rev):
264 from vcs.backends.base import BaseRepository
264 from vcs.backends.base import BaseRepository
265 from vcs.exceptions import RepositoryError
265 from vcs.exceptions import RepositoryError
266 if not isinstance(repo, BaseRepository):
266 if not isinstance(repo, BaseRepository):
267 raise Exception('You must pass an Repository '
267 raise Exception('You must pass an Repository '
268 'object as first argument got %s', type(repo))
268 'object as first argument got %s', type(repo))
269
269
270 try:
270 try:
271 cs = repo.get_changeset(rev)
271 cs = repo.get_changeset(rev)
272 except RepositoryError:
272 except RepositoryError:
273 from rhodecode.lib.utils import EmptyChangeset
273 from rhodecode.lib.utils import EmptyChangeset
274 cs = EmptyChangeset()
274 cs = EmptyChangeset()
275 return cs
275 return cs
276
276
277
277
278 def is_following_repo(repo_name, user_id):
278 def is_following_repo(repo_name, user_id):
279 from rhodecode.model.scm import ScmModel
279 from rhodecode.model.scm import ScmModel
280 return ScmModel().is_following_repo(repo_name, user_id)
280 return ScmModel().is_following_repo(repo_name, user_id)
281
281
282 flash = _Flash()
282 flash = _Flash()
283
283
284 #==============================================================================
284 #==============================================================================
285 # SCM FILTERS available via h.
285 # SCM FILTERS available via h.
286 #==============================================================================
286 #==============================================================================
287 from vcs.utils import author_name, author_email
287 from vcs.utils import author_name, author_email
288 from rhodecode.lib import credentials_filter, age as _age
288 from rhodecode.lib import credentials_filter, age as _age
289
289
290 age = lambda x:_age(x)
290 age = lambda x:_age(x)
291 capitalize = lambda x: x.capitalize()
291 capitalize = lambda x: x.capitalize()
292 email = author_email
292 email = author_email
293 email_or_none = lambda x: email(x) if email(x) != x else None
293 email_or_none = lambda x: email(x) if email(x) != x else None
294 person = lambda x: author_name(x)
294 person = lambda x: author_name(x)
295 short_id = lambda x: x[:12]
295 short_id = lambda x: x[:12]
296 hide_credentials = lambda x: ''.join(credentials_filter(x))
296 hide_credentials = lambda x: ''.join(credentials_filter(x))
297
297
298 def bool2icon(value):
298 def bool2icon(value):
299 """Returns True/False values represented as small html image of true/false
299 """Returns True/False values represented as small html image of true/false
300 icons
300 icons
301
301
302 :param value: bool value
302 :param value: bool value
303 """
303 """
304
304
305 if value is True:
305 if value is True:
306 return HTML.tag('img', src=url("/images/icons/accept.png"),
306 return HTML.tag('img', src=url("/images/icons/accept.png"),
307 alt=_('True'))
307 alt=_('True'))
308
308
309 if value is False:
309 if value is False:
310 return HTML.tag('img', src=url("/images/icons/cancel.png"),
310 return HTML.tag('img', src=url("/images/icons/cancel.png"),
311 alt=_('False'))
311 alt=_('False'))
312
312
313 return value
313 return value
314
314
315
315
316 def action_parser(user_log, feed=False):
316 def action_parser(user_log, feed=False):
317 """This helper will action_map the specified string action into translated
317 """This helper will action_map the specified string action into translated
318 fancy names with icons and links
318 fancy names with icons and links
319
319
320 :param user_log: user log instance
320 :param user_log: user log instance
321 :param feed: use output for feeds (no html and fancy icons)
321 :param feed: use output for feeds (no html and fancy icons)
322 """
322 """
323
323
324 action = user_log.action
324 action = user_log.action
325 action_params = ' '
325 action_params = ' '
326
326
327 x = action.split(':')
327 x = action.split(':')
328
328
329 if len(x) > 1:
329 if len(x) > 1:
330 action, action_params = x
330 action, action_params = x
331
331
332 def get_cs_links():
332 def get_cs_links():
333 revs_limit = 3 #display this amount always
333 revs_limit = 3 #display this amount always
334 revs_top_limit = 50 #show upto this amount of changesets hidden
334 revs_top_limit = 50 #show upto this amount of changesets hidden
335 revs = action_params.split(',')
335 revs = action_params.split(',')
336 repo_name = user_log.repository.repo_name
336 repo_name = user_log.repository.repo_name
337
337
338 from rhodecode.model.scm import ScmModel
338 from rhodecode.model.scm import ScmModel
339 repo = user_log.repository.scm_instance
339 repo = user_log.repository.scm_instance
340
340
341 message = lambda rev: get_changeset_safe(repo, rev).message
341 message = lambda rev: get_changeset_safe(repo, rev).message
342 cs_links = []
342 cs_links = []
343 cs_links.append(" " + ', '.join ([link_to(rev,
343 cs_links.append(" " + ', '.join ([link_to(rev,
344 url('changeset_home',
344 url('changeset_home',
345 repo_name=repo_name,
345 repo_name=repo_name,
346 revision=rev), title=tooltip(message(rev)),
346 revision=rev), title=tooltip(message(rev)),
347 class_='tooltip') for rev in revs[:revs_limit] ]))
347 class_='tooltip') for rev in revs[:revs_limit] ]))
348
348
349 compare_view = (' <div class="compare_view tooltip" title="%s">'
349 compare_view = (' <div class="compare_view tooltip" title="%s">'
350 '<a href="%s">%s</a> '
350 '<a href="%s">%s</a> '
351 '</div>' % (_('Show all combined changesets %s->%s' \
351 '</div>' % (_('Show all combined changesets %s->%s' \
352 % (revs[0], revs[-1])),
352 % (revs[0], revs[-1])),
353 url('changeset_home', repo_name=repo_name,
353 url('changeset_home', repo_name=repo_name,
354 revision='%s...%s' % (revs[0], revs[-1])
354 revision='%s...%s' % (revs[0], revs[-1])
355 ),
355 ),
356 _('compare view'))
356 _('compare view'))
357 )
357 )
358
358
359 if len(revs) > revs_limit:
359 if len(revs) > revs_limit:
360 uniq_id = revs[0]
360 uniq_id = revs[0]
361 html_tmpl = ('<span> %s '
361 html_tmpl = ('<span> %s '
362 '<a class="show_more" id="_%s" href="#more">%s</a> '
362 '<a class="show_more" id="_%s" href="#more">%s</a> '
363 '%s</span>')
363 '%s</span>')
364 if not feed:
364 if not feed:
365 cs_links.append(html_tmpl % (_('and'), uniq_id, _('%s more') \
365 cs_links.append(html_tmpl % (_('and'), uniq_id, _('%s more') \
366 % (len(revs) - revs_limit),
366 % (len(revs) - revs_limit),
367 _('revisions')))
367 _('revisions')))
368
368
369 if not feed:
369 if not feed:
370 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
370 html_tmpl = '<span id="%s" style="display:none"> %s </span>'
371 else:
371 else:
372 html_tmpl = '<span id="%s"> %s </span>'
372 html_tmpl = '<span id="%s"> %s </span>'
373
373
374 cs_links.append(html_tmpl % (uniq_id, ', '.join([link_to(rev,
374 cs_links.append(html_tmpl % (uniq_id, ', '.join([link_to(rev,
375 url('changeset_home',
375 url('changeset_home',
376 repo_name=repo_name, revision=rev),
376 repo_name=repo_name, revision=rev),
377 title=message(rev), class_='tooltip')
377 title=message(rev), class_='tooltip')
378 for rev in revs[revs_limit:revs_top_limit]])))
378 for rev in revs[revs_limit:revs_top_limit]])))
379 if len(revs) > 1:
379 if len(revs) > 1:
380 cs_links.append(compare_view)
380 cs_links.append(compare_view)
381 return ''.join(cs_links)
381 return ''.join(cs_links)
382
382
383 def get_fork_name():
383 def get_fork_name():
384 repo_name = action_params
384 repo_name = action_params
385 return _('fork name ') + str(link_to(action_params, url('summary_home',
385 return _('fork name ') + str(link_to(action_params, url('summary_home',
386 repo_name=repo_name,)))
386 repo_name=repo_name,)))
387
387
388 action_map = {'user_deleted_repo':(_('[deleted] repository'), None),
388 action_map = {'user_deleted_repo':(_('[deleted] repository'), None),
389 'user_created_repo':(_('[created] repository'), None),
389 'user_created_repo':(_('[created] repository'), None),
390 'user_forked_repo':(_('[forked] repository'), get_fork_name),
390 'user_forked_repo':(_('[forked] repository'), get_fork_name),
391 'user_updated_repo':(_('[updated] repository'), None),
391 'user_updated_repo':(_('[updated] repository'), None),
392 'admin_deleted_repo':(_('[delete] repository'), None),
392 'admin_deleted_repo':(_('[delete] repository'), None),
393 'admin_created_repo':(_('[created] repository'), None),
393 'admin_created_repo':(_('[created] repository'), None),
394 'admin_forked_repo':(_('[forked] repository'), None),
394 'admin_forked_repo':(_('[forked] repository'), None),
395 'admin_updated_repo':(_('[updated] repository'), None),
395 'admin_updated_repo':(_('[updated] repository'), None),
396 'push':(_('[pushed] into'), get_cs_links),
396 'push':(_('[pushed] into'), get_cs_links),
397 'push_local':(_('[committed via RhodeCode] into'), get_cs_links),
397 'push_local':(_('[committed via RhodeCode] into'), get_cs_links),
398 'push_remote':(_('[pulled from remote] into'), get_cs_links),
398 'push_remote':(_('[pulled from remote] into'), get_cs_links),
399 'pull':(_('[pulled] from'), None),
399 'pull':(_('[pulled] from'), None),
400 'started_following_repo':(_('[started following] repository'), None),
400 'started_following_repo':(_('[started following] repository'), None),
401 'stopped_following_repo':(_('[stopped following] repository'), None),
401 'stopped_following_repo':(_('[stopped following] repository'), None),
402 }
402 }
403
403
404 action_str = action_map.get(action, action)
404 action_str = action_map.get(action, action)
405 if feed:
405 if feed:
406 action = action_str[0].replace('[', '').replace(']', '')
406 action = action_str[0].replace('[', '').replace(']', '')
407 else:
407 else:
408 action = action_str[0].replace('[', '<span class="journal_highlight">')\
408 action = action_str[0].replace('[', '<span class="journal_highlight">')\
409 .replace(']', '</span>')
409 .replace(']', '</span>')
410
410
411 action_params_func = lambda :""
411 action_params_func = lambda :""
412
412
413 if callable(action_str[1]):
413 if callable(action_str[1]):
414 action_params_func = action_str[1]
414 action_params_func = action_str[1]
415
415
416 return [literal(action), action_params_func]
416 return [literal(action), action_params_func]
417
417
418 def action_parser_icon(user_log):
418 def action_parser_icon(user_log):
419 action = user_log.action
419 action = user_log.action
420 action_params = None
420 action_params = None
421 x = action.split(':')
421 x = action.split(':')
422
422
423 if len(x) > 1:
423 if len(x) > 1:
424 action, action_params = x
424 action, action_params = x
425
425
426 tmpl = """<img src="%s%s" alt="%s"/>"""
426 tmpl = """<img src="%s%s" alt="%s"/>"""
427 map = {'user_deleted_repo':'database_delete.png',
427 map = {'user_deleted_repo':'database_delete.png',
428 'user_created_repo':'database_add.png',
428 'user_created_repo':'database_add.png',
429 'user_forked_repo':'arrow_divide.png',
429 'user_forked_repo':'arrow_divide.png',
430 'user_updated_repo':'database_edit.png',
430 'user_updated_repo':'database_edit.png',
431 'admin_deleted_repo':'database_delete.png',
431 'admin_deleted_repo':'database_delete.png',
432 'admin_created_repo':'database_add.png',
432 'admin_created_repo':'database_add.png',
433 'admin_forked_repo':'arrow_divide.png',
433 'admin_forked_repo':'arrow_divide.png',
434 'admin_updated_repo':'database_edit.png',
434 'admin_updated_repo':'database_edit.png',
435 'push':'script_add.png',
435 'push':'script_add.png',
436 'push_local':'script_edit.png',
436 'push_local':'script_edit.png',
437 'push_remote':'connect.png',
437 'push_remote':'connect.png',
438 'pull':'down_16.png',
438 'pull':'down_16.png',
439 'started_following_repo':'heart_add.png',
439 'started_following_repo':'heart_add.png',
440 'stopped_following_repo':'heart_delete.png',
440 'stopped_following_repo':'heart_delete.png',
441 }
441 }
442 return literal(tmpl % ((url('/images/icons/')),
442 return literal(tmpl % ((url('/images/icons/')),
443 map.get(action, action), action))
443 map.get(action, action), action))
444
444
445
445
446 #==============================================================================
446 #==============================================================================
447 # PERMS
447 # PERMS
448 #==============================================================================
448 #==============================================================================
449 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
449 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
450 HasRepoPermissionAny, HasRepoPermissionAll
450 HasRepoPermissionAny, HasRepoPermissionAll
451
451
452 #==============================================================================
452 #==============================================================================
453 # GRAVATAR URL
453 # GRAVATAR URL
454 #==============================================================================
454 #==============================================================================
455
455
456 def gravatar_url(email_address, size=30):
456 def gravatar_url(email_address, size=30):
457 if not str2bool(config['app_conf'].get('use_gravatar')) or \
457 if not str2bool(config['app_conf'].get('use_gravatar')) or \
458 email_address == 'anonymous@rhodecode.org':
458 email_address == 'anonymous@rhodecode.org':
459 return "/images/user%s.png" % size
459 return "/images/user%s.png" % size
460
460
461 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
461 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
462 default = 'identicon'
462 default = 'identicon'
463 baseurl_nossl = "http://www.gravatar.com/avatar/"
463 baseurl_nossl = "http://www.gravatar.com/avatar/"
464 baseurl_ssl = "https://secure.gravatar.com/avatar/"
464 baseurl_ssl = "https://secure.gravatar.com/avatar/"
465 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
465 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
466
466
467 if isinstance(email_address, unicode):
467 if isinstance(email_address, unicode):
468 #hashlib crashes on unicode items
468 #hashlib crashes on unicode items
469 email_address = safe_str(email_address)
469 email_address = safe_str(email_address)
470 # construct the url
470 # construct the url
471 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
471 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
472 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
472 gravatar_url += urllib.urlencode({'d':default, 's':str(size)})
473
473
474 return gravatar_url
474 return gravatar_url
475
475
476
476
477 #==============================================================================
477 #==============================================================================
478 # REPO PAGER, PAGER FOR REPOSITORY
478 # REPO PAGER, PAGER FOR REPOSITORY
479 #==============================================================================
479 #==============================================================================
480 class RepoPage(Page):
480 class RepoPage(Page):
481
481
482 def __init__(self, collection, page=1, items_per_page=20,
482 def __init__(self, collection, page=1, items_per_page=20,
483 item_count=None, url=None, branch_name=None, **kwargs):
483 item_count=None, url=None, branch_name=None, **kwargs):
484
484
485 """Create a "RepoPage" instance. special pager for paging
485 """Create a "RepoPage" instance. special pager for paging
486 repository
486 repository
487 """
487 """
488 self._url_generator = url
488 self._url_generator = url
489
489
490 # Safe the kwargs class-wide so they can be used in the pager() method
490 # Safe the kwargs class-wide so they can be used in the pager() method
491 self.kwargs = kwargs
491 self.kwargs = kwargs
492
492
493 # Save a reference to the collection
493 # Save a reference to the collection
494 self.original_collection = collection
494 self.original_collection = collection
495
495
496 self.collection = collection
496 self.collection = collection
497
497
498 # The self.page is the number of the current page.
498 # The self.page is the number of the current page.
499 # The first page has the number 1!
499 # The first page has the number 1!
500 try:
500 try:
501 self.page = int(page) # make it int() if we get it as a string
501 self.page = int(page) # make it int() if we get it as a string
502 except (ValueError, TypeError):
502 except (ValueError, TypeError):
503 self.page = 1
503 self.page = 1
504
504
505 self.items_per_page = items_per_page
505 self.items_per_page = items_per_page
506
506
507 # Unless the user tells us how many items the collections has
507 # Unless the user tells us how many items the collections has
508 # we calculate that ourselves.
508 # we calculate that ourselves.
509 if item_count is not None:
509 if item_count is not None:
510 self.item_count = item_count
510 self.item_count = item_count
511 else:
511 else:
512 self.item_count = len(self.collection)
512 self.item_count = len(self.collection)
513
513
514 # Compute the number of the first and last available page
514 # Compute the number of the first and last available page
515 if self.item_count > 0:
515 if self.item_count > 0:
516 self.first_page = 1
516 self.first_page = 1
517 self.page_count = int(math.ceil(float(self.item_count) /
517 self.page_count = int(math.ceil(float(self.item_count) /
518 self.items_per_page))
518 self.items_per_page))
519 self.last_page = self.first_page + self.page_count - 1
519 self.last_page = self.first_page + self.page_count - 1
520
520
521 # Make sure that the requested page number is the range of valid pages
521 # Make sure that the requested page number is the range of valid pages
522 if self.page > self.last_page:
522 if self.page > self.last_page:
523 self.page = self.last_page
523 self.page = self.last_page
524 elif self.page < self.first_page:
524 elif self.page < self.first_page:
525 self.page = self.first_page
525 self.page = self.first_page
526
526
527 # Note: the number of items on this page can be less than
527 # Note: the number of items on this page can be less than
528 # items_per_page if the last page is not full
528 # items_per_page if the last page is not full
529 self.first_item = max(0, (self.item_count) - (self.page *
529 self.first_item = max(0, (self.item_count) - (self.page *
530 items_per_page))
530 items_per_page))
531 self.last_item = ((self.item_count - 1) - items_per_page *
531 self.last_item = ((self.item_count - 1) - items_per_page *
532 (self.page - 1))
532 (self.page - 1))
533
533
534 iterator = self.collection.get_changesets(start=self.first_item,
534 iterator = self.collection.get_changesets(start=self.first_item,
535 end=self.last_item,
535 end=self.last_item,
536 reverse=True,
536 reverse=True,
537 branch_name=branch_name)
537 branch_name=branch_name)
538 self.items = list(iterator)
538 self.items = list(iterator)
539
539
540 # Links to previous and next page
540 # Links to previous and next page
541 if self.page > self.first_page:
541 if self.page > self.first_page:
542 self.previous_page = self.page - 1
542 self.previous_page = self.page - 1
543 else:
543 else:
544 self.previous_page = None
544 self.previous_page = None
545
545
546 if self.page < self.last_page:
546 if self.page < self.last_page:
547 self.next_page = self.page + 1
547 self.next_page = self.page + 1
548 else:
548 else:
549 self.next_page = None
549 self.next_page = None
550
550
551 # No items available
551 # No items available
552 else:
552 else:
553 self.first_page = None
553 self.first_page = None
554 self.page_count = 0
554 self.page_count = 0
555 self.last_page = None
555 self.last_page = None
556 self.first_item = None
556 self.first_item = None
557 self.last_item = None
557 self.last_item = None
558 self.previous_page = None
558 self.previous_page = None
559 self.next_page = None
559 self.next_page = None
560 self.items = []
560 self.items = []
561
561
562 # This is a subclass of the 'list' type. Initialise the list now.
562 # This is a subclass of the 'list' type. Initialise the list now.
563 list.__init__(self, self.items)
563 list.__init__(self, self.items)
564
564
565
565
566 def changed_tooltip(nodes):
566 def changed_tooltip(nodes):
567 """
567 """
568 Generates a html string for changed nodes in changeset page.
568 Generates a html string for changed nodes in changeset page.
569 It limits the output to 30 entries
569 It limits the output to 30 entries
570
570
571 :param nodes: LazyNodesGenerator
571 :param nodes: LazyNodesGenerator
572 """
572 """
573 if nodes:
573 if nodes:
574 pref = ': <br/> '
574 pref = ': <br/> '
575 suf = ''
575 suf = ''
576 if len(nodes) > 30:
576 if len(nodes) > 30:
577 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
577 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
578 return literal(pref + '<br/> '.join([safe_unicode(x.path)
578 return literal(pref + '<br/> '.join([safe_unicode(x.path)
579 for x in nodes[:30]]) + suf)
579 for x in nodes[:30]]) + suf)
580 else:
580 else:
581 return ': ' + _('No Files')
581 return ': ' + _('No Files')
582
582
583
583
584
584
585 def repo_link(groups_and_repos):
585 def repo_link(groups_and_repos):
586 """
586 """
587 Makes a breadcrumbs link to repo within a group
587 Makes a breadcrumbs link to repo within a group
588 joins &raquo; on each group to create a fancy link
588 joins &raquo; on each group to create a fancy link
589
589
590 ex::
590 ex::
591 group >> subgroup >> repo
591 group >> subgroup >> repo
592
592
593 :param groups_and_repos:
593 :param groups_and_repos:
594 """
594 """
595 groups, repo_name = groups_and_repos
595 groups, repo_name = groups_and_repos
596
596
597 if not groups:
597 if not groups:
598 return repo_name
598 return repo_name
599 else:
599 else:
600 def make_link(group):
600 def make_link(group):
601 return link_to(group.group_name, url('repos_group',
601 return link_to(group.group_name, url('repos_group',
602 id=group.group_id))
602 id=group.group_id))
603 return literal(' &raquo; '.join(map(make_link, groups)) + \
603 return literal(' &raquo; '.join(map(make_link, groups)) + \
604 " &raquo; " + repo_name)
604 " &raquo; " + repo_name)
605
605
606
606
607 def fancy_file_stats(stats):
607 def fancy_file_stats(stats):
608 """
608 """
609 Displays a fancy two colored bar for number of added/deleted
609 Displays a fancy two colored bar for number of added/deleted
610 lines of code on file
610 lines of code on file
611
611
612 :param stats: two element list of added/deleted lines of code
612 :param stats: two element list of added/deleted lines of code
613 """
613 """
614
614
615 a, d, t = stats[0], stats[1], stats[0] + stats[1]
615 a, d, t = stats[0], stats[1], stats[0] + stats[1]
616 width = 100
616 width = 100
617 unit = float(width) / (t or 1)
617 unit = float(width) / (t or 1)
618
618
619 # needs > 9% of width to be visible or 0 to be hidden
619 # needs > 9% of width to be visible or 0 to be hidden
620 a_p = max(9, unit * a) if a > 0 else 0
620 a_p = max(9, unit * a) if a > 0 else 0
621 d_p = max(9, unit * d) if d > 0 else 0
621 d_p = max(9, unit * d) if d > 0 else 0
622 p_sum = a_p + d_p
622 p_sum = a_p + d_p
623
623
624 if p_sum > width:
624 if p_sum > width:
625 #adjust the percentage to be == 100% since we adjusted to 9
625 #adjust the percentage to be == 100% since we adjusted to 9
626 if a_p > d_p:
626 if a_p > d_p:
627 a_p = a_p - (p_sum - width)
627 a_p = a_p - (p_sum - width)
628 else:
628 else:
629 d_p = d_p - (p_sum - width)
629 d_p = d_p - (p_sum - width)
630
630
631 a_v = a if a > 0 else ''
631 a_v = a if a > 0 else ''
632 d_v = d if d > 0 else ''
632 d_v = d if d > 0 else ''
633
633
634
634
635 def cgen(l_type):
635 def cgen(l_type):
636 mapping = {'tr':'top-right-rounded-corner',
636 mapping = {'tr':'top-right-rounded-corner',
637 'tl':'top-left-rounded-corner',
637 'tl':'top-left-rounded-corner',
638 'br':'bottom-right-rounded-corner',
638 'br':'bottom-right-rounded-corner',
639 'bl':'bottom-left-rounded-corner'}
639 'bl':'bottom-left-rounded-corner'}
640 map_getter = lambda x:mapping[x]
640 map_getter = lambda x:mapping[x]
641
641
642 if l_type == 'a' and d_v:
642 if l_type == 'a' and d_v:
643 #case when added and deleted are present
643 #case when added and deleted are present
644 return ' '.join(map(map_getter, ['tl', 'bl']))
644 return ' '.join(map(map_getter, ['tl', 'bl']))
645
645
646 if l_type == 'a' and not d_v:
646 if l_type == 'a' and not d_v:
647 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
647 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
648
648
649 if l_type == 'd' and a_v:
649 if l_type == 'd' and a_v:
650 return ' '.join(map(map_getter, ['tr', 'br']))
650 return ' '.join(map(map_getter, ['tr', 'br']))
651
651
652 if l_type == 'd' and not a_v:
652 if l_type == 'd' and not a_v:
653 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
653 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
654
654
655
655
656
656
657 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
657 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
658 a_p, a_v)
658 a_p, a_v)
659 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
659 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
660 d_p, d_v)
660 d_p, d_v)
661 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
661 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
662
663
664 def urlify_text(text):
665 import re
666
667 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
668
669 def url_func(match_obj):
670 url_full = match_obj.groups()[0]
671 return '<a href="%(url)s">%(url)s</a>' % ({'url':url_full})
672
673 return literal(url_pat.sub(url_func, text))
@@ -1,696 +1,696 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} ${_('Summary')} - ${c.rhodecode_name}
4 ${c.repo_name} ${_('Summary')} - ${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.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
10 ${h.link_to(c.dbrepo.just_name,h.url('summary_home',repo_name=c.repo_name))}
11 &raquo;
11 &raquo;
12 ${_('summary')}
12 ${_('summary')}
13 </%def>
13 </%def>
14
14
15 <%def name="page_nav()">
15 <%def name="page_nav()">
16 ${self.menu('summary')}
16 ${self.menu('summary')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box box-left">
20 <div class="box box-left">
21 <!-- box / title -->
21 <!-- box / title -->
22 <div class="title">
22 <div class="title">
23 ${self.breadcrumbs()}
23 ${self.breadcrumbs()}
24 </div>
24 </div>
25 <!-- end box / title -->
25 <!-- end box / title -->
26 <div class="form">
26 <div class="form">
27 <div id="summary" class="fields">
27 <div id="summary" class="fields">
28
28
29 <div class="field">
29 <div class="field">
30 <div class="label">
30 <div class="label">
31 <label>${_('Name')}:</label>
31 <label>${_('Name')}:</label>
32 </div>
32 </div>
33 <div class="input-short">
33 <div class="input-short">
34 %if c.rhodecode_user.username != 'default':
34 %if c.rhodecode_user.username != 'default':
35 %if c.following:
35 %if c.following:
36 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
36 <span id="follow_toggle" class="following" title="${_('Stop following this repository')}"
37 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
37 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
38 </span>
38 </span>
39 %else:
39 %else:
40 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
40 <span id="follow_toggle" class="follow" title="${_('Start following this repository')}"
41 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
41 onclick="javascript:toggleFollowingRepo(this,${c.dbrepo.repo_id},'${str(h.get_token())}')">
42 </span>
42 </span>
43 %endif
43 %endif
44 %endif:
44 %endif:
45
45
46 ##REPO TYPE
46 ##REPO TYPE
47 %if c.dbrepo.repo_type =='hg':
47 %if c.dbrepo.repo_type =='hg':
48 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
48 <img style="margin-bottom:2px" class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
49 %endif
49 %endif
50 %if c.dbrepo.repo_type =='git':
50 %if c.dbrepo.repo_type =='git':
51 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
51 <img style="margin-bottom:2px" class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
52 %endif
52 %endif
53
53
54 ##PUBLIC/PRIVATE
54 ##PUBLIC/PRIVATE
55 %if c.dbrepo.private:
55 %if c.dbrepo.private:
56 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
56 <img style="margin-bottom:2px" class="icon" title="${_('private repository')}" alt="${_('private repository')}" src="${h.url("/images/icons/lock.png")}"/>
57 %else:
57 %else:
58 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
58 <img style="margin-bottom:2px" class="icon" title="${_('public repository')}" alt="${_('public repository')}" src="${h.url("/images/icons/lock_open.png")}"/>
59 %endif
59 %endif
60
60
61 ##REPO NAME
61 ##REPO NAME
62 <span class="repo_name">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
62 <span class="repo_name">${h.repo_link(c.dbrepo.groups_and_repo)}</span>
63
63
64 ##FORK
64 ##FORK
65 %if c.dbrepo.fork:
65 %if c.dbrepo.fork:
66 <div style="margin-top:5px;clear:both"">
66 <div style="margin-top:5px;clear:both"">
67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
67 <a href="${h.url('summary_home',repo_name=c.dbrepo.fork.repo_name)}">
68 <img class="icon" alt="${_('public')}"
68 <img class="icon" alt="${_('public')}"
69 title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
69 title="${_('Fork of')} ${c.dbrepo.fork.repo_name}"
70 src="${h.url("/images/icons/arrow_divide.png")}"/>
70 src="${h.url("/images/icons/arrow_divide.png")}"/>
71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
71 ${_('Fork of')} ${c.dbrepo.fork.repo_name}
72 </a>
72 </a>
73 </div>
73 </div>
74 %endif
74 %endif
75 ##REMOTE
75 ##REMOTE
76 %if c.dbrepo.clone_uri:
76 %if c.dbrepo.clone_uri:
77 <div style="margin-top:5px;clear:both">
77 <div style="margin-top:5px;clear:both">
78 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}">
78 <a href="${h.url(str(h.hide_credentials(c.dbrepo.clone_uri)))}">
79 <img class="icon" alt="${_('remote clone')}"
79 <img class="icon" alt="${_('remote clone')}"
80 title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}"
80 title="${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}"
81 src="${h.url("/images/icons/connect.png")}"/>
81 src="${h.url("/images/icons/connect.png")}"/>
82 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
82 ${_('Clone from')} ${h.hide_credentials(c.dbrepo.clone_uri)}
83 </a>
83 </a>
84 </div>
84 </div>
85 %endif
85 %endif
86 </div>
86 </div>
87 </div>
87 </div>
88
88
89
89
90 <div class="field">
90 <div class="field">
91 <div class="label">
91 <div class="label">
92 <label>${_('Description')}:</label>
92 <label>${_('Description')}:</label>
93 </div>
93 </div>
94 <div class="input-short desc">${c.dbrepo.description}</div>
94 <div class="input-short desc">${h.urlify_text(c.dbrepo.description)}</div>
95 </div>
95 </div>
96
96
97
97
98 <div class="field">
98 <div class="field">
99 <div class="label">
99 <div class="label">
100 <label>${_('Contact')}:</label>
100 <label>${_('Contact')}:</label>
101 </div>
101 </div>
102 <div class="input-short">
102 <div class="input-short">
103 <div class="gravatar">
103 <div class="gravatar">
104 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
104 <img alt="gravatar" src="${h.gravatar_url(c.dbrepo.user.email)}"/>
105 </div>
105 </div>
106 ${_('Username')}: ${c.dbrepo.user.username}<br/>
106 ${_('Username')}: ${c.dbrepo.user.username}<br/>
107 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
107 ${_('Name')}: ${c.dbrepo.user.name} ${c.dbrepo.user.lastname}<br/>
108 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
108 ${_('Email')}: <a href="mailto:${c.dbrepo.user.email}">${c.dbrepo.user.email}</a>
109 </div>
109 </div>
110 </div>
110 </div>
111
111
112 <div class="field">
112 <div class="field">
113 <div class="label">
113 <div class="label">
114 <label>${_('Last change')}:</label>
114 <label>${_('Last change')}:</label>
115 </div>
115 </div>
116 <div class="input-short">
116 <div class="input-short">
117 <b>${'r%s:%s' % (h.get_changeset_safe(c.rhodecode_repo,'tip').revision,
117 <b>${'r%s:%s' % (h.get_changeset_safe(c.rhodecode_repo,'tip').revision,
118 h.get_changeset_safe(c.rhodecode_repo,'tip').short_id)}</b> -
118 h.get_changeset_safe(c.rhodecode_repo,'tip').short_id)}</b> -
119 <span class="tooltip" title="${c.rhodecode_repo.last_change}">
119 <span class="tooltip" title="${c.rhodecode_repo.last_change}">
120 ${h.age(c.rhodecode_repo.last_change)}</span><br/>
120 ${h.age(c.rhodecode_repo.last_change)}</span><br/>
121 ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
121 ${_('by')} ${h.get_changeset_safe(c.rhodecode_repo,'tip').author}
122
122
123 </div>
123 </div>
124 </div>
124 </div>
125
125
126 <div class="field">
126 <div class="field">
127 <div class="label">
127 <div class="label">
128 <label>${_('Clone url')}:</label>
128 <label>${_('Clone url')}:</label>
129 </div>
129 </div>
130 <div class="input-short">
130 <div class="input-short">
131 <input type="text" id="clone_url" readonly="readonly" value="${c.rhodecode_repo.alias} clone ${c.clone_repo_url}" size="70"/>
131 <input type="text" id="clone_url" readonly="readonly" value="${c.rhodecode_repo.alias} clone ${c.clone_repo_url}" size="70"/>
132 </div>
132 </div>
133 </div>
133 </div>
134
134
135 <div class="field">
135 <div class="field">
136 <div class="label">
136 <div class="label">
137 <label>${_('Trending source files')}:</label>
137 <label>${_('Trending source files')}:</label>
138 </div>
138 </div>
139 <div class="input-short">
139 <div class="input-short">
140 <div id="lang_stats"></div>
140 <div id="lang_stats"></div>
141 </div>
141 </div>
142 </div>
142 </div>
143
143
144 <div class="field">
144 <div class="field">
145 <div class="label">
145 <div class="label">
146 <label>${_('Download')}:</label>
146 <label>${_('Download')}:</label>
147 </div>
147 </div>
148 <div class="input-short">
148 <div class="input-short">
149 %if len(c.rhodecode_repo.revisions) == 0:
149 %if len(c.rhodecode_repo.revisions) == 0:
150 ${_('There are no downloads yet')}
150 ${_('There are no downloads yet')}
151 %elif c.enable_downloads is False:
151 %elif c.enable_downloads is False:
152 ${_('Downloads are disabled for this repository')}
152 ${_('Downloads are disabled for this repository')}
153 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
153 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
154 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
154 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
155 %endif
155 %endif
156 %else:
156 %else:
157 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
157 ${h.select('download_options',c.rhodecode_repo.get_changeset().raw_id,c.download_options)}
158 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
158 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
159 %if cnt >=1:
159 %if cnt >=1:
160 |
160 |
161 %endif
161 %endif
162 <span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
162 <span class="tooltip" title="${_('Download %s as %s') %('tip',archive['type'])}"
163 id="${archive['type']+'_link'}">${h.link_to(archive['type'],
163 id="${archive['type']+'_link'}">${h.link_to(archive['type'],
164 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
164 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
165 fname='tip'+archive['extension']),class_="archive_icon")}</span>
165 fname='tip'+archive['extension']),class_="archive_icon")}</span>
166 %endfor
166 %endfor
167 %endif
167 %endif
168 </div>
168 </div>
169 </div>
169 </div>
170
170
171 <div class="field">
171 <div class="field">
172 <div class="label">
172 <div class="label">
173 <label>${_('Feeds')}:</label>
173 <label>${_('Feeds')}:</label>
174 </div>
174 </div>
175 <div class="input-short">
175 <div class="input-short">
176 %if c.rhodecode_user.username != 'default':
176 %if c.rhodecode_user.username != 'default':
177 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
177 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='rss_icon')}
178 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
178 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name,api_key=c.rhodecode_user.api_key),class_='atom_icon')}
179 %else:
179 %else:
180 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
180 ${h.link_to(_('RSS'),h.url('rss_feed_home',repo_name=c.dbrepo.repo_name),class_='rss_icon')}
181 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
181 ${h.link_to(_('Atom'),h.url('atom_feed_home',repo_name=c.dbrepo.repo_name),class_='atom_icon')}
182 %endif
182 %endif
183 </div>
183 </div>
184 </div>
184 </div>
185 </div>
185 </div>
186 </div>
186 </div>
187 <script type="text/javascript">
187 <script type="text/javascript">
188 YUE.onDOMReady(function(e){
188 YUE.onDOMReady(function(e){
189 id = 'clone_url';
189 id = 'clone_url';
190 YUE.on(id,'click',function(e){
190 YUE.on(id,'click',function(e){
191 if(YUD.hasClass(id,'selected')){
191 if(YUD.hasClass(id,'selected')){
192 return
192 return
193 }
193 }
194 else{
194 else{
195 YUD.addClass(id,'selected');
195 YUD.addClass(id,'selected');
196 YUD.get(id).select();
196 YUD.get(id).select();
197 }
197 }
198
198
199 })
199 })
200 })
200 })
201 var data = ${c.trending_languages|n};
201 var data = ${c.trending_languages|n};
202 var total = 0;
202 var total = 0;
203 var no_data = true;
203 var no_data = true;
204 for (k in data){
204 for (k in data){
205 total += data[k].count;
205 total += data[k].count;
206 no_data = false;
206 no_data = false;
207 }
207 }
208 var tbl = document.createElement('table');
208 var tbl = document.createElement('table');
209 tbl.setAttribute('class','trending_language_tbl');
209 tbl.setAttribute('class','trending_language_tbl');
210 var cnt = 0;
210 var cnt = 0;
211 for (k in data){
211 for (k in data){
212 cnt += 1;
212 cnt += 1;
213 var hide = cnt>2;
213 var hide = cnt>2;
214 var tr = document.createElement('tr');
214 var tr = document.createElement('tr');
215 if (hide){
215 if (hide){
216 tr.setAttribute('style','display:none');
216 tr.setAttribute('style','display:none');
217 tr.setAttribute('class','stats_hidden');
217 tr.setAttribute('class','stats_hidden');
218 }
218 }
219 var percentage = Math.round((data[k].count/total*100),2);
219 var percentage = Math.round((data[k].count/total*100),2);
220 var value = data[k].count;
220 var value = data[k].count;
221 var td1 = document.createElement('td');
221 var td1 = document.createElement('td');
222 td1.width = 150;
222 td1.width = 150;
223 var trending_language_label = document.createElement('div');
223 var trending_language_label = document.createElement('div');
224 trending_language_label.innerHTML = data[k].desc+" ("+k+")";
224 trending_language_label.innerHTML = data[k].desc+" ("+k+")";
225 td1.appendChild(trending_language_label);
225 td1.appendChild(trending_language_label);
226
226
227 var td2 = document.createElement('td');
227 var td2 = document.createElement('td');
228 td2.setAttribute('style','padding-right:14px !important');
228 td2.setAttribute('style','padding-right:14px !important');
229 var trending_language = document.createElement('div');
229 var trending_language = document.createElement('div');
230 var nr_files = value+" ${_('files')}";
230 var nr_files = value+" ${_('files')}";
231
231
232 trending_language.title = k+" "+nr_files;
232 trending_language.title = k+" "+nr_files;
233
233
234 if (percentage>22){
234 if (percentage>22){
235 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
235 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"% "+nr_files+ "</b>";
236 }
236 }
237 else{
237 else{
238 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
238 trending_language.innerHTML = "<b style='font-size:0.8em'>"+percentage+"%</b>";
239 }
239 }
240
240
241 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
241 trending_language.setAttribute("class", 'trending_language top-right-rounded-corner bottom-right-rounded-corner');
242 trending_language.style.width=percentage+"%";
242 trending_language.style.width=percentage+"%";
243 td2.appendChild(trending_language);
243 td2.appendChild(trending_language);
244
244
245 tr.appendChild(td1);
245 tr.appendChild(td1);
246 tr.appendChild(td2);
246 tr.appendChild(td2);
247 tbl.appendChild(tr);
247 tbl.appendChild(tr);
248 if(cnt == 3){
248 if(cnt == 3){
249 var show_more = document.createElement('tr');
249 var show_more = document.createElement('tr');
250 var td = document.createElement('td');
250 var td = document.createElement('td');
251 lnk = document.createElement('a');
251 lnk = document.createElement('a');
252
252
253 lnk.href='#';
253 lnk.href='#';
254 lnk.innerHTML = "${_('show more')}";
254 lnk.innerHTML = "${_('show more')}";
255 lnk.id='code_stats_show_more';
255 lnk.id='code_stats_show_more';
256 td.appendChild(lnk);
256 td.appendChild(lnk);
257
257
258 show_more.appendChild(td);
258 show_more.appendChild(td);
259 show_more.appendChild(document.createElement('td'));
259 show_more.appendChild(document.createElement('td'));
260 tbl.appendChild(show_more);
260 tbl.appendChild(show_more);
261 }
261 }
262
262
263 }
263 }
264 if(no_data){
264 if(no_data){
265 var tr = document.createElement('tr');
265 var tr = document.createElement('tr');
266 var td1 = document.createElement('td');
266 var td1 = document.createElement('td');
267 td1.innerHTML = "${c.no_data_msg}";
267 td1.innerHTML = "${c.no_data_msg}";
268 tr.appendChild(td1);
268 tr.appendChild(td1);
269 tbl.appendChild(tr);
269 tbl.appendChild(tr);
270 }
270 }
271 YUD.get('lang_stats').appendChild(tbl);
271 YUD.get('lang_stats').appendChild(tbl);
272 YUE.on('code_stats_show_more','click',function(){
272 YUE.on('code_stats_show_more','click',function(){
273 l = YUD.getElementsByClassName('stats_hidden')
273 l = YUD.getElementsByClassName('stats_hidden')
274 for (e in l){
274 for (e in l){
275 YUD.setStyle(l[e],'display','');
275 YUD.setStyle(l[e],'display','');
276 };
276 };
277 YUD.setStyle(YUD.get('code_stats_show_more'),
277 YUD.setStyle(YUD.get('code_stats_show_more'),
278 'display','none');
278 'display','none');
279 })
279 })
280
280
281
281
282 YUE.on('download_options','change',function(e){
282 YUE.on('download_options','change',function(e){
283 var new_cs = e.target.options[e.target.selectedIndex];
283 var new_cs = e.target.options[e.target.selectedIndex];
284 var tmpl_links = {}
284 var tmpl_links = {}
285 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
285 %for cnt,archive in enumerate(c.rhodecode_repo._get_archives()):
286 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
286 tmpl_links['${archive['type']}'] = '${h.link_to(archive['type'],
287 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
287 h.url('files_archive_home',repo_name=c.dbrepo.repo_name,
288 fname='__CS__'+archive['extension']),class_="archive_icon")}';
288 fname='__CS__'+archive['extension']),class_="archive_icon")}';
289 %endfor
289 %endfor
290
290
291
291
292 for(k in tmpl_links){
292 for(k in tmpl_links){
293 var s = YUD.get(k+'_link')
293 var s = YUD.get(k+'_link')
294 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
294 title_tmpl = "${_('Download %s as %s') % ('__CS_NAME__','__CS_EXT__')}";
295 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
295 s.title = title_tmpl.replace('__CS_NAME__',new_cs.text);
296 s.title = s.title.replace('__CS_EXT__',k);
296 s.title = s.title.replace('__CS_EXT__',k);
297 s.innerHTML = tmpl_links[k].replace('__CS__',new_cs.value);
297 s.innerHTML = tmpl_links[k].replace('__CS__',new_cs.value);
298 }
298 }
299
299
300 })
300 })
301
301
302 </script>
302 </script>
303 </div>
303 </div>
304
304
305 <div class="box box-right" style="min-height:455px">
305 <div class="box box-right" style="min-height:455px">
306 <!-- box / title -->
306 <!-- box / title -->
307 <div class="title">
307 <div class="title">
308 <h5>${_('Commit activity by day / author')}</h5>
308 <h5>${_('Commit activity by day / author')}</h5>
309 </div>
309 </div>
310
310
311 <div class="graph">
311 <div class="graph">
312 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
312 <div style="padding:0 10px 10px 15px;font-size: 1.2em;">
313 %if c.no_data:
313 %if c.no_data:
314 ${c.no_data_msg}
314 ${c.no_data_msg}
315 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
315 %if h.HasPermissionAll('hg.admin')('enable stats on from summary'):
316 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
316 [${h.link_to(_('enable'),h.url('edit_repo',repo_name=c.repo_name))}]
317 %endif
317 %endif
318
318
319 %else:
319 %else:
320 ${_('Loaded in')} ${c.stats_percentage} %
320 ${_('Loaded in')} ${c.stats_percentage} %
321 %endif
321 %endif
322 </div>
322 </div>
323 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
323 <div id="commit_history" style="width:450px;height:300px;float:left"></div>
324 <div style="clear: both;height: 10px"></div>
324 <div style="clear: both;height: 10px"></div>
325 <div id="overview" style="width:450px;height:100px;float:left"></div>
325 <div id="overview" style="width:450px;height:100px;float:left"></div>
326
326
327 <div id="legend_data" style="clear:both;margin-top:10px;">
327 <div id="legend_data" style="clear:both;margin-top:10px;">
328 <div id="legend_container"></div>
328 <div id="legend_container"></div>
329 <div id="legend_choices">
329 <div id="legend_choices">
330 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
330 <table id="legend_choices_tables" style="font-size:smaller;color:#545454"></table>
331 </div>
331 </div>
332 </div>
332 </div>
333 <script type="text/javascript">
333 <script type="text/javascript">
334 /**
334 /**
335 * Plots summary graph
335 * Plots summary graph
336 *
336 *
337 * @class SummaryPlot
337 * @class SummaryPlot
338 * @param {from} initial from for detailed graph
338 * @param {from} initial from for detailed graph
339 * @param {to} initial to for detailed graph
339 * @param {to} initial to for detailed graph
340 * @param {dataset}
340 * @param {dataset}
341 * @param {overview_dataset}
341 * @param {overview_dataset}
342 */
342 */
343 function SummaryPlot(from,to,dataset,overview_dataset) {
343 function SummaryPlot(from,to,dataset,overview_dataset) {
344 var initial_ranges = {
344 var initial_ranges = {
345 "xaxis":{
345 "xaxis":{
346 "from":from,
346 "from":from,
347 "to":to,
347 "to":to,
348 },
348 },
349 };
349 };
350 var dataset = dataset;
350 var dataset = dataset;
351 var overview_dataset = [overview_dataset];
351 var overview_dataset = [overview_dataset];
352 var choiceContainer = YUD.get("legend_choices");
352 var choiceContainer = YUD.get("legend_choices");
353 var choiceContainerTable = YUD.get("legend_choices_tables");
353 var choiceContainerTable = YUD.get("legend_choices_tables");
354 var plotContainer = YUD.get('commit_history');
354 var plotContainer = YUD.get('commit_history');
355 var overviewContainer = YUD.get('overview');
355 var overviewContainer = YUD.get('overview');
356
356
357 var plot_options = {
357 var plot_options = {
358 bars: {show:true,align:'center',lineWidth:4},
358 bars: {show:true,align:'center',lineWidth:4},
359 legend: {show:true, container:"legend_container"},
359 legend: {show:true, container:"legend_container"},
360 points: {show:true,radius:0,fill:false},
360 points: {show:true,radius:0,fill:false},
361 yaxis: {tickDecimals:0,},
361 yaxis: {tickDecimals:0,},
362 xaxis: {
362 xaxis: {
363 mode: "time",
363 mode: "time",
364 timeformat: "%d/%m",
364 timeformat: "%d/%m",
365 min:from,
365 min:from,
366 max:to,
366 max:to,
367 },
367 },
368 grid: {
368 grid: {
369 hoverable: true,
369 hoverable: true,
370 clickable: true,
370 clickable: true,
371 autoHighlight:true,
371 autoHighlight:true,
372 color: "#999"
372 color: "#999"
373 },
373 },
374 //selection: {mode: "x"}
374 //selection: {mode: "x"}
375 };
375 };
376 var overview_options = {
376 var overview_options = {
377 legend:{show:false},
377 legend:{show:false},
378 bars: {show:true,barWidth: 2,},
378 bars: {show:true,barWidth: 2,},
379 shadowSize: 0,
379 shadowSize: 0,
380 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
380 xaxis: {mode: "time", timeformat: "%d/%m/%y",},
381 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
381 yaxis: {ticks: 3, min: 0,tickDecimals:0,},
382 grid: {color: "#999",},
382 grid: {color: "#999",},
383 selection: {mode: "x"}
383 selection: {mode: "x"}
384 };
384 };
385
385
386 /**
386 /**
387 *get dummy data needed in few places
387 *get dummy data needed in few places
388 */
388 */
389 function getDummyData(label){
389 function getDummyData(label){
390 return {"label":label,
390 return {"label":label,
391 "data":[{"time":0,
391 "data":[{"time":0,
392 "commits":0,
392 "commits":0,
393 "added":0,
393 "added":0,
394 "changed":0,
394 "changed":0,
395 "removed":0,
395 "removed":0,
396 }],
396 }],
397 "schema":["commits"],
397 "schema":["commits"],
398 "color":'#ffffff',
398 "color":'#ffffff',
399 }
399 }
400 }
400 }
401
401
402 /**
402 /**
403 * generate checkboxes accordindly to data
403 * generate checkboxes accordindly to data
404 * @param keys
404 * @param keys
405 * @returns
405 * @returns
406 */
406 */
407 function generateCheckboxes(data) {
407 function generateCheckboxes(data) {
408 //append checkboxes
408 //append checkboxes
409 var i = 0;
409 var i = 0;
410 choiceContainerTable.innerHTML = '';
410 choiceContainerTable.innerHTML = '';
411 for(var pos in data) {
411 for(var pos in data) {
412
412
413 data[pos].color = i;
413 data[pos].color = i;
414 i++;
414 i++;
415 if(data[pos].label != ''){
415 if(data[pos].label != ''){
416 choiceContainerTable.innerHTML += '<tr><td>'+
416 choiceContainerTable.innerHTML += '<tr><td>'+
417 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
417 '<input type="checkbox" name="' + data[pos].label +'" checked="checked" />'
418 +data[pos].label+
418 +data[pos].label+
419 '</td></tr>';
419 '</td></tr>';
420 }
420 }
421 }
421 }
422 }
422 }
423
423
424 /**
424 /**
425 * ToolTip show
425 * ToolTip show
426 */
426 */
427 function showTooltip(x, y, contents) {
427 function showTooltip(x, y, contents) {
428 var div=document.getElementById('tooltip');
428 var div=document.getElementById('tooltip');
429 if(!div) {
429 if(!div) {
430 div = document.createElement('div');
430 div = document.createElement('div');
431 div.id="tooltip";
431 div.id="tooltip";
432 div.style.position="absolute";
432 div.style.position="absolute";
433 div.style.border='1px solid #fdd';
433 div.style.border='1px solid #fdd';
434 div.style.padding='2px';
434 div.style.padding='2px';
435 div.style.backgroundColor='#fee';
435 div.style.backgroundColor='#fee';
436 document.body.appendChild(div);
436 document.body.appendChild(div);
437 }
437 }
438 YUD.setStyle(div, 'opacity', 0);
438 YUD.setStyle(div, 'opacity', 0);
439 div.innerHTML = contents;
439 div.innerHTML = contents;
440 div.style.top=(y + 5) + "px";
440 div.style.top=(y + 5) + "px";
441 div.style.left=(x + 5) + "px";
441 div.style.left=(x + 5) + "px";
442
442
443 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
443 var anim = new YAHOO.util.Anim(div, {opacity: {to: 0.8}}, 0.2);
444 anim.animate();
444 anim.animate();
445 }
445 }
446
446
447 /**
447 /**
448 * This function will detect if selected period has some changesets
448 * This function will detect if selected period has some changesets
449 for this user if it does this data is then pushed for displaying
449 for this user if it does this data is then pushed for displaying
450 Additionally it will only display users that are selected by the checkbox
450 Additionally it will only display users that are selected by the checkbox
451 */
451 */
452 function getDataAccordingToRanges(ranges) {
452 function getDataAccordingToRanges(ranges) {
453
453
454 var data = [];
454 var data = [];
455 var new_dataset = {};
455 var new_dataset = {};
456 var keys = [];
456 var keys = [];
457 var max_commits = 0;
457 var max_commits = 0;
458 for(var key in dataset){
458 for(var key in dataset){
459
459
460 for(var ds in dataset[key].data){
460 for(var ds in dataset[key].data){
461 commit_data = dataset[key].data[ds];
461 commit_data = dataset[key].data[ds];
462 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
462 if (commit_data.time >= ranges.xaxis.from && commit_data.time <= ranges.xaxis.to){
463
463
464 if(new_dataset[key] === undefined){
464 if(new_dataset[key] === undefined){
465 new_dataset[key] = {data:[],schema:["commits"],label:key};
465 new_dataset[key] = {data:[],schema:["commits"],label:key};
466 }
466 }
467 new_dataset[key].data.push(commit_data);
467 new_dataset[key].data.push(commit_data);
468 }
468 }
469 }
469 }
470 if (new_dataset[key] !== undefined){
470 if (new_dataset[key] !== undefined){
471 data.push(new_dataset[key]);
471 data.push(new_dataset[key]);
472 }
472 }
473 }
473 }
474
474
475 if (data.length > 0){
475 if (data.length > 0){
476 return data;
476 return data;
477 }
477 }
478 else{
478 else{
479 //just return dummy data for graph to plot itself
479 //just return dummy data for graph to plot itself
480 return [getDummyData('')];
480 return [getDummyData('')];
481 }
481 }
482 }
482 }
483
483
484 /**
484 /**
485 * redraw using new checkbox data
485 * redraw using new checkbox data
486 */
486 */
487 function plotchoiced(e,args){
487 function plotchoiced(e,args){
488 var cur_data = args[0];
488 var cur_data = args[0];
489 var cur_ranges = args[1];
489 var cur_ranges = args[1];
490
490
491 var new_data = [];
491 var new_data = [];
492 var inputs = choiceContainer.getElementsByTagName("input");
492 var inputs = choiceContainer.getElementsByTagName("input");
493
493
494 //show only checked labels
494 //show only checked labels
495 for(var i=0; i<inputs.length; i++) {
495 for(var i=0; i<inputs.length; i++) {
496 var checkbox_key = inputs[i].name;
496 var checkbox_key = inputs[i].name;
497
497
498 if(inputs[i].checked){
498 if(inputs[i].checked){
499 for(var d in cur_data){
499 for(var d in cur_data){
500 if(cur_data[d].label == checkbox_key){
500 if(cur_data[d].label == checkbox_key){
501 new_data.push(cur_data[d]);
501 new_data.push(cur_data[d]);
502 }
502 }
503 }
503 }
504 }
504 }
505 else{
505 else{
506 //push dummy data to not hide the label
506 //push dummy data to not hide the label
507 new_data.push(getDummyData(checkbox_key));
507 new_data.push(getDummyData(checkbox_key));
508 }
508 }
509 }
509 }
510
510
511 var new_options = YAHOO.lang.merge(plot_options, {
511 var new_options = YAHOO.lang.merge(plot_options, {
512 xaxis: {
512 xaxis: {
513 min: cur_ranges.xaxis.from,
513 min: cur_ranges.xaxis.from,
514 max: cur_ranges.xaxis.to,
514 max: cur_ranges.xaxis.to,
515 mode:"time",
515 mode:"time",
516 timeformat: "%d/%m",
516 timeformat: "%d/%m",
517 },
517 },
518 });
518 });
519 if (!new_data){
519 if (!new_data){
520 new_data = [[0,1]];
520 new_data = [[0,1]];
521 }
521 }
522 // do the zooming
522 // do the zooming
523 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
523 plot = YAHOO.widget.Flot(plotContainer, new_data, new_options);
524
524
525 plot.subscribe("plotselected", plotselected);
525 plot.subscribe("plotselected", plotselected);
526
526
527 //resubscribe plothover
527 //resubscribe plothover
528 plot.subscribe("plothover", plothover);
528 plot.subscribe("plothover", plothover);
529
529
530 // don't fire event on the overview to prevent eternal loop
530 // don't fire event on the overview to prevent eternal loop
531 overview.setSelection(cur_ranges, true);
531 overview.setSelection(cur_ranges, true);
532
532
533 }
533 }
534
534
535 /**
535 /**
536 * plot only selected items from overview
536 * plot only selected items from overview
537 * @param ranges
537 * @param ranges
538 * @returns
538 * @returns
539 */
539 */
540 function plotselected(ranges,cur_data) {
540 function plotselected(ranges,cur_data) {
541 //updates the data for new plot
541 //updates the data for new plot
542 var data = getDataAccordingToRanges(ranges);
542 var data = getDataAccordingToRanges(ranges);
543 generateCheckboxes(data);
543 generateCheckboxes(data);
544
544
545 var new_options = YAHOO.lang.merge(plot_options, {
545 var new_options = YAHOO.lang.merge(plot_options, {
546 xaxis: {
546 xaxis: {
547 min: ranges.xaxis.from,
547 min: ranges.xaxis.from,
548 max: ranges.xaxis.to,
548 max: ranges.xaxis.to,
549 mode:"time",
549 mode:"time",
550 timeformat: "%d/%m",
550 timeformat: "%d/%m",
551 },
551 },
552 });
552 });
553 // do the zooming
553 // do the zooming
554 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
554 plot = YAHOO.widget.Flot(plotContainer, data, new_options);
555
555
556 plot.subscribe("plotselected", plotselected);
556 plot.subscribe("plotselected", plotselected);
557
557
558 //resubscribe plothover
558 //resubscribe plothover
559 plot.subscribe("plothover", plothover);
559 plot.subscribe("plothover", plothover);
560
560
561 // don't fire event on the overview to prevent eternal loop
561 // don't fire event on the overview to prevent eternal loop
562 overview.setSelection(ranges, true);
562 overview.setSelection(ranges, true);
563
563
564 //resubscribe choiced
564 //resubscribe choiced
565 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
565 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, ranges]);
566 }
566 }
567
567
568 var previousPoint = null;
568 var previousPoint = null;
569
569
570 function plothover(o) {
570 function plothover(o) {
571 var pos = o.pos;
571 var pos = o.pos;
572 var item = o.item;
572 var item = o.item;
573
573
574 //YUD.get("x").innerHTML = pos.x.toFixed(2);
574 //YUD.get("x").innerHTML = pos.x.toFixed(2);
575 //YUD.get("y").innerHTML = pos.y.toFixed(2);
575 //YUD.get("y").innerHTML = pos.y.toFixed(2);
576 if (item) {
576 if (item) {
577 if (previousPoint != item.datapoint) {
577 if (previousPoint != item.datapoint) {
578 previousPoint = item.datapoint;
578 previousPoint = item.datapoint;
579
579
580 var tooltip = YUD.get("tooltip");
580 var tooltip = YUD.get("tooltip");
581 if(tooltip) {
581 if(tooltip) {
582 tooltip.parentNode.removeChild(tooltip);
582 tooltip.parentNode.removeChild(tooltip);
583 }
583 }
584 var x = item.datapoint.x.toFixed(2);
584 var x = item.datapoint.x.toFixed(2);
585 var y = item.datapoint.y.toFixed(2);
585 var y = item.datapoint.y.toFixed(2);
586
586
587 if (!item.series.label){
587 if (!item.series.label){
588 item.series.label = 'commits';
588 item.series.label = 'commits';
589 }
589 }
590 var d = new Date(x*1000);
590 var d = new Date(x*1000);
591 var fd = d.toDateString()
591 var fd = d.toDateString()
592 var nr_commits = parseInt(y);
592 var nr_commits = parseInt(y);
593
593
594 var cur_data = dataset[item.series.label].data[item.dataIndex];
594 var cur_data = dataset[item.series.label].data[item.dataIndex];
595 var added = cur_data.added;
595 var added = cur_data.added;
596 var changed = cur_data.changed;
596 var changed = cur_data.changed;
597 var removed = cur_data.removed;
597 var removed = cur_data.removed;
598
598
599 var nr_commits_suffix = " ${_('commits')} ";
599 var nr_commits_suffix = " ${_('commits')} ";
600 var added_suffix = " ${_('files added')} ";
600 var added_suffix = " ${_('files added')} ";
601 var changed_suffix = " ${_('files changed')} ";
601 var changed_suffix = " ${_('files changed')} ";
602 var removed_suffix = " ${_('files removed')} ";
602 var removed_suffix = " ${_('files removed')} ";
603
603
604
604
605 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
605 if(nr_commits == 1){nr_commits_suffix = " ${_('commit')} ";}
606 if(added==1){added_suffix=" ${_('file added')} ";}
606 if(added==1){added_suffix=" ${_('file added')} ";}
607 if(changed==1){changed_suffix=" ${_('file changed')} ";}
607 if(changed==1){changed_suffix=" ${_('file changed')} ";}
608 if(removed==1){removed_suffix=" ${_('file removed')} ";}
608 if(removed==1){removed_suffix=" ${_('file removed')} ";}
609
609
610 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
610 showTooltip(item.pageX, item.pageY, item.series.label + " on " + fd
611 +'<br/>'+
611 +'<br/>'+
612 nr_commits + nr_commits_suffix+'<br/>'+
612 nr_commits + nr_commits_suffix+'<br/>'+
613 added + added_suffix +'<br/>'+
613 added + added_suffix +'<br/>'+
614 changed + changed_suffix + '<br/>'+
614 changed + changed_suffix + '<br/>'+
615 removed + removed_suffix + '<br/>');
615 removed + removed_suffix + '<br/>');
616 }
616 }
617 }
617 }
618 else {
618 else {
619 var tooltip = YUD.get("tooltip");
619 var tooltip = YUD.get("tooltip");
620
620
621 if(tooltip) {
621 if(tooltip) {
622 tooltip.parentNode.removeChild(tooltip);
622 tooltip.parentNode.removeChild(tooltip);
623 }
623 }
624 previousPoint = null;
624 previousPoint = null;
625 }
625 }
626 }
626 }
627
627
628 /**
628 /**
629 * MAIN EXECUTION
629 * MAIN EXECUTION
630 */
630 */
631
631
632 var data = getDataAccordingToRanges(initial_ranges);
632 var data = getDataAccordingToRanges(initial_ranges);
633 generateCheckboxes(data);
633 generateCheckboxes(data);
634
634
635 //main plot
635 //main plot
636 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
636 var plot = YAHOO.widget.Flot(plotContainer,data,plot_options);
637
637
638 //overview
638 //overview
639 var overview = YAHOO.widget.Flot(overviewContainer,
639 var overview = YAHOO.widget.Flot(overviewContainer,
640 overview_dataset, overview_options);
640 overview_dataset, overview_options);
641
641
642 //show initial selection on overview
642 //show initial selection on overview
643 overview.setSelection(initial_ranges);
643 overview.setSelection(initial_ranges);
644
644
645 plot.subscribe("plotselected", plotselected);
645 plot.subscribe("plotselected", plotselected);
646 plot.subscribe("plothover", plothover)
646 plot.subscribe("plothover", plothover)
647
647
648 overview.subscribe("plotselected", function (ranges) {
648 overview.subscribe("plotselected", function (ranges) {
649 plot.setSelection(ranges);
649 plot.setSelection(ranges);
650 });
650 });
651
651
652 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
652 YUE.on(choiceContainer.getElementsByTagName("input"), "click", plotchoiced, [data, initial_ranges]);
653 }
653 }
654 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
654 SummaryPlot(${c.ts_min},${c.ts_max},${c.commit_data|n},${c.overview_data|n});
655 </script>
655 </script>
656
656
657 </div>
657 </div>
658 </div>
658 </div>
659
659
660 <div class="box">
660 <div class="box">
661 <div class="title">
661 <div class="title">
662 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
662 <div class="breadcrumbs">${h.link_to(_('Shortlog'),h.url('shortlog_home',repo_name=c.repo_name))}</div>
663 </div>
663 </div>
664 <div class="table">
664 <div class="table">
665 <div id="shortlog_data">
665 <div id="shortlog_data">
666 <%include file='../shortlog/shortlog_data.html'/>
666 <%include file='../shortlog/shortlog_data.html'/>
667 </div>
667 </div>
668 ##%if c.repo_changesets:
668 ##%if c.repo_changesets:
669 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
669 ## ${h.link_to(_('show more'),h.url('changelog_home',repo_name=c.repo_name))}
670 ##%endif
670 ##%endif
671 </div>
671 </div>
672 </div>
672 </div>
673 <div class="box">
673 <div class="box">
674 <div class="title">
674 <div class="title">
675 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
675 <div class="breadcrumbs">${h.link_to(_('Tags'),h.url('tags_home',repo_name=c.repo_name))}</div>
676 </div>
676 </div>
677 <div class="table">
677 <div class="table">
678 <%include file='../tags/tags_data.html'/>
678 <%include file='../tags/tags_data.html'/>
679 %if c.repo_changesets:
679 %if c.repo_changesets:
680 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
680 ${h.link_to(_('show more'),h.url('tags_home',repo_name=c.repo_name))}
681 %endif
681 %endif
682 </div>
682 </div>
683 </div>
683 </div>
684 <div class="box">
684 <div class="box">
685 <div class="title">
685 <div class="title">
686 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
686 <div class="breadcrumbs">${h.link_to(_('Branches'),h.url('branches_home',repo_name=c.repo_name))}</div>
687 </div>
687 </div>
688 <div class="table">
688 <div class="table">
689 <%include file='../branches/branches_data.html'/>
689 <%include file='../branches/branches_data.html'/>
690 %if c.repo_changesets:
690 %if c.repo_changesets:
691 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
691 ${h.link_to(_('show more'),h.url('branches_home',repo_name=c.repo_name))}
692 %endif
692 %endif
693 </div>
693 </div>
694 </div>
694 </div>
695
695
696 </%def>
696 </%def>
General Comments 0
You need to be logged in to leave comments. Login now