##// END OF EJS Templates
implements #293 gravatar link should be disabled when use_gravatar = false
marcink -
r1643:bdb6fbc8 default
parent child Browse files
Show More
@@ -1,82 +1,83
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6
6
7 from pylons import config, tmpl_context as c, request, session, url
7 from pylons import config, tmpl_context as c, request, session, url
8 from pylons.controllers import WSGIController
8 from pylons.controllers import WSGIController
9 from pylons.controllers.util import redirect
9 from pylons.controllers.util import redirect
10 from pylons.templating import render_mako as render
10 from pylons.templating import render_mako as render
11
11
12 from rhodecode import __version__
12 from rhodecode import __version__
13 from rhodecode.lib import str2bool
13 from rhodecode.lib.auth import AuthUser
14 from rhodecode.lib.auth import AuthUser
14 from rhodecode.lib.utils import get_repo_slug
15 from rhodecode.lib.utils import get_repo_slug
15 from rhodecode.model import meta
16 from rhodecode.model import meta
16 from rhodecode.model.scm import ScmModel
17 from rhodecode.model.scm import ScmModel
17 from rhodecode import BACKENDS
18 from rhodecode import BACKENDS
18 from rhodecode.model.db import Repository
19 from rhodecode.model.db import Repository
19
20
20 log = logging.getLogger(__name__)
21 log = logging.getLogger(__name__)
21
22
22 class BaseController(WSGIController):
23 class BaseController(WSGIController):
23
24
24 def __before__(self):
25 def __before__(self):
25 c.rhodecode_version = __version__
26 c.rhodecode_version = __version__
26 c.rhodecode_name = config.get('rhodecode_title')
27 c.rhodecode_name = config.get('rhodecode_title')
28 c.use_gravatar = str2bool(config.get('use_gravatar'))
27 c.ga_code = config.get('rhodecode_ga_code')
29 c.ga_code = config.get('rhodecode_ga_code')
28 c.repo_name = get_repo_slug(request)
30 c.repo_name = get_repo_slug(request)
29 c.backends = BACKENDS.keys()
31 c.backends = BACKENDS.keys()
30 self.cut_off_limit = int(config.get('cut_off_limit'))
32 self.cut_off_limit = int(config.get('cut_off_limit'))
31
33
32 self.sa = meta.Session()
34 self.sa = meta.Session()
33 self.scm_model = ScmModel(self.sa)
35 self.scm_model = ScmModel(self.sa)
34
36
35 #c.unread_journal = scm_model.get_unread_journal()
36
37 def __call__(self, environ, start_response):
37 def __call__(self, environ, start_response):
38 """Invoke the Controller"""
38 """Invoke the Controller"""
39 # WSGIController.__call__ dispatches to the Controller method
39 # WSGIController.__call__ dispatches to the Controller method
40 # the request is routed to. This routing information is
40 # the request is routed to. This routing information is
41 # available in environ['pylons.routes_dict']
41 # available in environ['pylons.routes_dict']
42 try:
42 try:
43 # putting this here makes sure that we update permissions each time
43 # putting this here makes sure that we update permissions each time
44 api_key = request.GET.get('api_key')
44 api_key = request.GET.get('api_key')
45 user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
45 user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
46 self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
46 self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key)
47 self.rhodecode_user.set_authenticated(
47 self.rhodecode_user.set_authenticated(
48 getattr(session.get('rhodecode_user'),
48 getattr(session.get('rhodecode_user'),
49 'is_authenticated', False))
49 'is_authenticated', False))
50 session['rhodecode_user'] = self.rhodecode_user
50 session['rhodecode_user'] = self.rhodecode_user
51 session.save()
51 session.save()
52 return WSGIController.__call__(self, environ, start_response)
52 return WSGIController.__call__(self, environ, start_response)
53 finally:
53 finally:
54 meta.Session.remove()
54 meta.Session.remove()
55
55
56
56
57 class BaseRepoController(BaseController):
57 class BaseRepoController(BaseController):
58 """
58 """
59 Base class for controllers responsible for loading all needed data
59 Base class for controllers responsible for loading all needed data for
60 for those controllers, loaded items are
60 repository loaded items are
61
61
62 c.rhodecode_repo: instance of scm repository (taken from cache)
62 c.rhodecode_repo: instance of scm repository
63
63 c.rhodecode_db_repo: instance of db
64 c.repository_followers: number of followers
65 c.repository_forks: number of forks
64 """
66 """
65
67
66 def __before__(self):
68 def __before__(self):
67 super(BaseRepoController, self).__before__()
69 super(BaseRepoController, self).__before__()
68 if c.repo_name:
70 if c.repo_name:
69
71
70 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
72 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
71 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
73 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
72
74
73 if c.rhodecode_repo is None:
75 if c.rhodecode_repo is None:
74 log.error('%s this repository is present in database but it '
76 log.error('%s this repository is present in database but it '
75 'cannot be created as an scm instance', c.repo_name)
77 'cannot be created as an scm instance', c.repo_name)
76
78
77 redirect(url('home'))
79 redirect(url('home'))
78
80
79 c.repository_followers = \
81 c.repository_followers = self.scm_model.get_followers(c.repo_name)
80 self.scm_model.get_followers(c.repo_name)
81 c.repository_forks = self.scm_model.get_forks(c.repo_name)
82 c.repository_forks = self.scm_model.get_forks(c.repo_name)
82
83
@@ -1,672 +1,672
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,get_changeset_safe
40 from rhodecode.lib import str2bool, safe_unicode, safe_str,get_changeset_safe
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
214
215 def hsv_to_rgb(h, s, v):
215 def hsv_to_rgb(h, s, v):
216 if s == 0.0: return v, v, v
216 if s == 0.0: return v, v, v
217 i = int(h * 6.0) # XXX assume int() truncates!
217 i = int(h * 6.0) # XXX assume int() truncates!
218 f = (h * 6.0) - i
218 f = (h * 6.0) - i
219 p = v * (1.0 - s)
219 p = v * (1.0 - s)
220 q = v * (1.0 - s * f)
220 q = v * (1.0 - s * f)
221 t = v * (1.0 - s * (1.0 - f))
221 t = v * (1.0 - s * (1.0 - f))
222 i = i % 6
222 i = i % 6
223 if i == 0: return v, t, p
223 if i == 0: return v, t, p
224 if i == 1: return q, v, p
224 if i == 1: return q, v, p
225 if i == 2: return p, v, t
225 if i == 2: return p, v, t
226 if i == 3: return p, q, v
226 if i == 3: return p, q, v
227 if i == 4: return t, p, v
227 if i == 4: return t, p, v
228 if i == 5: return v, p, q
228 if i == 5: return v, p, q
229
229
230 golden_ratio = 0.618033988749895
230 golden_ratio = 0.618033988749895
231 h = 0.22717784590367374
231 h = 0.22717784590367374
232
232
233 for _ in xrange(n):
233 for _ in xrange(n):
234 h += golden_ratio
234 h += golden_ratio
235 h %= 1
235 h %= 1
236 HSV_tuple = [h, 0.95, 0.95]
236 HSV_tuple = [h, 0.95, 0.95]
237 RGB_tuple = hsv_to_rgb(*HSV_tuple)
237 RGB_tuple = hsv_to_rgb(*HSV_tuple)
238 yield map(lambda x:str(int(x * 256)), RGB_tuple)
238 yield map(lambda x:str(int(x * 256)), RGB_tuple)
239
239
240 cgenerator = gen_color()
240 cgenerator = gen_color()
241
241
242 def get_color_string(cs):
242 def get_color_string(cs):
243 if color_dict.has_key(cs):
243 if color_dict.has_key(cs):
244 col = color_dict[cs]
244 col = color_dict[cs]
245 else:
245 else:
246 col = color_dict[cs] = cgenerator.next()
246 col = color_dict[cs] = cgenerator.next()
247 return "color: rgb(%s)! important;" % (', '.join(col))
247 return "color: rgb(%s)! important;" % (', '.join(col))
248
248
249 def url_func(repo_name):
249 def url_func(repo_name):
250
250
251 def _url_func(changeset):
251 def _url_func(changeset):
252 author = changeset.author
252 author = changeset.author
253 date = changeset.date
253 date = changeset.date
254 message = tooltip(changeset.message)
254 message = tooltip(changeset.message)
255
255
256 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
256 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
257 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
257 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
258 "</b> %s<br/></div>")
258 "</b> %s<br/></div>")
259
259
260 tooltip_html = tooltip_html % (author, date, message)
260 tooltip_html = tooltip_html % (author, date, message)
261 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
261 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
262 short_id(changeset.raw_id))
262 short_id(changeset.raw_id))
263 uri = link_to(
263 uri = link_to(
264 lnk_format,
264 lnk_format,
265 url('changeset_home', repo_name=repo_name,
265 url('changeset_home', repo_name=repo_name,
266 revision=changeset.raw_id),
266 revision=changeset.raw_id),
267 style=get_color_string(changeset.raw_id),
267 style=get_color_string(changeset.raw_id),
268 class_='tooltip',
268 class_='tooltip',
269 title=tooltip_html
269 title=tooltip_html
270 )
270 )
271
271
272 uri += '\n'
272 uri += '\n'
273 return uri
273 return uri
274 return _url_func
274 return _url_func
275
275
276 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
276 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
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 not email_address or email_address == 'anonymous@rhodecode.org'):
459 return url("/images/user%s.png" % size)
459 return url("/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.name, url('repos_group_home',
601 return link_to(group.name, url('repos_group_home',
602 group_name=group.group_name))
602 group_name=group.group_name))
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 def fancy_file_stats(stats):
606 def fancy_file_stats(stats):
607 """
607 """
608 Displays a fancy two colored bar for number of added/deleted
608 Displays a fancy two colored bar for number of added/deleted
609 lines of code on file
609 lines of code on file
610
610
611 :param stats: two element list of added/deleted lines of code
611 :param stats: two element list of added/deleted lines of code
612 """
612 """
613
613
614 a, d, t = stats[0], stats[1], stats[0] + stats[1]
614 a, d, t = stats[0], stats[1], stats[0] + stats[1]
615 width = 100
615 width = 100
616 unit = float(width) / (t or 1)
616 unit = float(width) / (t or 1)
617
617
618 # needs > 9% of width to be visible or 0 to be hidden
618 # needs > 9% of width to be visible or 0 to be hidden
619 a_p = max(9, unit * a) if a > 0 else 0
619 a_p = max(9, unit * a) if a > 0 else 0
620 d_p = max(9, unit * d) if d > 0 else 0
620 d_p = max(9, unit * d) if d > 0 else 0
621 p_sum = a_p + d_p
621 p_sum = a_p + d_p
622
622
623 if p_sum > width:
623 if p_sum > width:
624 #adjust the percentage to be == 100% since we adjusted to 9
624 #adjust the percentage to be == 100% since we adjusted to 9
625 if a_p > d_p:
625 if a_p > d_p:
626 a_p = a_p - (p_sum - width)
626 a_p = a_p - (p_sum - width)
627 else:
627 else:
628 d_p = d_p - (p_sum - width)
628 d_p = d_p - (p_sum - width)
629
629
630 a_v = a if a > 0 else ''
630 a_v = a if a > 0 else ''
631 d_v = d if d > 0 else ''
631 d_v = d if d > 0 else ''
632
632
633
633
634 def cgen(l_type):
634 def cgen(l_type):
635 mapping = {'tr':'top-right-rounded-corner',
635 mapping = {'tr':'top-right-rounded-corner',
636 'tl':'top-left-rounded-corner',
636 'tl':'top-left-rounded-corner',
637 'br':'bottom-right-rounded-corner',
637 'br':'bottom-right-rounded-corner',
638 'bl':'bottom-left-rounded-corner'}
638 'bl':'bottom-left-rounded-corner'}
639 map_getter = lambda x:mapping[x]
639 map_getter = lambda x:mapping[x]
640
640
641 if l_type == 'a' and d_v:
641 if l_type == 'a' and d_v:
642 #case when added and deleted are present
642 #case when added and deleted are present
643 return ' '.join(map(map_getter, ['tl', 'bl']))
643 return ' '.join(map(map_getter, ['tl', 'bl']))
644
644
645 if l_type == 'a' and not d_v:
645 if l_type == 'a' and not d_v:
646 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
646 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
647
647
648 if l_type == 'd' and a_v:
648 if l_type == 'd' and a_v:
649 return ' '.join(map(map_getter, ['tr', 'br']))
649 return ' '.join(map(map_getter, ['tr', 'br']))
650
650
651 if l_type == 'd' and not a_v:
651 if l_type == 'd' and not a_v:
652 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
652 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
653
653
654
654
655
655
656 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
656 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (cgen('a'),
657 a_p, a_v)
657 a_p, a_v)
658 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
658 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (cgen('d'),
659 d_p, d_v)
659 d_p, d_v)
660 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
660 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
661
661
662
662
663 def urlify_text(text):
663 def urlify_text(text):
664 import re
664 import re
665
665
666 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
666 url_pat = re.compile(r'(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)')
667
667
668 def url_func(match_obj):
668 def url_func(match_obj):
669 url_full = match_obj.groups()[0]
669 url_full = match_obj.groups()[0]
670 return '<a href="%(url)s">%(url)s</a>' % ({'url':url_full})
670 return '<a href="%(url)s">%(url)s</a>' % ({'url':url_full})
671
671
672 return literal(url_pat.sub(url_func, text))
672 return literal(url_pat.sub(url_func, text))
@@ -1,158 +1,161
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
5 ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('Users'),h.url('users'))}
11 ${h.link_to(_('Users'),h.url('users'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} "${c.user.username}"
13 ${_('edit')} "${c.user.username}"
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <!-- end box / title -->
26 <!-- end box / title -->
27 ${h.form(url('update_user', id=c.user.user_id),method='put')}
27 ${h.form(url('update_user', id=c.user.user_id),method='put')}
28 <div class="form">
28 <div class="form">
29 <div class="field">
29 <div class="field">
30 <div class="gravatar_box">
30 <div class="gravatar_box">
31 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
31 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
32 <p>
32 <p>
33 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong><br/>
33 %if c.use_gravatar:
34 ${_('Using')} ${c.user.email}
34 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
35 </p>
35 <br/>${_('Using')} ${c.user.email}
36 %else:
37 <br/>${c.user.email}
38 %endif
36 </div>
39 </div>
37 </div>
40 </div>
38 <div class="field">
41 <div class="field">
39 <div class="label">
42 <div class="label">
40 <label>${_('API key')}</label> ${c.user.api_key}
43 <label>${_('API key')}</label> ${c.user.api_key}
41 </div>
44 </div>
42 </div>
45 </div>
43
46
44 <div class="fields">
47 <div class="fields">
45 <div class="field">
48 <div class="field">
46 <div class="label">
49 <div class="label">
47 <label for="username">${_('Username')}:</label>
50 <label for="username">${_('Username')}:</label>
48 </div>
51 </div>
49 <div class="input">
52 <div class="input">
50 ${h.text('username',class_='medium')}
53 ${h.text('username',class_='medium')}
51 </div>
54 </div>
52 </div>
55 </div>
53
56
54 <div class="field">
57 <div class="field">
55 <div class="label">
58 <div class="label">
56 <label for="ldap_dn">${_('LDAP DN')}:</label>
59 <label for="ldap_dn">${_('LDAP DN')}:</label>
57 </div>
60 </div>
58 <div class="input">
61 <div class="input">
59 ${h.text('ldap_dn',class_='medium')}
62 ${h.text('ldap_dn',class_='medium')}
60 </div>
63 </div>
61 </div>
64 </div>
62
65
63 <div class="field">
66 <div class="field">
64 <div class="label">
67 <div class="label">
65 <label for="new_password">${_('New password')}:</label>
68 <label for="new_password">${_('New password')}:</label>
66 </div>
69 </div>
67 <div class="input">
70 <div class="input">
68 ${h.password('new_password',class_='medium',autocomplete="off")}
71 ${h.password('new_password',class_='medium',autocomplete="off")}
69 </div>
72 </div>
70 </div>
73 </div>
71
74
72 <div class="field">
75 <div class="field">
73 <div class="label">
76 <div class="label">
74 <label for="password_confirmation">${_('New password confirmation')}:</label>
77 <label for="password_confirmation">${_('New password confirmation')}:</label>
75 </div>
78 </div>
76 <div class="input">
79 <div class="input">
77 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
80 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
78 </div>
81 </div>
79 </div>
82 </div>
80
83
81 <div class="field">
84 <div class="field">
82 <div class="label">
85 <div class="label">
83 <label for="name">${_('First Name')}:</label>
86 <label for="name">${_('First Name')}:</label>
84 </div>
87 </div>
85 <div class="input">
88 <div class="input">
86 ${h.text('name',class_='medium')}
89 ${h.text('name',class_='medium')}
87 </div>
90 </div>
88 </div>
91 </div>
89
92
90 <div class="field">
93 <div class="field">
91 <div class="label">
94 <div class="label">
92 <label for="lastname">${_('Last Name')}:</label>
95 <label for="lastname">${_('Last Name')}:</label>
93 </div>
96 </div>
94 <div class="input">
97 <div class="input">
95 ${h.text('lastname',class_='medium')}
98 ${h.text('lastname',class_='medium')}
96 </div>
99 </div>
97 </div>
100 </div>
98
101
99 <div class="field">
102 <div class="field">
100 <div class="label">
103 <div class="label">
101 <label for="email">${_('Email')}:</label>
104 <label for="email">${_('Email')}:</label>
102 </div>
105 </div>
103 <div class="input">
106 <div class="input">
104 ${h.text('email',class_='medium')}
107 ${h.text('email',class_='medium')}
105 </div>
108 </div>
106 </div>
109 </div>
107
110
108 <div class="field">
111 <div class="field">
109 <div class="label label-checkbox">
112 <div class="label label-checkbox">
110 <label for="active">${_('Active')}:</label>
113 <label for="active">${_('Active')}:</label>
111 </div>
114 </div>
112 <div class="checkboxes">
115 <div class="checkboxes">
113 ${h.checkbox('active',value=True)}
116 ${h.checkbox('active',value=True)}
114 </div>
117 </div>
115 </div>
118 </div>
116
119
117 <div class="field">
120 <div class="field">
118 <div class="label label-checkbox">
121 <div class="label label-checkbox">
119 <label for="admin">${_('Admin')}:</label>
122 <label for="admin">${_('Admin')}:</label>
120 </div>
123 </div>
121 <div class="checkboxes">
124 <div class="checkboxes">
122 ${h.checkbox('admin',value=True)}
125 ${h.checkbox('admin',value=True)}
123 </div>
126 </div>
124 </div>
127 </div>
125 <div class="buttons">
128 <div class="buttons">
126 ${h.submit('save',_('Save'),class_="ui-button")}
129 ${h.submit('save',_('Save'),class_="ui-button")}
127 ${h.reset('reset',_('Reset'),class_="ui-button")}
130 ${h.reset('reset',_('Reset'),class_="ui-button")}
128 </div>
131 </div>
129 </div>
132 </div>
130 </div>
133 </div>
131 ${h.end_form()}
134 ${h.end_form()}
132 </div>
135 </div>
133 <div class="box box-right">
136 <div class="box box-right">
134 <!-- box / title -->
137 <!-- box / title -->
135 <div class="title">
138 <div class="title">
136 <h5>${_('Permissions')}</h5>
139 <h5>${_('Permissions')}</h5>
137 </div>
140 </div>
138 ${h.form(url('user_perm', id=c.user.user_id),method='put')}
141 ${h.form(url('user_perm', id=c.user.user_id),method='put')}
139 <div class="form">
142 <div class="form">
140 <!-- fields -->
143 <!-- fields -->
141 <div class="fields">
144 <div class="fields">
142 <div class="field">
145 <div class="field">
143 <div class="label label-checkbox">
146 <div class="label label-checkbox">
144 <label for="create_repo_perm">${_('Create repositories')}:</label>
147 <label for="create_repo_perm">${_('Create repositories')}:</label>
145 </div>
148 </div>
146 <div class="checkboxes">
149 <div class="checkboxes">
147 ${h.checkbox('create_repo_perm',value=True)}
150 ${h.checkbox('create_repo_perm',value=True)}
148 </div>
151 </div>
149 </div>
152 </div>
150 <div class="buttons">
153 <div class="buttons">
151 ${h.submit('save',_('Save'),class_="ui-button")}
154 ${h.submit('save',_('Save'),class_="ui-button")}
152 ${h.reset('reset',_('Reset'),class_="ui-button")}
155 ${h.reset('reset',_('Reset'),class_="ui-button")}
153 </div>
156 </div>
154 </div>
157 </div>
155 </div>
158 </div>
156 ${h.end_form()}
159 ${h.end_form()}
157 </div>
160 </div>
158 </%def>
161 </%def>
@@ -1,222 +1,226
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('My account')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
5 ${_('My account')} ${c.rhodecode_user.username} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${_('My Account')}
9 ${_('My Account')}
10 </%def>
10 </%def>
11
11
12 <%def name="page_nav()">
12 <%def name="page_nav()">
13 ${self.menu('admin')}
13 ${self.menu('admin')}
14 </%def>
14 </%def>
15
15
16 <%def name="main()">
16 <%def name="main()">
17
17
18 <div class="box box-left">
18 <div class="box box-left">
19 <!-- box / title -->
19 <!-- box / title -->
20 <div class="title">
20 <div class="title">
21 ${self.breadcrumbs()}
21 ${self.breadcrumbs()}
22 </div>
22 </div>
23 <!-- end box / title -->
23 <!-- end box / title -->
24 <div>
24 <div>
25 ${h.form(url('admin_settings_my_account_update'),method='put')}
25 ${h.form(url('admin_settings_my_account_update'),method='put')}
26 <div class="form">
26 <div class="form">
27
27
28 <div class="field">
28 <div class="field">
29 <div class="gravatar_box">
29 <div class="gravatar_box">
30 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
30 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
31 <p>
31 <p>
32 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong><br/>
32 %if c.use_gravatar:
33 ${_('Using')} ${c.user.email}
33 <strong>${_('Change your avatar at')} <a href="http://gravatar.com">gravatar.com</a></strong>
34 <br/>${_('Using')} ${c.user.email}
35 %else:
36 <br/>${c.user.email}
37 %endif
34 </p>
38 </p>
35 </div>
39 </div>
36 </div>
40 </div>
37 <div class="field">
41 <div class="field">
38 <div class="label">
42 <div class="label">
39 <label>${_('API key')}</label> ${c.user.api_key}
43 <label>${_('API key')}</label> ${c.user.api_key}
40 </div>
44 </div>
41 </div>
45 </div>
42 <div class="fields">
46 <div class="fields">
43 <div class="field">
47 <div class="field">
44 <div class="label">
48 <div class="label">
45 <label for="username">${_('Username')}:</label>
49 <label for="username">${_('Username')}:</label>
46 </div>
50 </div>
47 <div class="input">
51 <div class="input">
48 ${h.text('username',class_="medium")}
52 ${h.text('username',class_="medium")}
49 </div>
53 </div>
50 </div>
54 </div>
51
55
52 <div class="field">
56 <div class="field">
53 <div class="label">
57 <div class="label">
54 <label for="new_password">${_('New password')}:</label>
58 <label for="new_password">${_('New password')}:</label>
55 </div>
59 </div>
56 <div class="input">
60 <div class="input">
57 ${h.password('new_password',class_="medium",autocomplete="off")}
61 ${h.password('new_password',class_="medium",autocomplete="off")}
58 </div>
62 </div>
59 </div>
63 </div>
60
64
61 <div class="field">
65 <div class="field">
62 <div class="label">
66 <div class="label">
63 <label for="password_confirmation">${_('New password confirmation')}:</label>
67 <label for="password_confirmation">${_('New password confirmation')}:</label>
64 </div>
68 </div>
65 <div class="input">
69 <div class="input">
66 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
70 ${h.password('password_confirmation',class_="medium",autocomplete="off")}
67 </div>
71 </div>
68 </div>
72 </div>
69
73
70 <div class="field">
74 <div class="field">
71 <div class="label">
75 <div class="label">
72 <label for="name">${_('First Name')}:</label>
76 <label for="name">${_('First Name')}:</label>
73 </div>
77 </div>
74 <div class="input">
78 <div class="input">
75 ${h.text('name',class_="medium")}
79 ${h.text('name',class_="medium")}
76 </div>
80 </div>
77 </div>
81 </div>
78
82
79 <div class="field">
83 <div class="field">
80 <div class="label">
84 <div class="label">
81 <label for="lastname">${_('Last Name')}:</label>
85 <label for="lastname">${_('Last Name')}:</label>
82 </div>
86 </div>
83 <div class="input">
87 <div class="input">
84 ${h.text('lastname',class_="medium")}
88 ${h.text('lastname',class_="medium")}
85 </div>
89 </div>
86 </div>
90 </div>
87
91
88 <div class="field">
92 <div class="field">
89 <div class="label">
93 <div class="label">
90 <label for="email">${_('Email')}:</label>
94 <label for="email">${_('Email')}:</label>
91 </div>
95 </div>
92 <div class="input">
96 <div class="input">
93 ${h.text('email',class_="medium")}
97 ${h.text('email',class_="medium")}
94 </div>
98 </div>
95 </div>
99 </div>
96
100
97 <div class="buttons">
101 <div class="buttons">
98 ${h.submit('save',_('Save'),class_="ui-button")}
102 ${h.submit('save',_('Save'),class_="ui-button")}
99 ${h.reset('reset',_('Reset'),class_="ui-button")}
103 ${h.reset('reset',_('Reset'),class_="ui-button")}
100 </div>
104 </div>
101 </div>
105 </div>
102 </div>
106 </div>
103 ${h.end_form()}
107 ${h.end_form()}
104 </div>
108 </div>
105 </div>
109 </div>
106
110
107 <div class="box box-right">
111 <div class="box box-right">
108 <!-- box / title -->
112 <!-- box / title -->
109 <div class="title">
113 <div class="title">
110 <h5>${_('My repositories')}
114 <h5>${_('My repositories')}
111 <input class="top-right-rounded-corner top-left-rounded-corner bottom-left-rounded-corner bottom-right-rounded-corner" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
115 <input class="top-right-rounded-corner top-left-rounded-corner bottom-left-rounded-corner bottom-right-rounded-corner" id="q_filter" size="15" type="text" name="filter" value="${_('quick filter...')}"/>
112 </h5>
116 </h5>
113 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
117 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
114 <ul class="links">
118 <ul class="links">
115 <li>
119 <li>
116 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
120 <span>${h.link_to(_('ADD REPOSITORY'),h.url('admin_settings_create_repository'))}</span>
117 </li>
121 </li>
118 </ul>
122 </ul>
119 %endif
123 %endif
120 </div>
124 </div>
121 <!-- end box / title -->
125 <!-- end box / title -->
122 <div class="table">
126 <div class="table">
123 <table>
127 <table>
124 <thead>
128 <thead>
125 <tr>
129 <tr>
126 <th class="left">${_('Name')}</th>
130 <th class="left">${_('Name')}</th>
127 <th class="left">${_('revision')}</th>
131 <th class="left">${_('revision')}</th>
128 <th colspan="2" class="left">${_('action')}</th>
132 <th colspan="2" class="left">${_('action')}</th>
129 </thead>
133 </thead>
130 <tbody>
134 <tbody>
131 %if c.user_repos:
135 %if c.user_repos:
132 %for repo in c.user_repos:
136 %for repo in c.user_repos:
133 <tr>
137 <tr>
134 <td>
138 <td>
135 %if repo['dbrepo']['repo_type'] =='hg':
139 %if repo['dbrepo']['repo_type'] =='hg':
136 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
140 <img class="icon" title="${_('Mercurial repository')}" alt="${_('Mercurial repository')}" src="${h.url("/images/icons/hgicon.png")}"/>
137 %elif repo['dbrepo']['repo_type'] =='git':
141 %elif repo['dbrepo']['repo_type'] =='git':
138 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
142 <img class="icon" title="${_('Git repository')}" alt="${_('Git repository')}" src="${h.url("/images/icons/giticon.png")}"/>
139 %else:
143 %else:
140
144
141 %endif
145 %endif
142 %if repo['dbrepo']['private']:
146 %if repo['dbrepo']['private']:
143 <img class="icon" alt="${_('private')}" src="${h.url("/images/icons/lock.png")}"/>
147 <img class="icon" alt="${_('private')}" src="${h.url("/images/icons/lock.png")}"/>
144 %else:
148 %else:
145 <img class="icon" alt="${_('public')}" src="${h.url("/images/icons/lock_open.png")}"/>
149 <img class="icon" alt="${_('public')}" src="${h.url("/images/icons/lock_open.png")}"/>
146 %endif
150 %endif
147
151
148 ${h.link_to(repo['name'], h.url('summary_home',repo_name=repo['name']),class_="repo_name")}
152 ${h.link_to(repo['name'], h.url('summary_home',repo_name=repo['name']),class_="repo_name")}
149 %if repo['dbrepo_fork']:
153 %if repo['dbrepo_fork']:
150 <a href="${h.url('summary_home',repo_name=repo['dbrepo_fork']['repo_name'])}">
154 <a href="${h.url('summary_home',repo_name=repo['dbrepo_fork']['repo_name'])}">
151 <img class="icon" alt="${_('public')}"
155 <img class="icon" alt="${_('public')}"
152 title="${_('Fork of')} ${repo['dbrepo_fork']['repo_name']}"
156 title="${_('Fork of')} ${repo['dbrepo_fork']['repo_name']}"
153 src="${h.url('/images/icons/arrow_divide.png')}"/></a>
157 src="${h.url('/images/icons/arrow_divide.png')}"/></a>
154 %endif
158 %endif
155 </td>
159 </td>
156 <td><span class="tooltip" title="${repo['last_change']}">${("r%s:%s") % (repo['rev'],h.short_id(repo['tip']))}</span></td>
160 <td><span class="tooltip" title="${repo['last_change']}">${("r%s:%s") % (repo['rev'],h.short_id(repo['tip']))}</span></td>
157 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
161 <td><a href="${h.url('repo_settings_home',repo_name=repo['name'])}" title="${_('edit')}"><img class="icon" alt="${_('private')}" src="${h.url('/images/icons/application_form_edit.png')}"/></a></td>
158 <td>
162 <td>
159 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
163 ${h.form(url('repo_settings_delete', repo_name=repo['name']),method='delete')}
160 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
164 ${h.submit('remove_%s' % repo['name'],'',class_="delete_icon action_button",onclick="return confirm('Confirm to delete this repository');")}
161 ${h.end_form()}
165 ${h.end_form()}
162 </td>
166 </td>
163 </tr>
167 </tr>
164 %endfor
168 %endfor
165 %else:
169 %else:
166 <div style="padding:5px 0px 10px 0px;">
170 <div style="padding:5px 0px 10px 0px;">
167 ${_('No repositories yet')}
171 ${_('No repositories yet')}
168 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
172 %if h.HasPermissionAny('hg.admin','hg.create.repository')():
169 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-button-small")}
173 ${h.link_to(_('create one now'),h.url('admin_settings_create_repository'),class_="ui-button-small")}
170 %endif
174 %endif
171 </div>
175 </div>
172 %endif
176 %endif
173 </tbody>
177 </tbody>
174 </table>
178 </table>
175 </div>
179 </div>
176
180
177 </div>
181 </div>
178 <script type="text/javascript">
182 <script type="text/javascript">
179 var D = YAHOO.util.Dom;
183 var D = YAHOO.util.Dom;
180 var E = YAHOO.util.Event;
184 var E = YAHOO.util.Event;
181 var S = YAHOO.util.Selector;
185 var S = YAHOO.util.Selector;
182
186
183 var q_filter = D.get('q_filter');
187 var q_filter = D.get('q_filter');
184 var F = YAHOO.namespace('q_filter');
188 var F = YAHOO.namespace('q_filter');
185
189
186 E.on(q_filter,'click',function(){
190 E.on(q_filter,'click',function(){
187 q_filter.value = '';
191 q_filter.value = '';
188 });
192 });
189
193
190 F.filterTimeout = null;
194 F.filterTimeout = null;
191
195
192 F.updateFilter = function() {
196 F.updateFilter = function() {
193 // Reset timeout
197 // Reset timeout
194 F.filterTimeout = null;
198 F.filterTimeout = null;
195
199
196 var obsolete = [];
200 var obsolete = [];
197 var nodes = S.query('div.table tr td a.repo_name');
201 var nodes = S.query('div.table tr td a.repo_name');
198 var req = q_filter.value.toLowerCase();
202 var req = q_filter.value.toLowerCase();
199 for (n in nodes){
203 for (n in nodes){
200 D.setStyle(nodes[n].parentNode.parentNode,'display','')
204 D.setStyle(nodes[n].parentNode.parentNode,'display','')
201 }
205 }
202 if (req){
206 if (req){
203 for (n in nodes){
207 for (n in nodes){
204 if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) {
208 if (nodes[n].innerHTML.toLowerCase().indexOf(req) == -1) {
205 obsolete.push(nodes[n]);
209 obsolete.push(nodes[n]);
206 }
210 }
207 }
211 }
208 if(obsolete){
212 if(obsolete){
209 for (n in obsolete){
213 for (n in obsolete){
210 D.setStyle(obsolete[n].parentNode.parentNode,'display','none');
214 D.setStyle(obsolete[n].parentNode.parentNode,'display','none');
211 }
215 }
212 }
216 }
213 }
217 }
214 }
218 }
215
219
216 E.on(q_filter,'keyup',function(e){
220 E.on(q_filter,'keyup',function(e){
217 clearTimeout(F.filterTimeout);
221 clearTimeout(F.filterTimeout);
218 F.filterTimeout = setTimeout(F.updateFilter,600);
222 F.filterTimeout = setTimeout(F.updateFilter,600);
219 });
223 });
220
224
221 </script>
225 </script>
222 </%def>
226 </%def>
General Comments 0
You need to be logged in to leave comments. Login now