##// END OF EJS Templates
Added a translatable date formatter for every date displayed.
Vincent Duvert -
r2416:44f328d6 beta
parent child Browse files
Show More
@@ -1,972 +1,980
1 1 """Helper functions
2 2
3 3 Consists of functions to typically be used within templates, but also
4 4 available to Controllers. This module is available to both as 'h'.
5 5 """
6 6 import random
7 7 import hashlib
8 8 import StringIO
9 9 import urllib
10 10 import math
11 11 import logging
12 12
13 13 from datetime import datetime
14 14 from pygments.formatters.html import HtmlFormatter
15 15 from pygments import highlight as code_highlight
16 16 from pylons import url, request, config
17 17 from pylons.i18n.translation import _, ungettext
18 18 from hashlib import md5
19 19
20 20 from webhelpers.html import literal, HTML, escape
21 21 from webhelpers.html.tools import *
22 22 from webhelpers.html.builder import make_tag
23 23 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
24 24 end_form, file, form, hidden, image, javascript_link, link_to, \
25 25 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
26 26 submit, text, password, textarea, title, ul, xml_declaration, radio
27 27 from webhelpers.html.tools import auto_link, button_to, highlight, \
28 28 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
29 29 from webhelpers.number import format_byte_size, format_bit_size
30 30 from webhelpers.pylonslib import Flash as _Flash
31 31 from webhelpers.pylonslib.secure_form import secure_form
32 32 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
33 33 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
34 34 replace_whitespace, urlify, truncate, wrap_paragraphs
35 35 from webhelpers.date import time_ago_in_words
36 36 from webhelpers.paginate import Page
37 37 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
38 38 convert_boolean_attrs, NotGiven, _make_safe_id_component
39 39
40 40 from rhodecode.lib.annotate import annotate_highlight
41 41 from rhodecode.lib.utils import repo_name_slug
42 42 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
43 43 get_changeset_safe
44 44 from rhodecode.lib.markup_renderer import MarkupRenderer
45 45 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
46 46 from rhodecode.lib.vcs.backends.base import BaseChangeset
47 47 from rhodecode.model.db import URL_SEP
48 48
49 49 log = logging.getLogger(__name__)
50 50
51 51
52 52 def shorter(text, size=20):
53 53 postfix = '...'
54 54 if len(text) > size:
55 55 return text[:size - len(postfix)] + postfix
56 56 return text
57 57
58 58
59 59 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
60 60 """
61 61 Reset button
62 62 """
63 63 _set_input_attrs(attrs, type, name, value)
64 64 _set_id_attr(attrs, id, name)
65 65 convert_boolean_attrs(attrs, ["disabled"])
66 66 return HTML.input(**attrs)
67 67
68 68 reset = _reset
69 69 safeid = _make_safe_id_component
70 70
71 71
72 72 def FID(raw_id, path):
73 73 """
74 74 Creates a uniqe ID for filenode based on it's hash of path and revision
75 75 it's safe to use in urls
76 76
77 77 :param raw_id:
78 78 :param path:
79 79 """
80 80
81 81 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
82 82
83 83
84 84 def get_token():
85 85 """Return the current authentication token, creating one if one doesn't
86 86 already exist.
87 87 """
88 88 token_key = "_authentication_token"
89 89 from pylons import session
90 90 if not token_key in session:
91 91 try:
92 92 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
93 93 except AttributeError: # Python < 2.4
94 94 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
95 95 session[token_key] = token
96 96 if hasattr(session, 'save'):
97 97 session.save()
98 98 return session[token_key]
99 99
100 100
101 101 class _GetError(object):
102 102 """Get error from form_errors, and represent it as span wrapped error
103 103 message
104 104
105 105 :param field_name: field to fetch errors for
106 106 :param form_errors: form errors dict
107 107 """
108 108
109 109 def __call__(self, field_name, form_errors):
110 110 tmpl = """<span class="error_msg">%s</span>"""
111 111 if form_errors and form_errors.has_key(field_name):
112 112 return literal(tmpl % form_errors.get(field_name))
113 113
114 114 get_error = _GetError()
115 115
116 116
117 117 class _ToolTip(object):
118 118
119 119 def __call__(self, tooltip_title, trim_at=50):
120 120 """Special function just to wrap our text into nice formatted
121 121 autowrapped text
122 122
123 123 :param tooltip_title:
124 124 """
125 125 return escape(tooltip_title)
126 126 tooltip = _ToolTip()
127 127
128 128
129 129 class _FilesBreadCrumbs(object):
130 130
131 131 def __call__(self, repo_name, rev, paths):
132 132 if isinstance(paths, str):
133 133 paths = safe_unicode(paths)
134 134 url_l = [link_to(repo_name, url('files_home',
135 135 repo_name=repo_name,
136 136 revision=rev, f_path=''))]
137 137 paths_l = paths.split('/')
138 138 for cnt, p in enumerate(paths_l):
139 139 if p != '':
140 140 url_l.append(link_to(p,
141 141 url('files_home',
142 142 repo_name=repo_name,
143 143 revision=rev,
144 144 f_path='/'.join(paths_l[:cnt + 1])
145 145 )
146 146 )
147 147 )
148 148
149 149 return literal('/'.join(url_l))
150 150
151 151 files_breadcrumbs = _FilesBreadCrumbs()
152 152
153 153
154 154 class CodeHtmlFormatter(HtmlFormatter):
155 155 """
156 156 My code Html Formatter for source codes
157 157 """
158 158
159 159 def wrap(self, source, outfile):
160 160 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
161 161
162 162 def _wrap_code(self, source):
163 163 for cnt, it in enumerate(source):
164 164 i, t = it
165 165 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
166 166 yield i, t
167 167
168 168 def _wrap_tablelinenos(self, inner):
169 169 dummyoutfile = StringIO.StringIO()
170 170 lncount = 0
171 171 for t, line in inner:
172 172 if t:
173 173 lncount += 1
174 174 dummyoutfile.write(line)
175 175
176 176 fl = self.linenostart
177 177 mw = len(str(lncount + fl - 1))
178 178 sp = self.linenospecial
179 179 st = self.linenostep
180 180 la = self.lineanchors
181 181 aln = self.anchorlinenos
182 182 nocls = self.noclasses
183 183 if sp:
184 184 lines = []
185 185
186 186 for i in range(fl, fl + lncount):
187 187 if i % st == 0:
188 188 if i % sp == 0:
189 189 if aln:
190 190 lines.append('<a href="#%s%d" class="special">%*d</a>' %
191 191 (la, i, mw, i))
192 192 else:
193 193 lines.append('<span class="special">%*d</span>' % (mw, i))
194 194 else:
195 195 if aln:
196 196 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
197 197 else:
198 198 lines.append('%*d' % (mw, i))
199 199 else:
200 200 lines.append('')
201 201 ls = '\n'.join(lines)
202 202 else:
203 203 lines = []
204 204 for i in range(fl, fl + lncount):
205 205 if i % st == 0:
206 206 if aln:
207 207 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
208 208 else:
209 209 lines.append('%*d' % (mw, i))
210 210 else:
211 211 lines.append('')
212 212 ls = '\n'.join(lines)
213 213
214 214 # in case you wonder about the seemingly redundant <div> here: since the
215 215 # content in the other cell also is wrapped in a div, some browsers in
216 216 # some configurations seem to mess up the formatting...
217 217 if nocls:
218 218 yield 0, ('<table class="%stable">' % self.cssclass +
219 219 '<tr><td><div class="linenodiv" '
220 220 'style="background-color: #f0f0f0; padding-right: 10px">'
221 221 '<pre style="line-height: 125%">' +
222 222 ls + '</pre></div></td><td id="hlcode" class="code">')
223 223 else:
224 224 yield 0, ('<table class="%stable">' % self.cssclass +
225 225 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
226 226 ls + '</pre></div></td><td id="hlcode" class="code">')
227 227 yield 0, dummyoutfile.getvalue()
228 228 yield 0, '</td></tr></table>'
229 229
230 230
231 231 def pygmentize(filenode, **kwargs):
232 232 """pygmentize function using pygments
233 233
234 234 :param filenode:
235 235 """
236 236
237 237 return literal(code_highlight(filenode.content,
238 238 filenode.lexer, CodeHtmlFormatter(**kwargs)))
239 239
240 240
241 241 def pygmentize_annotation(repo_name, filenode, **kwargs):
242 242 """
243 243 pygmentize function for annotation
244 244
245 245 :param filenode:
246 246 """
247 247
248 248 color_dict = {}
249 249
250 250 def gen_color(n=10000):
251 251 """generator for getting n of evenly distributed colors using
252 252 hsv color and golden ratio. It always return same order of colors
253 253
254 254 :returns: RGB tuple
255 255 """
256 256
257 257 def hsv_to_rgb(h, s, v):
258 258 if s == 0.0:
259 259 return v, v, v
260 260 i = int(h * 6.0) # XXX assume int() truncates!
261 261 f = (h * 6.0) - i
262 262 p = v * (1.0 - s)
263 263 q = v * (1.0 - s * f)
264 264 t = v * (1.0 - s * (1.0 - f))
265 265 i = i % 6
266 266 if i == 0:
267 267 return v, t, p
268 268 if i == 1:
269 269 return q, v, p
270 270 if i == 2:
271 271 return p, v, t
272 272 if i == 3:
273 273 return p, q, v
274 274 if i == 4:
275 275 return t, p, v
276 276 if i == 5:
277 277 return v, p, q
278 278
279 279 golden_ratio = 0.618033988749895
280 280 h = 0.22717784590367374
281 281
282 282 for _ in xrange(n):
283 283 h += golden_ratio
284 284 h %= 1
285 285 HSV_tuple = [h, 0.95, 0.95]
286 286 RGB_tuple = hsv_to_rgb(*HSV_tuple)
287 287 yield map(lambda x: str(int(x * 256)), RGB_tuple)
288 288
289 289 cgenerator = gen_color()
290 290
291 291 def get_color_string(cs):
292 292 if cs in color_dict:
293 293 col = color_dict[cs]
294 294 else:
295 295 col = color_dict[cs] = cgenerator.next()
296 296 return "color: rgb(%s)! important;" % (', '.join(col))
297 297
298 298 def url_func(repo_name):
299 299
300 300 def _url_func(changeset):
301 301 author = changeset.author
302 302 date = changeset.date
303 303 message = tooltip(changeset.message)
304 304
305 305 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
306 306 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
307 307 "</b> %s<br/></div>")
308 308
309 309 tooltip_html = tooltip_html % (author, date, message)
310 310 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
311 311 short_id(changeset.raw_id))
312 312 uri = link_to(
313 313 lnk_format,
314 314 url('changeset_home', repo_name=repo_name,
315 315 revision=changeset.raw_id),
316 316 style=get_color_string(changeset.raw_id),
317 317 class_='tooltip',
318 318 title=tooltip_html
319 319 )
320 320
321 321 uri += '\n'
322 322 return uri
323 323 return _url_func
324 324
325 325 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
326 326
327 327
328 328 def is_following_repo(repo_name, user_id):
329 329 from rhodecode.model.scm import ScmModel
330 330 return ScmModel().is_following_repo(repo_name, user_id)
331 331
332 332 flash = _Flash()
333 333
334 334 #==============================================================================
335 335 # SCM FILTERS available via h.
336 336 #==============================================================================
337 337 from rhodecode.lib.vcs.utils import author_name, author_email
338 338 from rhodecode.lib.utils2 import credentials_filter, age as _age
339 339 from rhodecode.model.db import User
340 340
341 341 age = lambda x: _age(x)
342 342 capitalize = lambda x: x.capitalize()
343 343 email = author_email
344 344 short_id = lambda x: x[:12]
345 345 hide_credentials = lambda x: ''.join(credentials_filter(x))
346 346
347 347
348 def fmt_date(date):
349 if date:
350 return (date.strftime(_(u"%a, %d %b %Y %H:%M:%S").encode('utf8'))
351 .decode('utf8'))
352
353 return ""
354
355
348 356 def is_git(repository):
349 357 if hasattr(repository, 'alias'):
350 358 _type = repository.alias
351 359 elif hasattr(repository, 'repo_type'):
352 360 _type = repository.repo_type
353 361 else:
354 362 _type = repository
355 363 return _type == 'git'
356 364
357 365
358 366 def is_hg(repository):
359 367 if hasattr(repository, 'alias'):
360 368 _type = repository.alias
361 369 elif hasattr(repository, 'repo_type'):
362 370 _type = repository.repo_type
363 371 else:
364 372 _type = repository
365 373 return _type == 'hg'
366 374
367 375
368 376 def email_or_none(author):
369 377 _email = email(author)
370 378 if _email != '':
371 379 return _email
372 380
373 381 # See if it contains a username we can get an email from
374 382 user = User.get_by_username(author_name(author), case_insensitive=True,
375 383 cache=True)
376 384 if user is not None:
377 385 return user.email
378 386
379 387 # No valid email, not a valid user in the system, none!
380 388 return None
381 389
382 390
383 391 def person(author):
384 392 # attr to return from fetched user
385 393 person_getter = lambda usr: usr.username
386 394
387 395 # Valid email in the attribute passed, see if they're in the system
388 396 _email = email(author)
389 397 if _email != '':
390 398 user = User.get_by_email(_email, case_insensitive=True, cache=True)
391 399 if user is not None:
392 400 return person_getter(user)
393 401 return _email
394 402
395 403 # Maybe it's a username?
396 404 _author = author_name(author)
397 405 user = User.get_by_username(_author, case_insensitive=True,
398 406 cache=True)
399 407 if user is not None:
400 408 return person_getter(user)
401 409
402 410 # Still nothing? Just pass back the author name then
403 411 return _author
404 412
405 413
406 414 def bool2icon(value):
407 415 """Returns True/False values represented as small html image of true/false
408 416 icons
409 417
410 418 :param value: bool value
411 419 """
412 420
413 421 if value is True:
414 422 return HTML.tag('img', src=url("/images/icons/accept.png"),
415 423 alt=_('True'))
416 424
417 425 if value is False:
418 426 return HTML.tag('img', src=url("/images/icons/cancel.png"),
419 427 alt=_('False'))
420 428
421 429 return value
422 430
423 431
424 432 def action_parser(user_log, feed=False):
425 433 """
426 434 This helper will action_map the specified string action into translated
427 435 fancy names with icons and links
428 436
429 437 :param user_log: user log instance
430 438 :param feed: use output for feeds (no html and fancy icons)
431 439 """
432 440
433 441 action = user_log.action
434 442 action_params = ' '
435 443
436 444 x = action.split(':')
437 445
438 446 if len(x) > 1:
439 447 action, action_params = x
440 448
441 449 def get_cs_links():
442 450 revs_limit = 3 # display this amount always
443 451 revs_top_limit = 50 # show upto this amount of changesets hidden
444 452 revs_ids = action_params.split(',')
445 453 deleted = user_log.repository is None
446 454 if deleted:
447 455 return ','.join(revs_ids)
448 456
449 457 repo_name = user_log.repository.repo_name
450 458
451 459 repo = user_log.repository.scm_instance
452 460
453 461 def lnk(rev, repo_name):
454 462
455 463 if isinstance(rev, BaseChangeset):
456 464 lbl = 'r%s:%s' % (rev.revision, rev.short_id)
457 465 _url = url('changeset_home', repo_name=repo_name,
458 466 revision=rev.raw_id)
459 467 title = tooltip(rev.message)
460 468 else:
461 469 lbl = '%s' % rev
462 470 _url = '#'
463 471 title = _('Changeset not found')
464 472
465 473 return link_to(lbl, _url, title=title, class_='tooltip',)
466 474
467 475 revs = []
468 476 if len(filter(lambda v: v != '', revs_ids)) > 0:
469 477 for rev in revs_ids[:revs_top_limit]:
470 478 try:
471 479 rev = repo.get_changeset(rev)
472 480 revs.append(rev)
473 481 except ChangesetDoesNotExistError:
474 482 log.error('cannot find revision %s in this repo' % rev)
475 483 revs.append(rev)
476 484 continue
477 485 cs_links = []
478 486 cs_links.append(" " + ', '.join(
479 487 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
480 488 )
481 489 )
482 490
483 491 compare_view = (
484 492 ' <div class="compare_view tooltip" title="%s">'
485 493 '<a href="%s">%s</a> </div>' % (
486 494 _('Show all combined changesets %s->%s') % (
487 495 revs_ids[0], revs_ids[-1]
488 496 ),
489 497 url('changeset_home', repo_name=repo_name,
490 498 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
491 499 ),
492 500 _('compare view')
493 501 )
494 502 )
495 503
496 504 # if we have exactly one more than normally displayed
497 505 # just display it, takes less space than displaying
498 506 # "and 1 more revisions"
499 507 if len(revs_ids) == revs_limit + 1:
500 508 rev = revs[revs_limit]
501 509 cs_links.append(", " + lnk(rev, repo_name))
502 510
503 511 # hidden-by-default ones
504 512 if len(revs_ids) > revs_limit + 1:
505 513 uniq_id = revs_ids[0]
506 514 html_tmpl = (
507 515 '<span> %s <a class="show_more" id="_%s" '
508 516 'href="#more">%s</a> %s</span>'
509 517 )
510 518 if not feed:
511 519 cs_links.append(html_tmpl % (
512 520 _('and'),
513 521 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
514 522 _('revisions')
515 523 )
516 524 )
517 525
518 526 if not feed:
519 527 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
520 528 else:
521 529 html_tmpl = '<span id="%s"> %s </span>'
522 530
523 531 morelinks = ', '.join(
524 532 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
525 533 )
526 534
527 535 if len(revs_ids) > revs_top_limit:
528 536 morelinks += ', ...'
529 537
530 538 cs_links.append(html_tmpl % (uniq_id, morelinks))
531 539 if len(revs) > 1:
532 540 cs_links.append(compare_view)
533 541 return ''.join(cs_links)
534 542
535 543 def get_fork_name():
536 544 repo_name = action_params
537 545 return _('fork name ') + str(link_to(action_params, url('summary_home',
538 546 repo_name=repo_name,)))
539 547
540 548 def get_user_name():
541 549 user_name = action_params
542 550 return user_name
543 551
544 552 def get_users_group():
545 553 group_name = action_params
546 554 return group_name
547 555
548 556 # action : translated str, callback(extractor), icon
549 557 action_map = {
550 558 'user_deleted_repo': (_('[deleted] repository'),
551 559 None, 'database_delete.png'),
552 560 'user_created_repo': (_('[created] repository'),
553 561 None, 'database_add.png'),
554 562 'user_created_fork': (_('[created] repository as fork'),
555 563 None, 'arrow_divide.png'),
556 564 'user_forked_repo': (_('[forked] repository'),
557 565 get_fork_name, 'arrow_divide.png'),
558 566 'user_updated_repo': (_('[updated] repository'),
559 567 None, 'database_edit.png'),
560 568 'admin_deleted_repo': (_('[delete] repository'),
561 569 None, 'database_delete.png'),
562 570 'admin_created_repo': (_('[created] repository'),
563 571 None, 'database_add.png'),
564 572 'admin_forked_repo': (_('[forked] repository'),
565 573 None, 'arrow_divide.png'),
566 574 'admin_updated_repo': (_('[updated] repository'),
567 575 None, 'database_edit.png'),
568 576 'admin_created_user': (_('[created] user'),
569 577 get_user_name, 'user_add.png'),
570 578 'admin_updated_user': (_('[updated] user'),
571 579 get_user_name, 'user_edit.png'),
572 580 'admin_created_users_group': (_('[created] users group'),
573 581 get_users_group, 'group_add.png'),
574 582 'admin_updated_users_group': (_('[updated] users group'),
575 583 get_users_group, 'group_edit.png'),
576 584 'user_commented_revision': (_('[commented] on revision in repository'),
577 585 get_cs_links, 'comment_add.png'),
578 586 'push': (_('[pushed] into'),
579 587 get_cs_links, 'script_add.png'),
580 588 'push_local': (_('[committed via RhodeCode] into repository'),
581 589 get_cs_links, 'script_edit.png'),
582 590 'push_remote': (_('[pulled from remote] into repository'),
583 591 get_cs_links, 'connect.png'),
584 592 'pull': (_('[pulled] from'),
585 593 None, 'down_16.png'),
586 594 'started_following_repo': (_('[started following] repository'),
587 595 None, 'heart_add.png'),
588 596 'stopped_following_repo': (_('[stopped following] repository'),
589 597 None, 'heart_delete.png'),
590 598 }
591 599
592 600 action_str = action_map.get(action, action)
593 601 if feed:
594 602 action = action_str[0].replace('[', '').replace(']', '')
595 603 else:
596 604 action = action_str[0]\
597 605 .replace('[', '<span class="journal_highlight">')\
598 606 .replace(']', '</span>')
599 607
600 608 action_params_func = lambda: ""
601 609
602 610 if callable(action_str[1]):
603 611 action_params_func = action_str[1]
604 612
605 613 def action_parser_icon():
606 614 action = user_log.action
607 615 action_params = None
608 616 x = action.split(':')
609 617
610 618 if len(x) > 1:
611 619 action, action_params = x
612 620
613 621 tmpl = """<img src="%s%s" alt="%s"/>"""
614 622 ico = action_map.get(action, ['', '', ''])[2]
615 623 return literal(tmpl % ((url('/images/icons/')), ico, action))
616 624
617 625 # returned callbacks we need to call to get
618 626 return [lambda: literal(action), action_params_func, action_parser_icon]
619 627
620 628
621 629
622 630 #==============================================================================
623 631 # PERMS
624 632 #==============================================================================
625 633 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
626 634 HasRepoPermissionAny, HasRepoPermissionAll
627 635
628 636
629 637 #==============================================================================
630 638 # GRAVATAR URL
631 639 #==============================================================================
632 640
633 641 def gravatar_url(email_address, size=30):
634 642 if (not str2bool(config['app_conf'].get('use_gravatar')) or
635 643 not email_address or email_address == 'anonymous@rhodecode.org'):
636 644 f = lambda a, l: min(l, key=lambda x: abs(x - a))
637 645 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
638 646
639 647 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
640 648 default = 'identicon'
641 649 baseurl_nossl = "http://www.gravatar.com/avatar/"
642 650 baseurl_ssl = "https://secure.gravatar.com/avatar/"
643 651 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
644 652
645 653 if isinstance(email_address, unicode):
646 654 #hashlib crashes on unicode items
647 655 email_address = safe_str(email_address)
648 656 # construct the url
649 657 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
650 658 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
651 659
652 660 return gravatar_url
653 661
654 662
655 663 #==============================================================================
656 664 # REPO PAGER, PAGER FOR REPOSITORY
657 665 #==============================================================================
658 666 class RepoPage(Page):
659 667
660 668 def __init__(self, collection, page=1, items_per_page=20,
661 669 item_count=None, url=None, **kwargs):
662 670
663 671 """Create a "RepoPage" instance. special pager for paging
664 672 repository
665 673 """
666 674 self._url_generator = url
667 675
668 676 # Safe the kwargs class-wide so they can be used in the pager() method
669 677 self.kwargs = kwargs
670 678
671 679 # Save a reference to the collection
672 680 self.original_collection = collection
673 681
674 682 self.collection = collection
675 683
676 684 # The self.page is the number of the current page.
677 685 # The first page has the number 1!
678 686 try:
679 687 self.page = int(page) # make it int() if we get it as a string
680 688 except (ValueError, TypeError):
681 689 self.page = 1
682 690
683 691 self.items_per_page = items_per_page
684 692
685 693 # Unless the user tells us how many items the collections has
686 694 # we calculate that ourselves.
687 695 if item_count is not None:
688 696 self.item_count = item_count
689 697 else:
690 698 self.item_count = len(self.collection)
691 699
692 700 # Compute the number of the first and last available page
693 701 if self.item_count > 0:
694 702 self.first_page = 1
695 703 self.page_count = int(math.ceil(float(self.item_count) /
696 704 self.items_per_page))
697 705 self.last_page = self.first_page + self.page_count - 1
698 706
699 707 # Make sure that the requested page number is the range of
700 708 # valid pages
701 709 if self.page > self.last_page:
702 710 self.page = self.last_page
703 711 elif self.page < self.first_page:
704 712 self.page = self.first_page
705 713
706 714 # Note: the number of items on this page can be less than
707 715 # items_per_page if the last page is not full
708 716 self.first_item = max(0, (self.item_count) - (self.page *
709 717 items_per_page))
710 718 self.last_item = ((self.item_count - 1) - items_per_page *
711 719 (self.page - 1))
712 720
713 721 self.items = list(self.collection[self.first_item:self.last_item + 1])
714 722
715 723 # Links to previous and next page
716 724 if self.page > self.first_page:
717 725 self.previous_page = self.page - 1
718 726 else:
719 727 self.previous_page = None
720 728
721 729 if self.page < self.last_page:
722 730 self.next_page = self.page + 1
723 731 else:
724 732 self.next_page = None
725 733
726 734 # No items available
727 735 else:
728 736 self.first_page = None
729 737 self.page_count = 0
730 738 self.last_page = None
731 739 self.first_item = None
732 740 self.last_item = None
733 741 self.previous_page = None
734 742 self.next_page = None
735 743 self.items = []
736 744
737 745 # This is a subclass of the 'list' type. Initialise the list now.
738 746 list.__init__(self, reversed(self.items))
739 747
740 748
741 749 def changed_tooltip(nodes):
742 750 """
743 751 Generates a html string for changed nodes in changeset page.
744 752 It limits the output to 30 entries
745 753
746 754 :param nodes: LazyNodesGenerator
747 755 """
748 756 if nodes:
749 757 pref = ': <br/> '
750 758 suf = ''
751 759 if len(nodes) > 30:
752 760 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
753 761 return literal(pref + '<br/> '.join([safe_unicode(x.path)
754 762 for x in nodes[:30]]) + suf)
755 763 else:
756 764 return ': ' + _('No Files')
757 765
758 766
759 767 def repo_link(groups_and_repos):
760 768 """
761 769 Makes a breadcrumbs link to repo within a group
762 770 joins &raquo; on each group to create a fancy link
763 771
764 772 ex::
765 773 group >> subgroup >> repo
766 774
767 775 :param groups_and_repos:
768 776 """
769 777 groups, repo_name = groups_and_repos
770 778
771 779 if not groups:
772 780 return repo_name
773 781 else:
774 782 def make_link(group):
775 783 return link_to(group.name, url('repos_group_home',
776 784 group_name=group.group_name))
777 785 return literal(' &raquo; '.join(map(make_link, groups)) + \
778 786 " &raquo; " + repo_name)
779 787
780 788
781 789 def fancy_file_stats(stats):
782 790 """
783 791 Displays a fancy two colored bar for number of added/deleted
784 792 lines of code on file
785 793
786 794 :param stats: two element list of added/deleted lines of code
787 795 """
788 796
789 797 a, d, t = stats[0], stats[1], stats[0] + stats[1]
790 798 width = 100
791 799 unit = float(width) / (t or 1)
792 800
793 801 # needs > 9% of width to be visible or 0 to be hidden
794 802 a_p = max(9, unit * a) if a > 0 else 0
795 803 d_p = max(9, unit * d) if d > 0 else 0
796 804 p_sum = a_p + d_p
797 805
798 806 if p_sum > width:
799 807 #adjust the percentage to be == 100% since we adjusted to 9
800 808 if a_p > d_p:
801 809 a_p = a_p - (p_sum - width)
802 810 else:
803 811 d_p = d_p - (p_sum - width)
804 812
805 813 a_v = a if a > 0 else ''
806 814 d_v = d if d > 0 else ''
807 815
808 816 def cgen(l_type):
809 817 mapping = {'tr': 'top-right-rounded-corner-mid',
810 818 'tl': 'top-left-rounded-corner-mid',
811 819 'br': 'bottom-right-rounded-corner-mid',
812 820 'bl': 'bottom-left-rounded-corner-mid'}
813 821 map_getter = lambda x: mapping[x]
814 822
815 823 if l_type == 'a' and d_v:
816 824 #case when added and deleted are present
817 825 return ' '.join(map(map_getter, ['tl', 'bl']))
818 826
819 827 if l_type == 'a' and not d_v:
820 828 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
821 829
822 830 if l_type == 'd' and a_v:
823 831 return ' '.join(map(map_getter, ['tr', 'br']))
824 832
825 833 if l_type == 'd' and not a_v:
826 834 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
827 835
828 836 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
829 837 cgen('a'), a_p, a_v
830 838 )
831 839 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
832 840 cgen('d'), d_p, d_v
833 841 )
834 842 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
835 843
836 844
837 845 def urlify_text(text_):
838 846 import re
839 847
840 848 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
841 849 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
842 850
843 851 def url_func(match_obj):
844 852 url_full = match_obj.groups()[0]
845 853 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
846 854
847 855 return literal(url_pat.sub(url_func, text_))
848 856
849 857
850 858 def urlify_changesets(text_, repository):
851 859 """
852 860 Extract revision ids from changeset and make link from them
853 861
854 862 :param text_:
855 863 :param repository:
856 864 """
857 865 import re
858 866 URL_PAT = re.compile(r'([0-9a-fA-F]{12,})')
859 867
860 868 def url_func(match_obj):
861 869 rev = match_obj.groups()[0]
862 870 pref = ''
863 871 if match_obj.group().startswith(' '):
864 872 pref = ' '
865 873 tmpl = (
866 874 '%(pref)s<a class="%(cls)s" href="%(url)s">'
867 875 '%(rev)s'
868 876 '</a>'
869 877 )
870 878 return tmpl % {
871 879 'pref': pref,
872 880 'cls': 'revision-link',
873 881 'url': url('changeset_home', repo_name=repository, revision=rev),
874 882 'rev': rev,
875 883 }
876 884
877 885 newtext = URL_PAT.sub(url_func, text_)
878 886
879 887 return newtext
880 888
881 889
882 890 def urlify_commit(text_, repository=None, link_=None):
883 891 """
884 892 Parses given text message and makes proper links.
885 893 issues are linked to given issue-server, and rest is a changeset link
886 894 if link_ is given, in other case it's a plain text
887 895
888 896 :param text_:
889 897 :param repository:
890 898 :param link_: changeset link
891 899 """
892 900 import re
893 901 import traceback
894 902
895 903 def escaper(string):
896 904 return string.replace('<', '&lt;').replace('>', '&gt;')
897 905
898 906 def linkify_others(t, l):
899 907 urls = re.compile(r'(\<a.*?\<\/a\>)',)
900 908 links = []
901 909 for e in urls.split(t):
902 910 if not urls.match(e):
903 911 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
904 912 else:
905 913 links.append(e)
906 914
907 915 return ''.join(links)
908 916
909 917 # urlify changesets - extrac revisions and make link out of them
910 918 text_ = urlify_changesets(escaper(text_), repository)
911 919
912 920 try:
913 921 conf = config['app_conf']
914 922
915 923 URL_PAT = re.compile(r'%s' % conf.get('issue_pat'))
916 924
917 925 if URL_PAT:
918 926 ISSUE_SERVER_LNK = conf.get('issue_server_link')
919 927 ISSUE_PREFIX = conf.get('issue_prefix')
920 928
921 929 def url_func(match_obj):
922 930 pref = ''
923 931 if match_obj.group().startswith(' '):
924 932 pref = ' '
925 933
926 934 issue_id = ''.join(match_obj.groups())
927 935 tmpl = (
928 936 '%(pref)s<a class="%(cls)s" href="%(url)s">'
929 937 '%(issue-prefix)s%(id-repr)s'
930 938 '</a>'
931 939 )
932 940 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
933 941 if repository:
934 942 url = url.replace('{repo}', repository)
935 943 repo_name = repository.split(URL_SEP)[-1]
936 944 url = url.replace('{repo_name}', repo_name)
937 945 return tmpl % {
938 946 'pref': pref,
939 947 'cls': 'issue-tracker-link',
940 948 'url': url,
941 949 'id-repr': issue_id,
942 950 'issue-prefix': ISSUE_PREFIX,
943 951 'serv': ISSUE_SERVER_LNK,
944 952 }
945 953
946 954 newtext = URL_PAT.sub(url_func, text_)
947 955
948 956 if link_:
949 957 # wrap not links into final link => link_
950 958 newtext = linkify_others(newtext, link_)
951 959
952 960 return literal(newtext)
953 961 except:
954 962 log.error(traceback.format_exc())
955 963 pass
956 964
957 965 return text_
958 966
959 967
960 968 def rst(source):
961 969 return literal('<div class="rst-block">%s</div>' %
962 970 MarkupRenderer.rst(source))
963 971
964 972
965 973 def rst_w_mentions(source):
966 974 """
967 975 Wrapped rst renderer with @mention highlighting
968 976
969 977 :param source:
970 978 """
971 979 return literal('<div class="rst-block">%s</div>' %
972 980 MarkupRenderer.rst_with_mentions(source))
@@ -1,54 +1,54
1 1 ## -*- coding: utf-8 -*-
2 2 %if c.users_log:
3 3 <table>
4 4 <tr>
5 5 <th class="left">${_('Username')}</th>
6 6 <th class="left">${_('Action')}</th>
7 7 <th class="left">${_('Repository')}</th>
8 8 <th class="left">${_('Date')}</th>
9 9 <th class="left">${_('From IP')}</th>
10 10 </tr>
11 11
12 12 %for cnt,l in enumerate(c.users_log):
13 13 <tr class="parity${cnt%2}">
14 14 <td>${h.link_to(l.user.username,h.url('edit_user', id=l.user.user_id))}</td>
15 15 <td>${h.action_parser(l)[0]()}
16 16 <div class="journal_action_params">
17 17 ${h.literal(h.action_parser(l)[1]())}
18 18 </div>
19 19 </td>
20 20 <td>
21 21 %if l.repository is not None:
22 22 ${h.link_to(l.repository.repo_name,h.url('summary_home',repo_name=l.repository.repo_name))}
23 23 %else:
24 24 ${l.repository_name}
25 25 %endif
26 26 </td>
27 27
28 <td>${l.action_date}</td>
28 <td>${h.fmt_date(l.action_date)}</td>
29 29 <td>${l.user_ip}</td>
30 30 </tr>
31 31 %endfor
32 32 </table>
33 33
34 34 <script type="text/javascript">
35 35 YUE.onDOMReady(function(){
36 36 YUE.delegate("user_log","click",function(e, matchedEl, container){
37 37 ypjax(e.target.href,"user_log",function(){show_more_event();tooltip_activate();});
38 38 YUE.preventDefault(e);
39 39 },'.pager_link');
40 40
41 41 YUE.delegate("user_log","click",function(e,matchedEl,container){
42 42 var el = e.target;
43 43 YUD.setStyle(YUD.get(el.id.substring(1)),'display','');
44 44 YUD.setStyle(el.parentNode,'display','none');
45 45 },'.show_more');
46 46 });
47 47 </script>
48 48
49 49 <div class="pagination-wh pagination-left">
50 50 ${c.users_log.pager('$link_previous ~2~ $link_next')}
51 51 </div>
52 52 %else:
53 53 ${_('No actions yet')}
54 54 %endif
@@ -1,65 +1,65
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Users administration')} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))} &raquo; ${_('Users')}
10 10 </%def>
11 11
12 12 <%def name="page_nav()">
13 13 ${self.menu('admin')}
14 14 </%def>
15 15
16 16 <%def name="main()">
17 17 <div class="box">
18 18 <!-- box / title -->
19 19 <div class="title">
20 20 ${self.breadcrumbs()}
21 21 <ul class="links">
22 22 <li>
23 23 <span>${h.link_to(_(u'ADD NEW USER'),h.url('new_user'))}</span>
24 24 </li>
25 25
26 26 </ul>
27 27 </div>
28 28 <!-- end box / title -->
29 29 <div class="table">
30 30 <table class="table_disp">
31 31 <tr class="header">
32 32 <th></th>
33 33 <th class="left">${_('username')}</th>
34 34 <th class="left">${_('name')}</th>
35 35 <th class="left">${_('lastname')}</th>
36 36 <th class="left">${_('last login')}</th>
37 37 <th class="left">${_('active')}</th>
38 38 <th class="left">${_('admin')}</th>
39 39 <th class="left">${_('ldap')}</th>
40 40 <th class="left">${_('action')}</th>
41 41 </tr>
42 42 %for cnt,user in enumerate(c.users_list):
43 43 %if user.username !='default':
44 44 <tr class="parity${cnt%2}">
45 45 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div></td>
46 46 <td>${h.link_to(user.username,h.url('edit_user', id=user.user_id))}</td>
47 47 <td>${user.name}</td>
48 48 <td>${user.lastname}</td>
49 <td>${user.last_login}</td>
49 <td>${h.fmt_date(user.last_login)}</td>
50 50 <td>${h.bool2icon(user.active)}</td>
51 51 <td>${h.bool2icon(user.admin)}</td>
52 52 <td>${h.bool2icon(bool(user.ldap_dn))}</td>
53 53 <td>
54 54 ${h.form(url('delete_user', id=user.user_id),method='delete')}
55 55 ${h.submit('remove_',_('delete'),id="remove_user_%s" % user.user_id,
56 56 class_="delete_icon action_button",onclick="return confirm('"+_('Confirm to delete this user: %s') % user.username+"');")}
57 57 ${h.end_form()}
58 58 </td>
59 59 </tr>
60 60 %endif
61 61 %endfor
62 62 </table>
63 63 </div>
64 64 </div>
65 65 </%def>
@@ -1,33 +1,33
1 1 %if c.repo_bookmarks:
2 2 <div id="table_wrap" class="yui-skin-sam">
3 3 <table id="bookmarks_data">
4 4 <thead>
5 5 <tr>
6 6 <th class="left">${_('Name')}</th>
7 7 <th class="left">${_('Date')}</th>
8 8 <th class="left">${_('Author')}</th>
9 9 <th class="left">${_('Revision')}</th>
10 10 </tr>
11 11 </thead>
12 12 %for cnt,book in enumerate(c.repo_bookmarks.items()):
13 13 <tr class="parity${cnt%2}">
14 14 <td>
15 15 <span class="logbooks">
16 16 <span class="bookbook">${h.link_to(book[0],
17 17 h.url('files_home',repo_name=c.repo_name,revision=book[1].raw_id))}</span>
18 18 </span>
19 19 </td>
20 <td><span class="tooltip" title="${h.age(book[1].date)}">${book[1].date}</span></td>
20 <td><span class="tooltip" title="${h.age(book[1].date)}">${h.fmt_date(book[1].date)}</span></td>
21 21 <td title="${book[1].author}">${h.person(book[1].author)}</td>
22 22 <td>
23 23 <div>
24 24 <pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=book[1].raw_id)}">r${book[1].revision}:${h.short_id(book[1].raw_id)}</a></pre>
25 25 </div>
26 26 </td>
27 27 </tr>
28 28 %endfor
29 29 </table>
30 30 </div>
31 31 %else:
32 32 ${_('There are no bookmarks yet')}
33 33 %endif
@@ -1,52 +1,52
1 1 %if c.repo_branches:
2 2 <div id="table_wrap" class="yui-skin-sam">
3 3 <table id="branches_data">
4 4 <thead>
5 5 <tr>
6 6 <th class="left">${_('name')}</th>
7 7 <th class="left">${_('date')}</th>
8 8 <th class="left">${_('author')}</th>
9 9 <th class="left">${_('revision')}</th>
10 10 </tr>
11 11 </thead>
12 12 %for cnt,branch in enumerate(c.repo_branches.items()):
13 13 <tr class="parity${cnt%2}">
14 14 <td>
15 15 <span class="logtags">
16 16 <span class="branchtag">${h.link_to(branch[0],
17 17 h.url('files_home',repo_name=c.repo_name,revision=branch[1].raw_id))}</span>
18 18 </span>
19 19 </td>
20 <td><span class="tooltip" title="${h.age(branch[1].date)}">${branch[1].date}</span></td>
20 <td><span class="tooltip" title="${h.age(branch[1].date)}">${h.fmt_date(branch[1].date)}</span></td>
21 21 <td title="${branch[1].author}">${h.person(branch[1].author)}</td>
22 22 <td>
23 23 <div>
24 24 <pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=branch[1].raw_id)}">r${branch[1].revision}:${h.short_id(branch[1].raw_id)}</a></pre>
25 25 </div>
26 26 </td>
27 27 </tr>
28 28 %endfor
29 29 % if hasattr(c,'repo_closed_branches') and c.repo_closed_branches:
30 30 %for cnt,branch in enumerate(c.repo_closed_branches.items()):
31 31 <tr class="parity${cnt%2}">
32 32 <td>
33 33 <span class="logtags">
34 34 <span class="branchtag">${h.link_to(branch[0]+' [closed]',
35 35 h.url('changeset_home',repo_name=c.repo_name,revision=branch[1].raw_id))}</span>
36 36 </span>
37 37 </td>
38 <td><span class="tooltip" title="${h.age(branch[1].date)}">${branch[1].date}</span></td>
38 <td><span class="tooltip" title="${h.age(branch[1].date)}">${h.fmt_date(branch[1].date)}</span></td>
39 39 <td title="${branch[1].author}">${h.person(branch[1].author)}</td>
40 40 <td>
41 41 <div>
42 42 <pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=branch[1].raw_id)}">r${branch[1].revision}:${h.short_id(branch[1].raw_id)}</a></pre>
43 43 </div>
44 44 </td>
45 45 </tr>
46 46 %endfor
47 47 %endif
48 48 </table>
49 49 </div>
50 50 %else:
51 51 ${_('There are no branches yet')}
52 52 %endif
@@ -1,239 +1,239
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4
5 5 <%def name="title()">
6 6 ${c.repo_name} ${_('Changelog')} - ${c.rhodecode_name}
7 7 </%def>
8 8
9 9 <%def name="breadcrumbs_links()">
10 10 ${h.link_to(u'Home',h.url('/'))}
11 11 &raquo;
12 12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 13 &raquo;
14 14 <% size = c.size if c.size <= c.total_cs else c.total_cs %>
15 15 ${_('Changelog')} - ${ungettext('showing %d out of %d revision', 'showing %d out of %d revisions', size) % (size, c.total_cs)}
16 16 </%def>
17 17
18 18 <%def name="page_nav()">
19 19 ${self.menu('changelog')}
20 20 </%def>
21 21
22 22 <%def name="main()">
23 23 <div class="box">
24 24 <!-- box / title -->
25 25 <div class="title">
26 26 ${self.breadcrumbs()}
27 27 </div>
28 28 <div class="table">
29 29 % if c.pagination:
30 30 <div id="graph">
31 31 <div id="graph_nodes">
32 32 <canvas id="graph_canvas"></canvas>
33 33 </div>
34 34 <div id="graph_content">
35 35 <div class="container_header">
36 36 ${h.form(h.url.current(),method='get')}
37 37 <div class="info_box" style="float:left">
38 38 ${h.submit('set',_('Show'),class_="ui-btn")}
39 39 ${h.text('size',size=1,value=c.size)}
40 40 ${_('revisions')}
41 41 </div>
42 42 ${h.end_form()}
43 43 <div id="rev_range_container" style="display:none"></div>
44 44 <div style="float:right">${h.select('branch_filter',c.branch_name,c.branch_filters)}</div>
45 45 </div>
46 46
47 47 %for cnt,cs in enumerate(c.pagination):
48 48 <div id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
49 49 <div class="left">
50 50 <div>
51 51 ${h.checkbox(cs.short_id,class_="changeset_range")}
52 52 <span class="tooltip" title="${h.age(cs.date)}"><a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id)}"><span class="changeset_id">${cs.revision}:<span class="changeset_hash">${h.short_id(cs.raw_id)}</span></span></a></span>
53 53 </div>
54 54 <div class="author">
55 55 <div class="gravatar">
56 56 <img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),16)}"/>
57 57 </div>
58 58 <div title="${cs.author}" class="user">${h.shorter(h.person(cs.author),22)}</div>
59 59 </div>
60 <div class="date">${cs.date}</div>
60 <div class="date">${h.fmt_date(cs.date)}</div>
61 61 </div>
62 62 <div class="mid">
63 63 <div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name,h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</div>
64 64 <div class="expand"><span class="expandtext">&darr; ${_('show more')} &darr;</span></div>
65 65 </div>
66 66 <div class="right">
67 67 <div class="changes">
68 68 <div id="${cs.raw_id}" style="float:right;" class="changed_total tooltip" title="${_('Affected number of files, click to show more details')}">${len(cs.affected_files)}</div>
69 69 <div class="comments-container">
70 70 %if len(c.comments.get(cs.raw_id,[])) > 0:
71 71 <div class="comments-cnt" title="${('comments')}">
72 72 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
73 73 <div class="comments-cnt">${len(c.comments[cs.raw_id])}</div>
74 74 <img src="${h.url('/images/icons/comments.png')}">
75 75 </a>
76 76 </div>
77 77 %endif
78 78 </div>
79 79 </div>
80 80 %if cs.parents:
81 81 %for p_cs in reversed(cs.parents):
82 82 <div class="parent">${_('Parent')}
83 83 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
84 84 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
85 85 </div>
86 86 %endfor
87 87 %else:
88 88 <div class="parent">${_('No parents')}</div>
89 89 %endif
90 90
91 91 <span class="logtags">
92 92 %if len(cs.parents)>1:
93 93 <span class="merge">${_('merge')}</span>
94 94 %endif
95 95 %if cs.branch:
96 96 <span class="branchtag" title="${'%s %s' % (_('branch'),cs.branch)}">
97 97 ${h.link_to(h.shorter(cs.branch),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
98 98 </span>
99 99 %endif
100 100 %if h.is_hg(c.rhodecode_repo):
101 101 %for book in cs.bookmarks:
102 102 <span class="bookbook" title="${'%s %s' % (_('bookmark'),book)}">
103 103 ${h.link_to(h.shorter(book),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}
104 104 </span>
105 105 %endfor
106 106 %endif
107 107 %for tag in cs.tags:
108 108 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
109 109 ${h.link_to(h.shorter(tag),h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id))}</span>
110 110 %endfor
111 111 </span>
112 112 </div>
113 113 </div>
114 114
115 115 %endfor
116 116 <div class="pagination-wh pagination-left">
117 117 ${c.pagination.pager('$link_previous ~2~ $link_next')}
118 118 </div>
119 119 </div>
120 120 </div>
121 121
122 122 <script type="text/javascript" src="${h.url('/js/graph.js')}"></script>
123 123 <script type="text/javascript">
124 124 YAHOO.util.Event.onDOMReady(function(){
125 125
126 126 //Monitor range checkboxes and build a link to changesets
127 127 //ranges
128 128 var checkboxes = YUD.getElementsByClassName('changeset_range');
129 129 var url_tmpl = "${h.url('changeset_home',repo_name=c.repo_name,revision='__REVRANGE__')}";
130 130 YUE.on(checkboxes,'click',function(e){
131 131 var checked_checkboxes = [];
132 132 for (pos in checkboxes){
133 133 if(checkboxes[pos].checked){
134 134 checked_checkboxes.push(checkboxes[pos]);
135 135 }
136 136 }
137 137 if(checked_checkboxes.length>1){
138 138 var rev_end = checked_checkboxes[0].name;
139 139 var rev_start = checked_checkboxes[checked_checkboxes.length-1].name;
140 140
141 141 var url = url_tmpl.replace('__REVRANGE__',
142 142 rev_start+'...'+rev_end);
143 143
144 144 var link = "<a href="+url+">${_('Show selected changes __S -> __E')}</a>"
145 145 link = link.replace('__S',rev_start);
146 146 link = link.replace('__E',rev_end);
147 147 YUD.get('rev_range_container').innerHTML = link;
148 148 YUD.setStyle('rev_range_container','display','');
149 149 }
150 150 else{
151 151 YUD.setStyle('rev_range_container','display','none');
152 152
153 153 }
154 154 });
155 155
156 156 var msgs = YUQ('.message');
157 157 // get first element height
158 158 var el = YUQ('#graph_content .container')[0];
159 159 var row_h = el.clientHeight;
160 160 for(var i=0;i<msgs.length;i++){
161 161 var m = msgs[i];
162 162
163 163 var h = m.clientHeight;
164 164 var pad = YUD.getStyle(m,'padding');
165 165 if(h > row_h){
166 166 var offset = row_h - (h+12);
167 167 YUD.setStyle(m.nextElementSibling,'display','block');
168 168 YUD.setStyle(m.nextElementSibling,'margin-top',offset+'px');
169 169 };
170 170 }
171 171 YUE.on(YUQ('.expand'),'click',function(e){
172 172 var elem = e.currentTarget.parentNode.parentNode;
173 173 YUD.setStyle(e.currentTarget,'display','none');
174 174 YUD.setStyle(elem,'height','auto');
175 175
176 176 //redraw the graph, max_w and jsdata are global vars
177 177 set_canvas(max_w);
178 178
179 179 var r = new BranchRenderer();
180 180 r.render(jsdata,max_w);
181 181
182 182 })
183 183
184 184 // Fetch changeset details
185 185 YUE.on(YUD.getElementsByClassName('changed_total'),'click',function(e){
186 186 var id = e.currentTarget.id
187 187 var url = "${h.url('changelog_details',repo_name=c.repo_name,cs='__CS__')}"
188 188 var url = url.replace('__CS__',id);
189 189 ypjax(url,id,function(){tooltip_activate()});
190 190 });
191 191
192 192 // change branch filter
193 193 YUE.on(YUD.get('branch_filter'),'change',function(e){
194 194 var selected_branch = e.currentTarget.options[e.currentTarget.selectedIndex].value;
195 195 var url_main = "${h.url('changelog_home',repo_name=c.repo_name)}";
196 196 var url = "${h.url('changelog_home',repo_name=c.repo_name,branch='__BRANCH__')}";
197 197 var url = url.replace('__BRANCH__',selected_branch);
198 198 if(selected_branch != ''){
199 199 window.location = url;
200 200 }else{
201 201 window.location = url_main;
202 202 }
203 203
204 204 });
205 205
206 206 function set_canvas(heads) {
207 207 var c = document.getElementById('graph_nodes');
208 208 var t = document.getElementById('graph_content');
209 209 canvas = document.getElementById('graph_canvas');
210 210 var div_h = t.clientHeight;
211 211 c.style.height=div_h+'px';
212 212 canvas.setAttribute('height',div_h);
213 213 c.style.height=max_w+'px';
214 214 canvas.setAttribute('width',max_w);
215 215 };
216 216 var heads = 1;
217 217 var max_heads = 0;
218 218 var jsdata = ${c.jsdata|n};
219 219
220 220 for( var i=0;i<jsdata.length;i++){
221 221 var m = Math.max.apply(Math, jsdata[i][1]);
222 222 if (m>max_heads){
223 223 max_heads = m;
224 224 }
225 225 }
226 226 var max_w = Math.max(100,max_heads*25);
227 227 set_canvas(max_w);
228 228
229 229 var r = new BranchRenderer();
230 230 r.render(jsdata,max_w);
231 231
232 232 });
233 233 </script>
234 234 %else:
235 235 ${_('There are no changes yet')}
236 236 %endif
237 237 </div>
238 238 </div>
239 239 </%def>
@@ -1,167 +1,167
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 <%inherit file="/base/base.html"/>
4 4
5 5 <%def name="title()">
6 6 ${c.repo_name} ${_('Changeset')} - r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)} - ${c.rhodecode_name}
7 7 </%def>
8 8
9 9 <%def name="breadcrumbs_links()">
10 10 ${h.link_to(u'Home',h.url('/'))}
11 11 &raquo;
12 12 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
13 13 &raquo;
14 14 ${_('Changeset')} - <span class='hash'>r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}</span>
15 15 </%def>
16 16
17 17 <%def name="page_nav()">
18 18 ${self.menu('changelog')}
19 19 </%def>
20 20
21 21 <%def name="main()">
22 22 <div class="box">
23 23 <!-- box / title -->
24 24 <div class="title">
25 25 ${self.breadcrumbs()}
26 26 </div>
27 27 <div class="table">
28 28 <div class="diffblock">
29 29 <div class="code-header">
30 30 <div class="hash">
31 31 r${c.changeset.revision}:${h.short_id(c.changeset.raw_id)}
32 32 </div>
33 33 <div class="date">
34 ${c.changeset.date}
34 ${h.fmt_date(c.changeset.date)}
35 35 </div>
36 36 <div class="diff-actions">
37 37 <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='show')}" title="${_('raw diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white.png')}"/></a>
38 38 <a href="${h.url('raw_changeset_home',repo_name=c.repo_name,revision=c.changeset.raw_id,diff='download')}" title="${_('download diff')}" class="tooltip"><img class="icon" src="${h.url('/images/icons/page_white_get.png')}"/></a>
39 39 ${c.ignorews_url(request.GET)}
40 40 ${c.context_url(request.GET)}
41 41 </div>
42 42 <div class="comments-number" style="float:right;padding-right:5px">${ungettext("%d comment", "%d comments", len(c.comments)) % len(c.comments)} ${ungettext("(%d inline)", "(%d inline)", c.inline_cnt) % c.inline_cnt}</div>
43 43 </div>
44 44 </div>
45 45 <div id="changeset_content">
46 46 <div class="container">
47 47 <div class="left">
48 48 <div class="author">
49 49 <div class="gravatar">
50 50 <img alt="gravatar" src="${h.gravatar_url(h.email(c.changeset.author),20)}"/>
51 51 </div>
52 52 <span>${h.person(c.changeset.author)}</span><br/>
53 53 <span><a href="mailto:${h.email_or_none(c.changeset.author)}">${h.email_or_none(c.changeset.author)}</a></span><br/>
54 54 </div>
55 55 <div class="message">${h.urlify_commit(h.wrap_paragraphs(c.changeset.message),c.repo_name)}</div>
56 56 </div>
57 57 <div class="right">
58 58 <div class="changes">
59 59 % if len(c.changeset.affected_files) <= c.affected_files_cut_off:
60 60 <span class="removed" title="${_('removed')}">${len(c.changeset.removed)}</span>
61 61 <span class="changed" title="${_('changed')}">${len(c.changeset.changed)}</span>
62 62 <span class="added" title="${_('added')}">${len(c.changeset.added)}</span>
63 63 % else:
64 64 <span class="removed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
65 65 <span class="changed" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
66 66 <span class="added" title="${_('affected %s files') % len(c.changeset.affected_files)}">!</span>
67 67 % endif
68 68 </div>
69 69
70 70 %if c.changeset.parents:
71 71 %for p_cs in reversed(c.changeset.parents):
72 72 <div class="parent">${_('Parent')}
73 73 <span class="changeset_id">${p_cs.revision}:<span class="changeset_hash">${h.link_to(h.short_id(p_cs.raw_id),
74 74 h.url('changeset_home',repo_name=c.repo_name,revision=p_cs.raw_id),title=p_cs.message)}</span></span>
75 75 </div>
76 76 %endfor
77 77 %else:
78 78 <div class="parent">${_('No parents')}</div>
79 79 %endif
80 80 <span class="logtags">
81 81 %if len(c.changeset.parents)>1:
82 82 <span class="merge">${_('merge')}</span>
83 83 %endif
84 84 %if c.changeset.branch:
85 85 <span class="branchtag" title="${'%s %s' % (_('branch'),c.changeset.branch)}">
86 86 ${h.link_to(c.changeset.branch,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}
87 87 </span>
88 88 %endif
89 89 %for tag in c.changeset.tags:
90 90 <span class="tagtag" title="${'%s %s' % (_('tag'),tag)}">
91 91 ${h.link_to(tag,h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id))}</span>
92 92 %endfor
93 93 </span>
94 94 </div>
95 95 </div>
96 96 <span>
97 97 ${_('%s files affected with %s insertions and %s deletions:') % (len(c.changeset.affected_files),c.lines_added,c.lines_deleted)}
98 98 </span>
99 99 <div class="cs_files">
100 100 %for change,filenode,diff,cs1,cs2,stat in c.changes:
101 101 <div class="cs_${change}">
102 102 <div class="node">
103 103 %if change != 'removed':
104 104 ${h.link_to(h.safe_unicode(filenode.path),c.anchor_url(filenode.changeset.raw_id,filenode.path,request.GET)+"_target")}
105 105 %else:
106 106 ${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID('',filenode.path)))}
107 107 %endif
108 108 </div>
109 109 <div class="changes">${h.fancy_file_stats(stat)}</div>
110 110 </div>
111 111 %endfor
112 112 % if c.cut_off:
113 113 ${_('Changeset was too big and was cut off...')}
114 114 % endif
115 115 </div>
116 116 </div>
117 117
118 118 </div>
119 119 <script>
120 120 var _USERS_AC_DATA = ${c.users_array|n};
121 121 var _GROUPS_AC_DATA = ${c.users_groups_array|n};
122 122 </script>
123 123 ## diff block
124 124 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
125 125 ${diff_block.diff_block(c.changes)}
126 126
127 127 ## template for inline comment form
128 128 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
129 129 ${comment.comment_inline_form(c.changeset)}
130 130
131 131 ## render comments
132 132 ${comment.comments(c.changeset)}
133 133 <script type="text/javascript">
134 134 YUE.onDOMReady(function(){
135 135 AJAX_COMMENT_URL = "${url('changeset_comment',repo_name=c.repo_name,revision=c.changeset.raw_id)}";
136 136 AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}"
137 137 YUE.on(YUQ('.show-inline-comments'),'change',function(e){
138 138 var show = 'none';
139 139 var target = e.currentTarget;
140 140 if(target.checked){
141 141 var show = ''
142 142 }
143 143 var boxid = YUD.getAttribute(target,'id_for');
144 144 var comments = YUQ('#{0} .inline-comments'.format(boxid));
145 145 for(c in comments){
146 146 YUD.setStyle(comments[c],'display',show);
147 147 }
148 148 var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
149 149 for(c in btns){
150 150 YUD.setStyle(btns[c],'display',show);
151 151 }
152 152 })
153 153
154 154 YUE.on(YUQ('.line'),'click',function(e){
155 155 var tr = e.currentTarget;
156 156 injectInlineForm(tr);
157 157 });
158 158
159 159 // inject comments into they proper positions
160 160 var file_comments = YUQ('.inline-comment-placeholder');
161 161 renderInlineComments(file_comments);
162 162 })
163 163
164 164 </script>
165 165
166 166 </div>
167 167 </%def>
@@ -1,89 +1,89
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${c.repo_name} ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(u'Home',h.url('/'))}
10 10 &raquo;
11 11 ${h.link_to(c.repo_name,h.url('summary_home',repo_name=c.repo_name))}
12 12 &raquo;
13 13 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('changelog')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 <div class="table">
27 27 <div id="body" class="diffblock">
28 28 <div class="code-header cv">
29 29 <h3 class="code-header-title">${_('Compare View')}</h3>
30 30 <div>
31 31 ${_('Changesets')} - r${c.cs_ranges[0].revision}:${h.short_id(c.cs_ranges[0].raw_id)} -> r${c.cs_ranges[-1].revision}:${h.short_id(c.cs_ranges[-1].raw_id)}
32 32 </div>
33 33 </div>
34 34 </div>
35 35 <div id="changeset_compare_view_content">
36 36 <div class="container">
37 37 <table class="compare_view_commits noborder">
38 38 %for cs in c.cs_ranges:
39 39 <tr>
40 40 <td><div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(h.email(cs.author),14)}"/></div></td>
41 41 <td>${h.link_to('r%s:%s' % (cs.revision,h.short_id(cs.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id))}</td>
42 42 <td><div class="author">${h.person(cs.author)}</div></td>
43 <td><span class="tooltip" title="${h.age(cs.date)}">${cs.date}</span></td>
43 <td><span class="tooltip" title="${h.age(cs.date)}">${h.fmt_date(cs.date)}</span></td>
44 44 <td><div class="message">${h.urlify_commit(h.wrap_paragraphs(cs.message),c.repo_name)}</div></td>
45 45 </tr>
46 46 %endfor
47 47 </table>
48 48 </div>
49 49 <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
50 50 <div class="cs_files">
51 51 %for cs in c.cs_ranges:
52 52 <div class="cur_cs">r${cs}</div>
53 53 %for change,filenode,diff,cs1,cs2,st in c.changes[cs.raw_id]:
54 54 <div class="cs_${change}">${h.link_to(h.safe_unicode(filenode.path),h.url.current(anchor=h.FID(cs.raw_id,filenode.path)))}</div>
55 55 %endfor
56 56 %endfor
57 57 </div>
58 58 </div>
59 59
60 60 </div>
61 61 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
62 62 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
63 63 %for cs in c.cs_ranges:
64 64 ##${comment.comment_inline_form(cs)}
65 65 ## diff block
66 66 <h3 style="border:none;padding-top:8px;">${'r%s:%s' % (cs.revision,h.short_id(cs.raw_id))}</h3>
67 67 ${diff_block.diff_block(c.changes[cs.raw_id])}
68 68 ##${comment.comments(cs)}
69 69
70 70 %endfor
71 71 <script type="text/javascript">
72 72
73 73 YUE.onDOMReady(function(){
74 74
75 75 YUE.on(YUQ('.diff-menu-activate'),'click',function(e){
76 76 var act = e.currentTarget.nextElementSibling;
77 77
78 78 if(YUD.hasClass(act,'active')){
79 79 YUD.removeClass(act,'active');
80 80 YUD.setStyle(act,'display','none');
81 81 }else{
82 82 YUD.addClass(act,'active');
83 83 YUD.setStyle(act,'display','');
84 84 }
85 85 });
86 86 })
87 87 </script>
88 88 </div>
89 89 </%def>
@@ -1,116 +1,116
1 1 <%def name="file_class(node)">
2 2 %if node.is_file():
3 3 <%return "browser-file" %>
4 4 %else:
5 5 <%return "browser-dir"%>
6 6 %endif
7 7 </%def>
8 8 <div id="body" class="browserblock">
9 9 <div class="browser-header">
10 10 <div class="browser-nav">
11 11 ${h.form(h.url.current())}
12 12 <div class="info_box">
13 13 <span class="rev">${_('view')}@rev</span>
14 14 <a class="ui-btn" href="${c.url_prev}" title="${_('previous revision')}">&laquo;</a>
15 15 ${h.text('at_rev',value=c.changeset.revision,size=5)}
16 16 <a class="ui-btn" href="${c.url_next}" title="${_('next revision')}">&raquo;</a>
17 17 ## ${h.submit('view',_('view'),class_="ui-btn")}
18 18 </div>
19 19 ${h.end_form()}
20 20 </div>
21 21 <div class="browser-branch">
22 22 ${h.checkbox('stay_at_branch',c.changeset.branch,c.changeset.branch==c.branch)}
23 23 <label>${_('follow current branch')}</label>
24 24 </div>
25 25 <div class="browser-search">
26 26 <div id="search_activate_id" class="search_activate">
27 27 <a class="ui-btn" id="filter_activate" href="#">${_('search file list')}</a>
28 28 </div>
29 29 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
30 30 <div id="add_node_id" class="add_node">
31 31 <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.f_path)}">${_('add new file')}</a>
32 32 </div>
33 33 % endif
34 34 <div>
35 35 <div id="node_filter_box_loading" style="display:none">${_('Loading file list...')}</div>
36 36 <div id="node_filter_box" style="display:none">
37 37 ${h.files_breadcrumbs(c.repo_name,c.changeset.raw_id,c.file.path)}/<input class="init" type="text" value="type to search..." name="filter" size="25" id="node_filter" autocomplete="off">
38 38 </div>
39 39 </div>
40 40 </div>
41 41 </div>
42 42
43 43 <div class="browser-body">
44 44 <table class="code-browser">
45 45 <thead>
46 46 <tr>
47 47 <th>${_('Name')}</th>
48 48 <th>${_('Size')}</th>
49 49 <th>${_('Mimetype')}</th>
50 50 <th>${_('Last Revision')}</th>
51 51 <th>${_('Last modified')}</th>
52 52 <th>${_('Last commiter')}</th>
53 53 </tr>
54 54 </thead>
55 55
56 56 <tbody id="tbody">
57 57 %if c.file.parent:
58 58 <tr class="parity0">
59 59 <td>
60 60 ${h.link_to('..',h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=c.file.parent.path),class_="browser-dir ypjax-link")}
61 61 </td>
62 62 <td></td>
63 63 <td></td>
64 64 <td></td>
65 65 <td></td>
66 66 <td></td>
67 67 </tr>
68 68 %endif
69 69
70 70 %for cnt,node in enumerate(c.file):
71 71 <tr class="parity${cnt%2}">
72 72 <td>
73 73 %if node.is_submodule():
74 74 ${h.link_to(node.name,node.url or '#',class_="submodule-dir ypjax-link")}
75 75 %else:
76 76 ${h.link_to(node.name, h.url('files_home',repo_name=c.repo_name,revision=c.changeset.raw_id,f_path=h.safe_unicode(node.path)),class_=file_class(node)+" ypjax-link")}
77 77 %endif:
78 78 </td>
79 79 <td>
80 80 %if node.is_file():
81 81 ${h.format_byte_size(node.size,binary=True)}
82 82 %endif
83 83 </td>
84 84 <td>
85 85 %if node.is_file():
86 86 ${node.mimetype}
87 87 %endif
88 88 </td>
89 89 <td>
90 90 %if node.is_file():
91 91 <div class="tooltip" title="${node.last_changeset.message}">
92 92 <pre>${'r%s:%s' % (node.last_changeset.revision,node.last_changeset.short_id)}</pre>
93 93 </div>
94 94 %endif
95 95 </td>
96 96 <td>
97 97 %if node.is_file():
98 <span class="tooltip" title="${node.last_changeset.date}">
98 <span class="tooltip" title="${h.fmt_date(node.last_changeset.date)}">
99 99 ${h.age(node.last_changeset.date)}</span>
100 100 %endif
101 101 </td>
102 102 <td>
103 103 %if node.is_file():
104 104 <span title="${node.last_changeset.author}">
105 105 ${h.person(node.last_changeset.author)}
106 106 </span>
107 107 %endif
108 108 </td>
109 109 </tr>
110 110 %endfor
111 111 </tbody>
112 112 <tbody id="tbody_filtered" style="display:none">
113 113 </tbody>
114 114 </table>
115 115 </div>
116 116 </div>
@@ -1,114 +1,114
1 1 <dl>
2 2 <dt style="padding-top:10px;font-size:16px">${_('History')}</dt>
3 3 <dd>
4 4 <div>
5 5 ${h.form(h.url('files_diff_home',repo_name=c.repo_name,f_path=c.f_path),method='get')}
6 6 ${h.hidden('diff2',c.file.changeset.raw_id)}
7 7 ${h.select('diff1',c.file.changeset.raw_id,c.file_history)}
8 8 ${h.submit('diff','diff to revision',class_="ui-btn")}
9 9 ${h.submit('show_rev','show at revision',class_="ui-btn")}
10 10 ${h.end_form()}
11 11 </div>
12 12 </dd>
13 13 </dl>
14 14
15 15 <div id="body" class="codeblock">
16 16 <div class="code-header">
17 17 <div class="stats">
18 18 <div class="left img"><img src="${h.url('/images/icons/file.png')}"/></div>
19 <div class="left item"><pre class="tooltip" title="${c.file.changeset.date}">${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</pre></div>
19 <div class="left item"><pre class="tooltip" title="${h.fmt_date(c.file.changeset.date)}">${h.link_to("r%s:%s" % (c.file.changeset.revision,h.short_id(c.file.changeset.raw_id)),h.url('changeset_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id))}</pre></div>
20 20 <div class="left item"><pre>${h.format_byte_size(c.file.size,binary=True)}</pre></div>
21 21 <div class="left item last"><pre>${c.file.mimetype}</pre></div>
22 22 <div class="buttons">
23 23 %if c.annotate:
24 24 ${h.link_to(_('show source'), h.url('files_home', repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
25 25 %else:
26 26 ${h.link_to(_('show annotation'),h.url('files_annotate_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
27 27 %endif
28 28 ${h.link_to(_('show as raw'),h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
29 29 ${h.link_to(_('download as raw'),h.url('files_rawfile_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
30 30 % if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
31 31 % if not c.file.is_binary:
32 32 ${h.link_to(_('edit'),h.url('files_edit_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path),class_="ui-btn")}
33 33 % endif
34 34 % endif
35 35 </div>
36 36 </div>
37 37 <div class="author">
38 38 <div class="gravatar">
39 39 <img alt="gravatar" src="${h.gravatar_url(h.email(c.file.changeset.author),16)}"/>
40 40 </div>
41 41 <div title="${c.file.changeset.author}" class="user">${h.person(c.file.changeset.author)}</div>
42 42 </div>
43 43 <div class="commit">${h.urlify_commit(c.file.changeset.message,c.repo_name)}</div>
44 44 </div>
45 45 <div class="code-body">
46 46 %if c.file.is_binary:
47 47 ${_('Binary file (%s)') % c.file.mimetype}
48 48 %else:
49 49 % if c.file.size < c.cut_off_limit:
50 50 %if c.annotate:
51 51 ${h.pygmentize_annotation(c.repo_name,c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
52 52 %else:
53 53 ${h.pygmentize(c.file,linenos=True,anchorlinenos=True,lineanchors='L',cssclass="code-highlight")}
54 54 %endif
55 55 %else:
56 56 ${_('File is too big to display')} ${h.link_to(_('show as raw'),
57 57 h.url('files_raw_home',repo_name=c.repo_name,revision=c.file.changeset.raw_id,f_path=c.f_path))}
58 58 %endif
59 59 %endif
60 60 </div>
61 61 </div>
62 62
63 63 <script type="text/javascript">
64 64 YUE.onDOMReady(function(){
65 65 function highlight_lines(lines){
66 66 for(pos in lines){
67 67 YUD.setStyle('L'+lines[pos],'background-color','#FFFFBE');
68 68 }
69 69 }
70 70 page_highlights = location.href.substring(location.href.indexOf('#')+1).split('L');
71 71 if (page_highlights.length == 2){
72 72 highlight_ranges = page_highlights[1].split(",");
73 73
74 74 var h_lines = [];
75 75 for (pos in highlight_ranges){
76 76 var _range = highlight_ranges[pos].split('-');
77 77 if(_range.length == 2){
78 78 var start = parseInt(_range[0]);
79 79 var end = parseInt(_range[1]);
80 80 if (start < end){
81 81 for(var i=start;i<=end;i++){
82 82 h_lines.push(i);
83 83 }
84 84 }
85 85 }
86 86 else{
87 87 h_lines.push(parseInt(highlight_ranges[pos]));
88 88 }
89 89 }
90 90 highlight_lines(h_lines);
91 91
92 92 //remember original location
93 93 var old_hash = location.href.substring(location.href.indexOf('#'));
94 94
95 95 // this makes a jump to anchor moved by 3 posstions for padding
96 96 window.location.hash = '#L'+Math.max(parseInt(h_lines[0])-3,1);
97 97
98 98 //sets old anchor
99 99 window.location.hash = old_hash;
100 100
101 101 }
102 102 YUE.on('show_rev','click',function(e){
103 103 YUE.preventDefault(e);
104 104 var cs = YUD.get('diff1').value;
105 105 %if c.annotate:
106 106 var url = "${h.url('files_annotate_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
107 107 %else:
108 108 var url = "${h.url('files_home',repo_name=c.repo_name,revision='__CS__',f_path=c.f_path)}".replace('__CS__',cs);
109 109 %endif
110 110 window.location = url;
111 111 });
112 112 YUE.on('hlcode','mouseup',getSelectionLink("${_('Selection link')}"))
113 113 });
114 114 </script>
@@ -1,201 +1,201
1 1 <%page args="parent" />
2 2 <div class="box">
3 3 <!-- box / title -->
4 4 <div class="title">
5 5 <h5>
6 6 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/> ${parent.breadcrumbs()} <span id="repo_count">0</span> ${_('repositories')}
7 7 </h5>
8 8 %if c.rhodecode_user.username != 'default':
9 9 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
10 10 <ul class="links">
11 11 <li>
12 12 %if c.group:
13 13 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository',parent_group=c.group.group_id))}</span>
14 14 %else:
15 15 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
16 16 %endif
17 17 </li>
18 18 </ul>
19 19 %endif
20 20 %endif
21 21 </div>
22 22 <!-- end box / title -->
23 23 <div class="table">
24 24 % if c.groups:
25 25 <div id='groups_list_wrap' class="yui-skin-sam">
26 26 <table id="groups_list">
27 27 <thead>
28 28 <tr>
29 29 <th class="left"><a href="#">${_('Group name')}</a></th>
30 30 <th class="left"><a href="#">${_('Description')}</a></th>
31 31 ##<th class="left"><a href="#">${_('Number of repositories')}</a></th>
32 32 </tr>
33 33 </thead>
34 34
35 35 ## REPO GROUPS
36 36 % for gr in c.groups:
37 37 <tr>
38 38 <td>
39 39 <div style="white-space: nowrap">
40 40 <img class="icon" alt="${_('Repositories group')}" src="${h.url('/images/icons/database_link.png')}"/>
41 41 ${h.link_to(gr.name,url('repos_group_home',group_name=gr.group_name))}
42 42 </div>
43 43 </td>
44 44 <td>${gr.group_description}</td>
45 45 ## this is commented out since for multi nested repos can be HEAVY!
46 46 ## in number of executed queries during traversing uncomment at will
47 47 ##<td><b>${gr.repositories_recursive_count}</b></td>
48 48 </tr>
49 49 % endfor
50 50
51 51 </table>
52 52 </div>
53 53 <div style="height: 20px"></div>
54 54 % endif
55 55 <div id="welcome" style="display:none;text-align:center">
56 56 <h1><a href="${h.url('home')}">${c.rhodecode_name} ${c.rhodecode_version}</a></h1>
57 57 </div>
58 58 <div id='repos_list_wrap' class="yui-skin-sam">
59 59 <%cnt=0%>
60 60 <%namespace name="dt" file="/data_table/_dt_elements.html"/>
61 61
62 62 <table id="repos_list">
63 63 <thead>
64 64 <tr>
65 65 <th class="left"></th>
66 66 <th class="left">${_('Name')}</th>
67 67 <th class="left">${_('Description')}</th>
68 68 <th class="left">${_('Last change')}</th>
69 69 <th class="left">${_('Tip')}</th>
70 70 <th class="left">${_('Owner')}</th>
71 71 <th class="left">${_('RSS')}</th>
72 72 <th class="left">${_('Atom')}</th>
73 73 </tr>
74 74 </thead>
75 75 <tbody>
76 76 %for cnt,repo in enumerate(c.repos_list):
77 77 <tr class="parity${(cnt+1)%2}">
78 78 ##QUICK MENU
79 79 <td class="quick_repo_menu">
80 80 ${dt.quick_menu(repo['name'])}
81 81 </td>
82 82 ##REPO NAME AND ICONS
83 83 <td class="reponame">
84 84 ${dt.repo_name(repo['name'],repo['dbrepo']['repo_type'],repo['dbrepo']['private'],repo['dbrepo_fork'].get('repo_name'),pageargs.get('short_repo_names'))}
85 85 </td>
86 86 ##DESCRIPTION
87 87 <td><span class="tooltip" title="${h.tooltip(repo['description'])}">
88 88 ${h.truncate(repo['description'],60)}</span>
89 89 </td>
90 90 ##LAST CHANGE DATE
91 91 <td>
92 <span class="tooltip" title="${repo['last_change']}">${h.age(repo['last_change'])}</span>
92 <span class="tooltip" title="${h.fmt_date(repo['last_change'])}">${h.age(repo['last_change'])}</span>
93 93 </td>
94 94 ##LAST REVISION
95 95 <td>
96 96 ${dt.revision(repo['name'],repo['rev'],repo['tip'],repo['author'],repo['last_msg'])}
97 97 </td>
98 98 ##
99 99 <td title="${repo['contact']}">${h.person(repo['contact'])}</td>
100 100 <td>
101 101 %if c.rhodecode_user.username != 'default':
102 102 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
103 103 %else:
104 104 <a title="${_('Subscribe to %s rss feed')%repo['name']}" class="rss_icon" href="${h.url('rss_feed_home',repo_name=repo['name'])}"></a>
105 105 %endif:
106 106 </td>
107 107 <td>
108 108 %if c.rhodecode_user.username != 'default':
109 109 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'],api_key=c.rhodecode_user.api_key)}"></a>
110 110 %else:
111 111 <a title="${_('Subscribe to %s atom feed')%repo['name']}" class="atom_icon" href="${h.url('atom_feed_home',repo_name=repo['name'])}"></a>
112 112 %endif:
113 113 </td>
114 114 </tr>
115 115 %endfor
116 116 </tbody>
117 117 </table>
118 118 </div>
119 119 </div>
120 120 </div>
121 121 <script>
122 122 YUD.get('repo_count').innerHTML = ${cnt+1};
123 123 var func = function(node){
124 124 return node.parentNode.parentNode.parentNode.parentNode;
125 125 }
126 126
127 127
128 128 // groups table sorting
129 129 var myColumnDefs = [
130 130 {key:"name",label:"${_('Group Name')}",sortable:true,
131 131 sortOptions: { sortFunction: groupNameSort }},
132 132 {key:"desc",label:"${_('Description')}",sortable:true},
133 133 ];
134 134
135 135 var myDataSource = new YAHOO.util.DataSource(YUD.get("groups_list"));
136 136
137 137 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
138 138 myDataSource.responseSchema = {
139 139 fields: [
140 140 {key:"name"},
141 141 {key:"desc"},
142 142 ]
143 143 };
144 144
145 145 var myDataTable = new YAHOO.widget.DataTable("groups_list_wrap", myColumnDefs, myDataSource,
146 146 {
147 147 sortedBy:{key:"name",dir:"asc"},
148 148 MSG_SORTASC:"${_('Click to sort ascending')}",
149 149 MSG_SORTDESC:"${_('Click to sort descending')}"
150 150 }
151 151 );
152 152
153 153 // main table sorting
154 154 var myColumnDefs = [
155 155 {key:"menu",label:"",sortable:false,className:"quick_repo_menu hidden"},
156 156 {key:"name",label:"${_('Name')}",sortable:true,
157 157 sortOptions: { sortFunction: nameSort }},
158 158 {key:"desc",label:"${_('Description')}",sortable:true},
159 159 {key:"last_change",label:"${_('Last Change')}",sortable:true,
160 160 sortOptions: { sortFunction: ageSort }},
161 161 {key:"tip",label:"${_('Tip')}",sortable:true,
162 162 sortOptions: { sortFunction: revisionSort }},
163 163 {key:"owner",label:"${_('Owner')}",sortable:true},
164 164 {key:"rss",label:"",sortable:false},
165 165 {key:"atom",label:"",sortable:false},
166 166 ];
167 167
168 168 var myDataSource = new YAHOO.util.DataSource(YUD.get("repos_list"));
169 169
170 170 myDataSource.responseType = YAHOO.util.DataSource.TYPE_HTMLTABLE;
171 171
172 172 myDataSource.responseSchema = {
173 173 fields: [
174 174 {key:"menu"},
175 175 {key:"name"},
176 176 {key:"desc"},
177 177 {key:"last_change"},
178 178 {key:"tip"},
179 179 {key:"owner"},
180 180 {key:"rss"},
181 181 {key:"atom"},
182 182 ]
183 183 };
184 184
185 185 var myDataTable = new YAHOO.widget.DataTable("repos_list_wrap", myColumnDefs, myDataSource,
186 186 {
187 187 sortedBy:{key:"name",dir:"asc"},
188 188 MSG_SORTASC:"${_('Click to sort ascending')}",
189 189 MSG_SORTDESC:"${_('Click to sort descending')}",
190 190 MSG_EMPTY:"${_('No records found.')}",
191 191 MSG_ERROR:"${_('Data error.')}",
192 192 MSG_LOADING:"${_('Loading...')}",
193 193 }
194 194 );
195 195 myDataTable.subscribe('postRenderEvent',function(oArgs) {
196 196 tooltip_activate();
197 197 quick_repo_menu();
198 198 q_filter('q_filter',YUQ('div.table tr td a.repo_name'),func);
199 199 });
200 200
201 201 </script>
@@ -1,49 +1,49
1 1 ## -*- coding: utf-8 -*-
2 2
3 3 %if c.journal_day_aggreagate:
4 4 %for day,items in c.journal_day_aggreagate:
5 5 <div class="journal_day">${day}</div>
6 6 % for user,entries in items:
7 7 <div class="journal_container">
8 8 <div class="gravatar">
9 9 <img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/>
10 10 </div>
11 11 <div class="journal_user">${user.name} ${user.lastname}</div>
12 12 <div class="journal_action_container">
13 13 % for entry in entries:
14 14 <div class="journal_icon"> ${h.action_parser(entry)[2]()}</div>
15 15 <div class="journal_action">${h.action_parser(entry)[0]()}</div>
16 16 <div class="journal_repo">
17 17 <span class="journal_repo_name">
18 18 %if entry.repository is not None:
19 19 ${h.link_to(entry.repository.repo_name,
20 20 h.url('summary_home',repo_name=entry.repository.repo_name))}
21 21 %else:
22 22 ${entry.repository_name}
23 23 %endif
24 24 </span>
25 25 </div>
26 26 <div class="journal_action_params">${h.literal(h.action_parser(entry)[1]())}</div>
27 <div class="date"><span class="tooltip" title="${entry.action_date}">${h.age(entry.action_date)}</span></div>
27 <div class="date"><span class="tooltip" title="${h.fmt_date(entry.action_date)}">${h.age(entry.action_date)}</span></div>
28 28 %endfor
29 29 </div>
30 30 </div>
31 31 %endfor
32 32 %endfor
33 33
34 34 <div class="pagination-wh pagination-left">
35 35 <script type="text/javascript">
36 36 YUE.onDOMReady(function(){
37 37 YUE.delegate("journal","click",function(e, matchedEl, container){
38 38 ypjax(e.target.href,"journal",function(){show_more_event();tooltip_activate();});
39 39 YUE.preventDefault(e);
40 40 },'.pager_link');
41 41 });
42 42 </script>
43 43 ${c.journal_pager.pager('$link_previous ~2~ $link_next')}
44 44 </div>
45 45 %else:
46 46 <div style="padding:5px 0px 10px 10px;">
47 47 ${_('No entries yet')}
48 48 </div>
49 49 %endif
@@ -1,83 +1,83
1 1 ## -*- coding: utf-8 -*-
2 2 %if c.repo_changesets:
3 3 <table class="table_disp">
4 4 <tr>
5 5 <th class="left">${_('revision')}</th>
6 6 <th class="left">${_('commit message')}</th>
7 7 <th class="left">${_('age')}</th>
8 8 <th class="left">${_('author')}</th>
9 9 <th class="left">${_('branch')}</th>
10 10 <th class="left">${_('tags')}</th>
11 11 </tr>
12 12 %for cnt,cs in enumerate(c.repo_changesets):
13 13 <tr class="parity${cnt%2}">
14 14 <td>
15 15 <div><pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}">r${cs.revision}:${h.short_id(cs.raw_id)}</a></pre></div>
16 16 </td>
17 17 <td>
18 18 ${h.link_to(h.truncate(cs.message,50) or _('No commit message'),
19 19 h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id),
20 20 title=cs.message)}
21 21 </td>
22 <td><span class="tooltip" title="${cs.date}">
22 <td><span class="tooltip" title="${h.fmt_date(cs.date)}">
23 23 ${h.age(cs.date)}</span>
24 24 </td>
25 25 <td title="${cs.author}">${h.person(cs.author)}</td>
26 26 <td>
27 27 <span class="logtags">
28 28 %if cs.branch:
29 29 <span class="branchtag">
30 30 ${cs.branch}
31 31 </span>
32 32 %endif
33 33 </span>
34 34 </td>
35 35 <td>
36 36 <span class="logtags">
37 37 %for tag in cs.tags:
38 38 <span class="tagtag">${tag}</span>
39 39 %endfor
40 40 </span>
41 41 </td>
42 42 </tr>
43 43 %endfor
44 44
45 45 </table>
46 46
47 47 <script type="text/javascript">
48 48 YUE.onDOMReady(function(){
49 49 YUE.delegate("shortlog_data","click",function(e, matchedEl, container){
50 50 ypjax(e.target.href,"shortlog_data",function(){tooltip_activate();});
51 51 YUE.preventDefault(e);
52 52 },'.pager_link');
53 53 });
54 54 </script>
55 55
56 56 <div class="pagination-wh pagination-left">
57 57 ${c.repo_changesets.pager('$link_previous ~2~ $link_next')}
58 58 </div>
59 59 %else:
60 60
61 61 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
62 62 <h4>${_('Add or upload files directly via RhodeCode')}</h4>
63 63 <div style="margin: 20px 30px;">
64 64 <div id="add_node_id" class="add_node">
65 65 <a class="ui-btn" href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='')}">${_('add new file')}</a>
66 66 </div>
67 67 </div>
68 68 %endif
69 69
70 70
71 71 <h4>${_('Push new repo')}</h4>
72 72 <pre>
73 73 ${c.rhodecode_repo.alias} clone ${c.clone_repo_url}
74 74 ${c.rhodecode_repo.alias} add README # add first file
75 75 ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message
76 76 ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back
77 77 </pre>
78 78
79 79 <h4>${_('Existing repository?')}</h4>
80 80 <pre>
81 81 ${c.rhodecode_repo.alias} push ${c.clone_repo_url}
82 82 </pre>
83 83 %endif
@@ -1,34 +1,34
1 1 %if c.repo_tags:
2 2 <div id="table_wrap" class="yui-skin-sam">
3 3 <table id="tags_data">
4 4 <thead>
5 5 <tr>
6 6 <th class="left">${_('Name')}</th>
7 7 <th class="left">${_('Date')}</th>
8 8 <th class="left">${_('Author')}</th>
9 9 <th class="left">${_('Revision')}</th>
10 10 </tr>
11 11 </thead>
12 12 %for cnt,tag in enumerate(c.repo_tags.items()):
13 13 <tr class="parity${cnt%2}">
14 14 <td>
15 15 <span class="logtags">
16 16 <span class="tagtag">${h.link_to(tag[0],
17 17 h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id))}
18 18 </span>
19 19 </span>
20 20 </td>
21 <td><span class="tooltip" title="${h.age(tag[1].date)}">${tag[1].date}</span></td>
21 <td><span class="tooltip" title="${h.age(tag[1].date)}">${h.fmt_date(tag[1].date)}</span></td>
22 22 <td title="${tag[1].author}">${h.person(tag[1].author)}</td>
23 23 <td>
24 24 <div>
25 25 <pre><a href="${h.url('files_home',repo_name=c.repo_name,revision=tag[1].raw_id)}">r${tag[1].revision}:${h.short_id(tag[1].raw_id)}</a></pre>
26 26 </div>
27 27 </td>
28 28 </tr>
29 29 %endfor
30 30 </table>
31 31 </div>
32 32 %else:
33 33 ${_('There are no tags yet')}
34 34 %endif
General Comments 0
You need to be logged in to leave comments. Login now