##// END OF EJS Templates
templates: use a single helper to select activated menu entries in templates.
marcink -
r4062:42f2ce8c default
parent child Browse files
Show More
@@ -1,2126 +1,2138 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Helper functions
22 Helper functions
23
23
24 Consists of functions to typically be used within templates, but also
24 Consists of functions to typically be used within templates, but also
25 available to Controllers. This module is available to both as 'h'.
25 available to Controllers. This module is available to both as 'h'.
26 """
26 """
27
27
28 import os
28 import os
29 import random
29 import random
30 import hashlib
30 import hashlib
31 import StringIO
31 import StringIO
32 import textwrap
32 import textwrap
33 import urllib
33 import urllib
34 import math
34 import math
35 import logging
35 import logging
36 import re
36 import re
37 import time
37 import time
38 import string
38 import string
39 import hashlib
39 import hashlib
40 from collections import OrderedDict
40 from collections import OrderedDict
41
41
42 import pygments
42 import pygments
43 import itertools
43 import itertools
44 import fnmatch
44 import fnmatch
45 import bleach
45 import bleach
46
46
47 from pyramid import compat
47 from pyramid import compat
48 from datetime import datetime
48 from datetime import datetime
49 from functools import partial
49 from functools import partial
50 from pygments.formatters.html import HtmlFormatter
50 from pygments.formatters.html import HtmlFormatter
51 from pygments.lexers import (
51 from pygments.lexers import (
52 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
52 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
53
53
54 from pyramid.threadlocal import get_current_request
54 from pyramid.threadlocal import get_current_request
55
55
56 from webhelpers.html import literal, HTML, escape
56 from webhelpers.html import literal, HTML, escape
57 from webhelpers.html.tools import *
57 from webhelpers.html.tools import *
58 from webhelpers.html.builder import make_tag
58 from webhelpers.html.builder import make_tag
59 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
59 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
60 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
60 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
61 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
61 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
62 submit, text, password, textarea, title, ul, xml_declaration, radio
62 submit, text, password, textarea, title, ul, xml_declaration, radio
63 from webhelpers.html.tools import auto_link, button_to, highlight, \
63 from webhelpers.html.tools import auto_link, button_to, highlight, \
64 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
64 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
65 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
65 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
66 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
66 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
67 replace_whitespace, urlify, truncate, wrap_paragraphs
67 replace_whitespace, urlify, truncate, wrap_paragraphs
68 from webhelpers.date import time_ago_in_words
68 from webhelpers.date import time_ago_in_words
69 from webhelpers.paginate import Page as _Page
69 from webhelpers.paginate import Page as _Page
70 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
70 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
71 convert_boolean_attrs, NotGiven, _make_safe_id_component
71 convert_boolean_attrs, NotGiven, _make_safe_id_component
72 from webhelpers2.number import format_byte_size
72 from webhelpers2.number import format_byte_size
73
73
74 from rhodecode.lib.action_parser import action_parser
74 from rhodecode.lib.action_parser import action_parser
75 from rhodecode.lib.ext_json import json
75 from rhodecode.lib.ext_json import json
76 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
76 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
77 from rhodecode.lib.utils2 import (
77 from rhodecode.lib.utils2 import (
78 str2bool, safe_unicode, safe_str,
78 str2bool, safe_unicode, safe_str,
79 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime,
79 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime,
80 AttributeDict, safe_int, md5, md5_safe, get_host_info)
80 AttributeDict, safe_int, md5, md5_safe, get_host_info)
81 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
81 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
82 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
82 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
83 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
83 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
84 from rhodecode.lib.index.search_utils import get_matching_line_offsets
84 from rhodecode.lib.index.search_utils import get_matching_line_offsets
85 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
85 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
86 from rhodecode.model.changeset_status import ChangesetStatusModel
86 from rhodecode.model.changeset_status import ChangesetStatusModel
87 from rhodecode.model.db import Permission, User, Repository
87 from rhodecode.model.db import Permission, User, Repository
88 from rhodecode.model.repo_group import RepoGroupModel
88 from rhodecode.model.repo_group import RepoGroupModel
89 from rhodecode.model.settings import IssueTrackerSettingsModel
89 from rhodecode.model.settings import IssueTrackerSettingsModel
90
90
91
91
92 log = logging.getLogger(__name__)
92 log = logging.getLogger(__name__)
93
93
94
94
95 DEFAULT_USER = User.DEFAULT_USER
95 DEFAULT_USER = User.DEFAULT_USER
96 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
96 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
97
97
98
98
99 def asset(path, ver=None, **kwargs):
99 def asset(path, ver=None, **kwargs):
100 """
100 """
101 Helper to generate a static asset file path for rhodecode assets
101 Helper to generate a static asset file path for rhodecode assets
102
102
103 eg. h.asset('images/image.png', ver='3923')
103 eg. h.asset('images/image.png', ver='3923')
104
104
105 :param path: path of asset
105 :param path: path of asset
106 :param ver: optional version query param to append as ?ver=
106 :param ver: optional version query param to append as ?ver=
107 """
107 """
108 request = get_current_request()
108 request = get_current_request()
109 query = {}
109 query = {}
110 query.update(kwargs)
110 query.update(kwargs)
111 if ver:
111 if ver:
112 query = {'ver': ver}
112 query = {'ver': ver}
113 return request.static_path(
113 return request.static_path(
114 'rhodecode:public/{}'.format(path), _query=query)
114 'rhodecode:public/{}'.format(path), _query=query)
115
115
116
116
117 default_html_escape_table = {
117 default_html_escape_table = {
118 ord('&'): u'&amp;',
118 ord('&'): u'&amp;',
119 ord('<'): u'&lt;',
119 ord('<'): u'&lt;',
120 ord('>'): u'&gt;',
120 ord('>'): u'&gt;',
121 ord('"'): u'&quot;',
121 ord('"'): u'&quot;',
122 ord("'"): u'&#39;',
122 ord("'"): u'&#39;',
123 }
123 }
124
124
125
125
126 def html_escape(text, html_escape_table=default_html_escape_table):
126 def html_escape(text, html_escape_table=default_html_escape_table):
127 """Produce entities within text."""
127 """Produce entities within text."""
128 return text.translate(html_escape_table)
128 return text.translate(html_escape_table)
129
129
130
130
131 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
131 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
132 """
132 """
133 Truncate string ``s`` at the first occurrence of ``sub``.
133 Truncate string ``s`` at the first occurrence of ``sub``.
134
134
135 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
135 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
136 """
136 """
137 suffix_if_chopped = suffix_if_chopped or ''
137 suffix_if_chopped = suffix_if_chopped or ''
138 pos = s.find(sub)
138 pos = s.find(sub)
139 if pos == -1:
139 if pos == -1:
140 return s
140 return s
141
141
142 if inclusive:
142 if inclusive:
143 pos += len(sub)
143 pos += len(sub)
144
144
145 chopped = s[:pos]
145 chopped = s[:pos]
146 left = s[pos:].strip()
146 left = s[pos:].strip()
147
147
148 if left and suffix_if_chopped:
148 if left and suffix_if_chopped:
149 chopped += suffix_if_chopped
149 chopped += suffix_if_chopped
150
150
151 return chopped
151 return chopped
152
152
153
153
154 def shorter(text, size=20, prefix=False):
154 def shorter(text, size=20, prefix=False):
155 postfix = '...'
155 postfix = '...'
156 if len(text) > size:
156 if len(text) > size:
157 if prefix:
157 if prefix:
158 # shorten in front
158 # shorten in front
159 return postfix + text[-(size - len(postfix)):]
159 return postfix + text[-(size - len(postfix)):]
160 else:
160 else:
161 return text[:size - len(postfix)] + postfix
161 return text[:size - len(postfix)] + postfix
162 return text
162 return text
163
163
164
164
165 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
165 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
166 """
166 """
167 Reset button
167 Reset button
168 """
168 """
169 _set_input_attrs(attrs, type, name, value)
169 _set_input_attrs(attrs, type, name, value)
170 _set_id_attr(attrs, id, name)
170 _set_id_attr(attrs, id, name)
171 convert_boolean_attrs(attrs, ["disabled"])
171 convert_boolean_attrs(attrs, ["disabled"])
172 return HTML.input(**attrs)
172 return HTML.input(**attrs)
173
173
174 reset = _reset
174 reset = _reset
175 safeid = _make_safe_id_component
175 safeid = _make_safe_id_component
176
176
177
177
178 def branding(name, length=40):
178 def branding(name, length=40):
179 return truncate(name, length, indicator="")
179 return truncate(name, length, indicator="")
180
180
181
181
182 def FID(raw_id, path):
182 def FID(raw_id, path):
183 """
183 """
184 Creates a unique ID for filenode based on it's hash of path and commit
184 Creates a unique ID for filenode based on it's hash of path and commit
185 it's safe to use in urls
185 it's safe to use in urls
186
186
187 :param raw_id:
187 :param raw_id:
188 :param path:
188 :param path:
189 """
189 """
190
190
191 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
191 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
192
192
193
193
194 class _GetError(object):
194 class _GetError(object):
195 """Get error from form_errors, and represent it as span wrapped error
195 """Get error from form_errors, and represent it as span wrapped error
196 message
196 message
197
197
198 :param field_name: field to fetch errors for
198 :param field_name: field to fetch errors for
199 :param form_errors: form errors dict
199 :param form_errors: form errors dict
200 """
200 """
201
201
202 def __call__(self, field_name, form_errors):
202 def __call__(self, field_name, form_errors):
203 tmpl = """<span class="error_msg">%s</span>"""
203 tmpl = """<span class="error_msg">%s</span>"""
204 if form_errors and field_name in form_errors:
204 if form_errors and field_name in form_errors:
205 return literal(tmpl % form_errors.get(field_name))
205 return literal(tmpl % form_errors.get(field_name))
206
206
207
207
208 get_error = _GetError()
208 get_error = _GetError()
209
209
210
210
211 class _ToolTip(object):
211 class _ToolTip(object):
212
212
213 def __call__(self, tooltip_title, trim_at=50):
213 def __call__(self, tooltip_title, trim_at=50):
214 """
214 """
215 Special function just to wrap our text into nice formatted
215 Special function just to wrap our text into nice formatted
216 autowrapped text
216 autowrapped text
217
217
218 :param tooltip_title:
218 :param tooltip_title:
219 """
219 """
220 tooltip_title = escape(tooltip_title)
220 tooltip_title = escape(tooltip_title)
221 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
221 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
222 return tooltip_title
222 return tooltip_title
223
223
224
224
225 tooltip = _ToolTip()
225 tooltip = _ToolTip()
226
226
227 files_icon = u'<i class="file-breadcrumb-copy tooltip icon-clipboard clipboard-action" data-clipboard-text="{}" title="Copy the full path"></i>'
227 files_icon = u'<i class="file-breadcrumb-copy tooltip icon-clipboard clipboard-action" data-clipboard-text="{}" title="Copy the full path"></i>'
228
228
229
229
230 def files_breadcrumbs(repo_name, commit_id, file_path, at_ref=None, limit_items=False, linkify_last_item=False):
230 def files_breadcrumbs(repo_name, commit_id, file_path, at_ref=None, limit_items=False, linkify_last_item=False):
231 if isinstance(file_path, str):
231 if isinstance(file_path, str):
232 file_path = safe_unicode(file_path)
232 file_path = safe_unicode(file_path)
233
233
234 route_qry = {'at': at_ref} if at_ref else None
234 route_qry = {'at': at_ref} if at_ref else None
235
235
236 # first segment is a `..` link to repo files
236 # first segment is a `..` link to repo files
237 root_name = literal(u'<i class="icon-home"></i>')
237 root_name = literal(u'<i class="icon-home"></i>')
238 url_segments = [
238 url_segments = [
239 link_to(
239 link_to(
240 root_name,
240 root_name,
241 route_path(
241 route_path(
242 'repo_files',
242 'repo_files',
243 repo_name=repo_name,
243 repo_name=repo_name,
244 commit_id=commit_id,
244 commit_id=commit_id,
245 f_path='',
245 f_path='',
246 _query=route_qry),
246 _query=route_qry),
247 )]
247 )]
248
248
249 path_segments = file_path.split('/')
249 path_segments = file_path.split('/')
250 last_cnt = len(path_segments) - 1
250 last_cnt = len(path_segments) - 1
251 for cnt, segment in enumerate(path_segments):
251 for cnt, segment in enumerate(path_segments):
252 if not segment:
252 if not segment:
253 continue
253 continue
254 segment_html = escape(segment)
254 segment_html = escape(segment)
255
255
256 last_item = cnt == last_cnt
256 last_item = cnt == last_cnt
257
257
258 if last_item and linkify_last_item is False:
258 if last_item and linkify_last_item is False:
259 # plain version
259 # plain version
260 url_segments.append(segment_html)
260 url_segments.append(segment_html)
261 else:
261 else:
262 url_segments.append(
262 url_segments.append(
263 link_to(
263 link_to(
264 segment_html,
264 segment_html,
265 route_path(
265 route_path(
266 'repo_files',
266 'repo_files',
267 repo_name=repo_name,
267 repo_name=repo_name,
268 commit_id=commit_id,
268 commit_id=commit_id,
269 f_path='/'.join(path_segments[:cnt + 1]),
269 f_path='/'.join(path_segments[:cnt + 1]),
270 _query=route_qry),
270 _query=route_qry),
271 ))
271 ))
272
272
273 limited_url_segments = url_segments[:1] + ['...'] + url_segments[-5:]
273 limited_url_segments = url_segments[:1] + ['...'] + url_segments[-5:]
274 if limit_items and len(limited_url_segments) < len(url_segments):
274 if limit_items and len(limited_url_segments) < len(url_segments):
275 url_segments = limited_url_segments
275 url_segments = limited_url_segments
276
276
277 full_path = file_path
277 full_path = file_path
278 icon = files_icon.format(escape(full_path))
278 icon = files_icon.format(escape(full_path))
279 if file_path == '':
279 if file_path == '':
280 return root_name
280 return root_name
281 else:
281 else:
282 return literal(' / '.join(url_segments) + icon)
282 return literal(' / '.join(url_segments) + icon)
283
283
284
284
285 def files_url_data(request):
285 def files_url_data(request):
286 matchdict = request.matchdict
286 matchdict = request.matchdict
287
287
288 if 'f_path' not in matchdict:
288 if 'f_path' not in matchdict:
289 matchdict['f_path'] = ''
289 matchdict['f_path'] = ''
290
290
291 if 'commit_id' not in matchdict:
291 if 'commit_id' not in matchdict:
292 matchdict['commit_id'] = 'tip'
292 matchdict['commit_id'] = 'tip'
293
293
294 return json.dumps(matchdict)
294 return json.dumps(matchdict)
295
295
296
296
297 def code_highlight(code, lexer, formatter, use_hl_filter=False):
297 def code_highlight(code, lexer, formatter, use_hl_filter=False):
298 """
298 """
299 Lex ``code`` with ``lexer`` and format it with the formatter ``formatter``.
299 Lex ``code`` with ``lexer`` and format it with the formatter ``formatter``.
300
300
301 If ``outfile`` is given and a valid file object (an object
301 If ``outfile`` is given and a valid file object (an object
302 with a ``write`` method), the result will be written to it, otherwise
302 with a ``write`` method), the result will be written to it, otherwise
303 it is returned as a string.
303 it is returned as a string.
304 """
304 """
305 if use_hl_filter:
305 if use_hl_filter:
306 # add HL filter
306 # add HL filter
307 from rhodecode.lib.index import search_utils
307 from rhodecode.lib.index import search_utils
308 lexer.add_filter(search_utils.ElasticSearchHLFilter())
308 lexer.add_filter(search_utils.ElasticSearchHLFilter())
309 return pygments.format(pygments.lex(code, lexer), formatter)
309 return pygments.format(pygments.lex(code, lexer), formatter)
310
310
311
311
312 class CodeHtmlFormatter(HtmlFormatter):
312 class CodeHtmlFormatter(HtmlFormatter):
313 """
313 """
314 My code Html Formatter for source codes
314 My code Html Formatter for source codes
315 """
315 """
316
316
317 def wrap(self, source, outfile):
317 def wrap(self, source, outfile):
318 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
318 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
319
319
320 def _wrap_code(self, source):
320 def _wrap_code(self, source):
321 for cnt, it in enumerate(source):
321 for cnt, it in enumerate(source):
322 i, t = it
322 i, t = it
323 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
323 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
324 yield i, t
324 yield i, t
325
325
326 def _wrap_tablelinenos(self, inner):
326 def _wrap_tablelinenos(self, inner):
327 dummyoutfile = StringIO.StringIO()
327 dummyoutfile = StringIO.StringIO()
328 lncount = 0
328 lncount = 0
329 for t, line in inner:
329 for t, line in inner:
330 if t:
330 if t:
331 lncount += 1
331 lncount += 1
332 dummyoutfile.write(line)
332 dummyoutfile.write(line)
333
333
334 fl = self.linenostart
334 fl = self.linenostart
335 mw = len(str(lncount + fl - 1))
335 mw = len(str(lncount + fl - 1))
336 sp = self.linenospecial
336 sp = self.linenospecial
337 st = self.linenostep
337 st = self.linenostep
338 la = self.lineanchors
338 la = self.lineanchors
339 aln = self.anchorlinenos
339 aln = self.anchorlinenos
340 nocls = self.noclasses
340 nocls = self.noclasses
341 if sp:
341 if sp:
342 lines = []
342 lines = []
343
343
344 for i in range(fl, fl + lncount):
344 for i in range(fl, fl + lncount):
345 if i % st == 0:
345 if i % st == 0:
346 if i % sp == 0:
346 if i % sp == 0:
347 if aln:
347 if aln:
348 lines.append('<a href="#%s%d" class="special">%*d</a>' %
348 lines.append('<a href="#%s%d" class="special">%*d</a>' %
349 (la, i, mw, i))
349 (la, i, mw, i))
350 else:
350 else:
351 lines.append('<span class="special">%*d</span>' % (mw, i))
351 lines.append('<span class="special">%*d</span>' % (mw, i))
352 else:
352 else:
353 if aln:
353 if aln:
354 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
354 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
355 else:
355 else:
356 lines.append('%*d' % (mw, i))
356 lines.append('%*d' % (mw, i))
357 else:
357 else:
358 lines.append('')
358 lines.append('')
359 ls = '\n'.join(lines)
359 ls = '\n'.join(lines)
360 else:
360 else:
361 lines = []
361 lines = []
362 for i in range(fl, fl + lncount):
362 for i in range(fl, fl + lncount):
363 if i % st == 0:
363 if i % st == 0:
364 if aln:
364 if aln:
365 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
365 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
366 else:
366 else:
367 lines.append('%*d' % (mw, i))
367 lines.append('%*d' % (mw, i))
368 else:
368 else:
369 lines.append('')
369 lines.append('')
370 ls = '\n'.join(lines)
370 ls = '\n'.join(lines)
371
371
372 # in case you wonder about the seemingly redundant <div> here: since the
372 # in case you wonder about the seemingly redundant <div> here: since the
373 # content in the other cell also is wrapped in a div, some browsers in
373 # content in the other cell also is wrapped in a div, some browsers in
374 # some configurations seem to mess up the formatting...
374 # some configurations seem to mess up the formatting...
375 if nocls:
375 if nocls:
376 yield 0, ('<table class="%stable">' % self.cssclass +
376 yield 0, ('<table class="%stable">' % self.cssclass +
377 '<tr><td><div class="linenodiv" '
377 '<tr><td><div class="linenodiv" '
378 'style="background-color: #f0f0f0; padding-right: 10px">'
378 'style="background-color: #f0f0f0; padding-right: 10px">'
379 '<pre style="line-height: 125%">' +
379 '<pre style="line-height: 125%">' +
380 ls + '</pre></div></td><td id="hlcode" class="code">')
380 ls + '</pre></div></td><td id="hlcode" class="code">')
381 else:
381 else:
382 yield 0, ('<table class="%stable">' % self.cssclass +
382 yield 0, ('<table class="%stable">' % self.cssclass +
383 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
383 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
384 ls + '</pre></div></td><td id="hlcode" class="code">')
384 ls + '</pre></div></td><td id="hlcode" class="code">')
385 yield 0, dummyoutfile.getvalue()
385 yield 0, dummyoutfile.getvalue()
386 yield 0, '</td></tr></table>'
386 yield 0, '</td></tr></table>'
387
387
388
388
389 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
389 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
390 def __init__(self, **kw):
390 def __init__(self, **kw):
391 # only show these line numbers if set
391 # only show these line numbers if set
392 self.only_lines = kw.pop('only_line_numbers', [])
392 self.only_lines = kw.pop('only_line_numbers', [])
393 self.query_terms = kw.pop('query_terms', [])
393 self.query_terms = kw.pop('query_terms', [])
394 self.max_lines = kw.pop('max_lines', 5)
394 self.max_lines = kw.pop('max_lines', 5)
395 self.line_context = kw.pop('line_context', 3)
395 self.line_context = kw.pop('line_context', 3)
396 self.url = kw.pop('url', None)
396 self.url = kw.pop('url', None)
397
397
398 super(CodeHtmlFormatter, self).__init__(**kw)
398 super(CodeHtmlFormatter, self).__init__(**kw)
399
399
400 def _wrap_code(self, source):
400 def _wrap_code(self, source):
401 for cnt, it in enumerate(source):
401 for cnt, it in enumerate(source):
402 i, t = it
402 i, t = it
403 t = '<pre>%s</pre>' % t
403 t = '<pre>%s</pre>' % t
404 yield i, t
404 yield i, t
405
405
406 def _wrap_tablelinenos(self, inner):
406 def _wrap_tablelinenos(self, inner):
407 yield 0, '<table class="code-highlight %stable">' % self.cssclass
407 yield 0, '<table class="code-highlight %stable">' % self.cssclass
408
408
409 last_shown_line_number = 0
409 last_shown_line_number = 0
410 current_line_number = 1
410 current_line_number = 1
411
411
412 for t, line in inner:
412 for t, line in inner:
413 if not t:
413 if not t:
414 yield t, line
414 yield t, line
415 continue
415 continue
416
416
417 if current_line_number in self.only_lines:
417 if current_line_number in self.only_lines:
418 if last_shown_line_number + 1 != current_line_number:
418 if last_shown_line_number + 1 != current_line_number:
419 yield 0, '<tr>'
419 yield 0, '<tr>'
420 yield 0, '<td class="line">...</td>'
420 yield 0, '<td class="line">...</td>'
421 yield 0, '<td id="hlcode" class="code"></td>'
421 yield 0, '<td id="hlcode" class="code"></td>'
422 yield 0, '</tr>'
422 yield 0, '</tr>'
423
423
424 yield 0, '<tr>'
424 yield 0, '<tr>'
425 if self.url:
425 if self.url:
426 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
426 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
427 self.url, current_line_number, current_line_number)
427 self.url, current_line_number, current_line_number)
428 else:
428 else:
429 yield 0, '<td class="line"><a href="">%i</a></td>' % (
429 yield 0, '<td class="line"><a href="">%i</a></td>' % (
430 current_line_number)
430 current_line_number)
431 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
431 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
432 yield 0, '</tr>'
432 yield 0, '</tr>'
433
433
434 last_shown_line_number = current_line_number
434 last_shown_line_number = current_line_number
435
435
436 current_line_number += 1
436 current_line_number += 1
437
437
438 yield 0, '</table>'
438 yield 0, '</table>'
439
439
440
440
441 def hsv_to_rgb(h, s, v):
441 def hsv_to_rgb(h, s, v):
442 """ Convert hsv color values to rgb """
442 """ Convert hsv color values to rgb """
443
443
444 if s == 0.0:
444 if s == 0.0:
445 return v, v, v
445 return v, v, v
446 i = int(h * 6.0) # XXX assume int() truncates!
446 i = int(h * 6.0) # XXX assume int() truncates!
447 f = (h * 6.0) - i
447 f = (h * 6.0) - i
448 p = v * (1.0 - s)
448 p = v * (1.0 - s)
449 q = v * (1.0 - s * f)
449 q = v * (1.0 - s * f)
450 t = v * (1.0 - s * (1.0 - f))
450 t = v * (1.0 - s * (1.0 - f))
451 i = i % 6
451 i = i % 6
452 if i == 0:
452 if i == 0:
453 return v, t, p
453 return v, t, p
454 if i == 1:
454 if i == 1:
455 return q, v, p
455 return q, v, p
456 if i == 2:
456 if i == 2:
457 return p, v, t
457 return p, v, t
458 if i == 3:
458 if i == 3:
459 return p, q, v
459 return p, q, v
460 if i == 4:
460 if i == 4:
461 return t, p, v
461 return t, p, v
462 if i == 5:
462 if i == 5:
463 return v, p, q
463 return v, p, q
464
464
465
465
466 def unique_color_generator(n=10000, saturation=0.10, lightness=0.95):
466 def unique_color_generator(n=10000, saturation=0.10, lightness=0.95):
467 """
467 """
468 Generator for getting n of evenly distributed colors using
468 Generator for getting n of evenly distributed colors using
469 hsv color and golden ratio. It always return same order of colors
469 hsv color and golden ratio. It always return same order of colors
470
470
471 :param n: number of colors to generate
471 :param n: number of colors to generate
472 :param saturation: saturation of returned colors
472 :param saturation: saturation of returned colors
473 :param lightness: lightness of returned colors
473 :param lightness: lightness of returned colors
474 :returns: RGB tuple
474 :returns: RGB tuple
475 """
475 """
476
476
477 golden_ratio = 0.618033988749895
477 golden_ratio = 0.618033988749895
478 h = 0.22717784590367374
478 h = 0.22717784590367374
479
479
480 for _ in xrange(n):
480 for _ in xrange(n):
481 h += golden_ratio
481 h += golden_ratio
482 h %= 1
482 h %= 1
483 HSV_tuple = [h, saturation, lightness]
483 HSV_tuple = [h, saturation, lightness]
484 RGB_tuple = hsv_to_rgb(*HSV_tuple)
484 RGB_tuple = hsv_to_rgb(*HSV_tuple)
485 yield map(lambda x: str(int(x * 256)), RGB_tuple)
485 yield map(lambda x: str(int(x * 256)), RGB_tuple)
486
486
487
487
488 def color_hasher(n=10000, saturation=0.10, lightness=0.95):
488 def color_hasher(n=10000, saturation=0.10, lightness=0.95):
489 """
489 """
490 Returns a function which when called with an argument returns a unique
490 Returns a function which when called with an argument returns a unique
491 color for that argument, eg.
491 color for that argument, eg.
492
492
493 :param n: number of colors to generate
493 :param n: number of colors to generate
494 :param saturation: saturation of returned colors
494 :param saturation: saturation of returned colors
495 :param lightness: lightness of returned colors
495 :param lightness: lightness of returned colors
496 :returns: css RGB string
496 :returns: css RGB string
497
497
498 >>> color_hash = color_hasher()
498 >>> color_hash = color_hasher()
499 >>> color_hash('hello')
499 >>> color_hash('hello')
500 'rgb(34, 12, 59)'
500 'rgb(34, 12, 59)'
501 >>> color_hash('hello')
501 >>> color_hash('hello')
502 'rgb(34, 12, 59)'
502 'rgb(34, 12, 59)'
503 >>> color_hash('other')
503 >>> color_hash('other')
504 'rgb(90, 224, 159)'
504 'rgb(90, 224, 159)'
505 """
505 """
506
506
507 color_dict = {}
507 color_dict = {}
508 cgenerator = unique_color_generator(
508 cgenerator = unique_color_generator(
509 saturation=saturation, lightness=lightness)
509 saturation=saturation, lightness=lightness)
510
510
511 def get_color_string(thing):
511 def get_color_string(thing):
512 if thing in color_dict:
512 if thing in color_dict:
513 col = color_dict[thing]
513 col = color_dict[thing]
514 else:
514 else:
515 col = color_dict[thing] = cgenerator.next()
515 col = color_dict[thing] = cgenerator.next()
516 return "rgb(%s)" % (', '.join(col))
516 return "rgb(%s)" % (', '.join(col))
517
517
518 return get_color_string
518 return get_color_string
519
519
520
520
521 def get_lexer_safe(mimetype=None, filepath=None):
521 def get_lexer_safe(mimetype=None, filepath=None):
522 """
522 """
523 Tries to return a relevant pygments lexer using mimetype/filepath name,
523 Tries to return a relevant pygments lexer using mimetype/filepath name,
524 defaulting to plain text if none could be found
524 defaulting to plain text if none could be found
525 """
525 """
526 lexer = None
526 lexer = None
527 try:
527 try:
528 if mimetype:
528 if mimetype:
529 lexer = get_lexer_for_mimetype(mimetype)
529 lexer = get_lexer_for_mimetype(mimetype)
530 if not lexer:
530 if not lexer:
531 lexer = get_lexer_for_filename(filepath)
531 lexer = get_lexer_for_filename(filepath)
532 except pygments.util.ClassNotFound:
532 except pygments.util.ClassNotFound:
533 pass
533 pass
534
534
535 if not lexer:
535 if not lexer:
536 lexer = get_lexer_by_name('text')
536 lexer = get_lexer_by_name('text')
537
537
538 return lexer
538 return lexer
539
539
540
540
541 def get_lexer_for_filenode(filenode):
541 def get_lexer_for_filenode(filenode):
542 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
542 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
543 return lexer
543 return lexer
544
544
545
545
546 def pygmentize(filenode, **kwargs):
546 def pygmentize(filenode, **kwargs):
547 """
547 """
548 pygmentize function using pygments
548 pygmentize function using pygments
549
549
550 :param filenode:
550 :param filenode:
551 """
551 """
552 lexer = get_lexer_for_filenode(filenode)
552 lexer = get_lexer_for_filenode(filenode)
553 return literal(code_highlight(filenode.content, lexer,
553 return literal(code_highlight(filenode.content, lexer,
554 CodeHtmlFormatter(**kwargs)))
554 CodeHtmlFormatter(**kwargs)))
555
555
556
556
557 def is_following_repo(repo_name, user_id):
557 def is_following_repo(repo_name, user_id):
558 from rhodecode.model.scm import ScmModel
558 from rhodecode.model.scm import ScmModel
559 return ScmModel().is_following_repo(repo_name, user_id)
559 return ScmModel().is_following_repo(repo_name, user_id)
560
560
561
561
562 class _Message(object):
562 class _Message(object):
563 """A message returned by ``Flash.pop_messages()``.
563 """A message returned by ``Flash.pop_messages()``.
564
564
565 Converting the message to a string returns the message text. Instances
565 Converting the message to a string returns the message text. Instances
566 also have the following attributes:
566 also have the following attributes:
567
567
568 * ``message``: the message text.
568 * ``message``: the message text.
569 * ``category``: the category specified when the message was created.
569 * ``category``: the category specified when the message was created.
570 """
570 """
571
571
572 def __init__(self, category, message):
572 def __init__(self, category, message):
573 self.category = category
573 self.category = category
574 self.message = message
574 self.message = message
575
575
576 def __str__(self):
576 def __str__(self):
577 return self.message
577 return self.message
578
578
579 __unicode__ = __str__
579 __unicode__ = __str__
580
580
581 def __html__(self):
581 def __html__(self):
582 return escape(safe_unicode(self.message))
582 return escape(safe_unicode(self.message))
583
583
584
584
585 class Flash(object):
585 class Flash(object):
586 # List of allowed categories. If None, allow any category.
586 # List of allowed categories. If None, allow any category.
587 categories = ["warning", "notice", "error", "success"]
587 categories = ["warning", "notice", "error", "success"]
588
588
589 # Default category if none is specified.
589 # Default category if none is specified.
590 default_category = "notice"
590 default_category = "notice"
591
591
592 def __init__(self, session_key="flash", categories=None,
592 def __init__(self, session_key="flash", categories=None,
593 default_category=None):
593 default_category=None):
594 """
594 """
595 Instantiate a ``Flash`` object.
595 Instantiate a ``Flash`` object.
596
596
597 ``session_key`` is the key to save the messages under in the user's
597 ``session_key`` is the key to save the messages under in the user's
598 session.
598 session.
599
599
600 ``categories`` is an optional list which overrides the default list
600 ``categories`` is an optional list which overrides the default list
601 of categories.
601 of categories.
602
602
603 ``default_category`` overrides the default category used for messages
603 ``default_category`` overrides the default category used for messages
604 when none is specified.
604 when none is specified.
605 """
605 """
606 self.session_key = session_key
606 self.session_key = session_key
607 if categories is not None:
607 if categories is not None:
608 self.categories = categories
608 self.categories = categories
609 if default_category is not None:
609 if default_category is not None:
610 self.default_category = default_category
610 self.default_category = default_category
611 if self.categories and self.default_category not in self.categories:
611 if self.categories and self.default_category not in self.categories:
612 raise ValueError(
612 raise ValueError(
613 "unrecognized default category %r" % (self.default_category,))
613 "unrecognized default category %r" % (self.default_category,))
614
614
615 def pop_messages(self, session=None, request=None):
615 def pop_messages(self, session=None, request=None):
616 """
616 """
617 Return all accumulated messages and delete them from the session.
617 Return all accumulated messages and delete them from the session.
618
618
619 The return value is a list of ``Message`` objects.
619 The return value is a list of ``Message`` objects.
620 """
620 """
621 messages = []
621 messages = []
622
622
623 if not session:
623 if not session:
624 if not request:
624 if not request:
625 request = get_current_request()
625 request = get_current_request()
626 session = request.session
626 session = request.session
627
627
628 # Pop the 'old' pylons flash messages. They are tuples of the form
628 # Pop the 'old' pylons flash messages. They are tuples of the form
629 # (category, message)
629 # (category, message)
630 for cat, msg in session.pop(self.session_key, []):
630 for cat, msg in session.pop(self.session_key, []):
631 messages.append(_Message(cat, msg))
631 messages.append(_Message(cat, msg))
632
632
633 # Pop the 'new' pyramid flash messages for each category as list
633 # Pop the 'new' pyramid flash messages for each category as list
634 # of strings.
634 # of strings.
635 for cat in self.categories:
635 for cat in self.categories:
636 for msg in session.pop_flash(queue=cat):
636 for msg in session.pop_flash(queue=cat):
637 messages.append(_Message(cat, msg))
637 messages.append(_Message(cat, msg))
638 # Map messages from the default queue to the 'notice' category.
638 # Map messages from the default queue to the 'notice' category.
639 for msg in session.pop_flash():
639 for msg in session.pop_flash():
640 messages.append(_Message('notice', msg))
640 messages.append(_Message('notice', msg))
641
641
642 session.save()
642 session.save()
643 return messages
643 return messages
644
644
645 def json_alerts(self, session=None, request=None):
645 def json_alerts(self, session=None, request=None):
646 payloads = []
646 payloads = []
647 messages = flash.pop_messages(session=session, request=request)
647 messages = flash.pop_messages(session=session, request=request)
648 if messages:
648 if messages:
649 for message in messages:
649 for message in messages:
650 subdata = {}
650 subdata = {}
651 if hasattr(message.message, 'rsplit'):
651 if hasattr(message.message, 'rsplit'):
652 flash_data = message.message.rsplit('|DELIM|', 1)
652 flash_data = message.message.rsplit('|DELIM|', 1)
653 org_message = flash_data[0]
653 org_message = flash_data[0]
654 if len(flash_data) > 1:
654 if len(flash_data) > 1:
655 subdata = json.loads(flash_data[1])
655 subdata = json.loads(flash_data[1])
656 else:
656 else:
657 org_message = message.message
657 org_message = message.message
658 payloads.append({
658 payloads.append({
659 'message': {
659 'message': {
660 'message': u'{}'.format(org_message),
660 'message': u'{}'.format(org_message),
661 'level': message.category,
661 'level': message.category,
662 'force': True,
662 'force': True,
663 'subdata': subdata
663 'subdata': subdata
664 }
664 }
665 })
665 })
666 return json.dumps(payloads)
666 return json.dumps(payloads)
667
667
668 def __call__(self, message, category=None, ignore_duplicate=True,
668 def __call__(self, message, category=None, ignore_duplicate=True,
669 session=None, request=None):
669 session=None, request=None):
670
670
671 if not session:
671 if not session:
672 if not request:
672 if not request:
673 request = get_current_request()
673 request = get_current_request()
674 session = request.session
674 session = request.session
675
675
676 session.flash(
676 session.flash(
677 message, queue=category, allow_duplicate=not ignore_duplicate)
677 message, queue=category, allow_duplicate=not ignore_duplicate)
678
678
679
679
680 flash = Flash()
680 flash = Flash()
681
681
682 #==============================================================================
682 #==============================================================================
683 # SCM FILTERS available via h.
683 # SCM FILTERS available via h.
684 #==============================================================================
684 #==============================================================================
685 from rhodecode.lib.vcs.utils import author_name, author_email
685 from rhodecode.lib.vcs.utils import author_name, author_email
686 from rhodecode.lib.utils2 import credentials_filter, age, age_from_seconds
686 from rhodecode.lib.utils2 import credentials_filter, age, age_from_seconds
687 from rhodecode.model.db import User, ChangesetStatus
687 from rhodecode.model.db import User, ChangesetStatus
688
688
689 capitalize = lambda x: x.capitalize()
689 capitalize = lambda x: x.capitalize()
690 email = author_email
690 email = author_email
691 short_id = lambda x: x[:12]
691 short_id = lambda x: x[:12]
692 hide_credentials = lambda x: ''.join(credentials_filter(x))
692 hide_credentials = lambda x: ''.join(credentials_filter(x))
693
693
694
694
695 import pytz
695 import pytz
696 import tzlocal
696 import tzlocal
697 local_timezone = tzlocal.get_localzone()
697 local_timezone = tzlocal.get_localzone()
698
698
699
699
700 def age_component(datetime_iso, value=None, time_is_local=False):
700 def age_component(datetime_iso, value=None, time_is_local=False):
701 title = value or format_date(datetime_iso)
701 title = value or format_date(datetime_iso)
702 tzinfo = '+00:00'
702 tzinfo = '+00:00'
703
703
704 # detect if we have a timezone info, otherwise, add it
704 # detect if we have a timezone info, otherwise, add it
705 if time_is_local and isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
705 if time_is_local and isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
706 force_timezone = os.environ.get('RC_TIMEZONE', '')
706 force_timezone = os.environ.get('RC_TIMEZONE', '')
707 if force_timezone:
707 if force_timezone:
708 force_timezone = pytz.timezone(force_timezone)
708 force_timezone = pytz.timezone(force_timezone)
709 timezone = force_timezone or local_timezone
709 timezone = force_timezone or local_timezone
710 offset = timezone.localize(datetime_iso).strftime('%z')
710 offset = timezone.localize(datetime_iso).strftime('%z')
711 tzinfo = '{}:{}'.format(offset[:-2], offset[-2:])
711 tzinfo = '{}:{}'.format(offset[:-2], offset[-2:])
712
712
713 return literal(
713 return literal(
714 '<time class="timeago tooltip" '
714 '<time class="timeago tooltip" '
715 'title="{1}{2}" datetime="{0}{2}">{1}</time>'.format(
715 'title="{1}{2}" datetime="{0}{2}">{1}</time>'.format(
716 datetime_iso, title, tzinfo))
716 datetime_iso, title, tzinfo))
717
717
718
718
719 def _shorten_commit_id(commit_id, commit_len=None):
719 def _shorten_commit_id(commit_id, commit_len=None):
720 if commit_len is None:
720 if commit_len is None:
721 request = get_current_request()
721 request = get_current_request()
722 commit_len = request.call_context.visual.show_sha_length
722 commit_len = request.call_context.visual.show_sha_length
723 return commit_id[:commit_len]
723 return commit_id[:commit_len]
724
724
725
725
726 def show_id(commit, show_idx=None, commit_len=None):
726 def show_id(commit, show_idx=None, commit_len=None):
727 """
727 """
728 Configurable function that shows ID
728 Configurable function that shows ID
729 by default it's r123:fffeeefffeee
729 by default it's r123:fffeeefffeee
730
730
731 :param commit: commit instance
731 :param commit: commit instance
732 """
732 """
733 if show_idx is None:
733 if show_idx is None:
734 request = get_current_request()
734 request = get_current_request()
735 show_idx = request.call_context.visual.show_revision_number
735 show_idx = request.call_context.visual.show_revision_number
736
736
737 raw_id = _shorten_commit_id(commit.raw_id, commit_len=commit_len)
737 raw_id = _shorten_commit_id(commit.raw_id, commit_len=commit_len)
738 if show_idx:
738 if show_idx:
739 return 'r%s:%s' % (commit.idx, raw_id)
739 return 'r%s:%s' % (commit.idx, raw_id)
740 else:
740 else:
741 return '%s' % (raw_id, )
741 return '%s' % (raw_id, )
742
742
743
743
744 def format_date(date):
744 def format_date(date):
745 """
745 """
746 use a standardized formatting for dates used in RhodeCode
746 use a standardized formatting for dates used in RhodeCode
747
747
748 :param date: date/datetime object
748 :param date: date/datetime object
749 :return: formatted date
749 :return: formatted date
750 """
750 """
751
751
752 if date:
752 if date:
753 _fmt = "%a, %d %b %Y %H:%M:%S"
753 _fmt = "%a, %d %b %Y %H:%M:%S"
754 return safe_unicode(date.strftime(_fmt))
754 return safe_unicode(date.strftime(_fmt))
755
755
756 return u""
756 return u""
757
757
758
758
759 class _RepoChecker(object):
759 class _RepoChecker(object):
760
760
761 def __init__(self, backend_alias):
761 def __init__(self, backend_alias):
762 self._backend_alias = backend_alias
762 self._backend_alias = backend_alias
763
763
764 def __call__(self, repository):
764 def __call__(self, repository):
765 if hasattr(repository, 'alias'):
765 if hasattr(repository, 'alias'):
766 _type = repository.alias
766 _type = repository.alias
767 elif hasattr(repository, 'repo_type'):
767 elif hasattr(repository, 'repo_type'):
768 _type = repository.repo_type
768 _type = repository.repo_type
769 else:
769 else:
770 _type = repository
770 _type = repository
771 return _type == self._backend_alias
771 return _type == self._backend_alias
772
772
773
773
774 is_git = _RepoChecker('git')
774 is_git = _RepoChecker('git')
775 is_hg = _RepoChecker('hg')
775 is_hg = _RepoChecker('hg')
776 is_svn = _RepoChecker('svn')
776 is_svn = _RepoChecker('svn')
777
777
778
778
779 def get_repo_type_by_name(repo_name):
779 def get_repo_type_by_name(repo_name):
780 repo = Repository.get_by_repo_name(repo_name)
780 repo = Repository.get_by_repo_name(repo_name)
781 if repo:
781 if repo:
782 return repo.repo_type
782 return repo.repo_type
783
783
784
784
785 def is_svn_without_proxy(repository):
785 def is_svn_without_proxy(repository):
786 if is_svn(repository):
786 if is_svn(repository):
787 from rhodecode.model.settings import VcsSettingsModel
787 from rhodecode.model.settings import VcsSettingsModel
788 conf = VcsSettingsModel().get_ui_settings_as_config_obj()
788 conf = VcsSettingsModel().get_ui_settings_as_config_obj()
789 return not str2bool(conf.get('vcs_svn_proxy', 'http_requests_enabled'))
789 return not str2bool(conf.get('vcs_svn_proxy', 'http_requests_enabled'))
790 return False
790 return False
791
791
792
792
793 def discover_user(author):
793 def discover_user(author):
794 """
794 """
795 Tries to discover RhodeCode User based on the autho string. Author string
795 Tries to discover RhodeCode User based on the autho string. Author string
796 is typically `FirstName LastName <email@address.com>`
796 is typically `FirstName LastName <email@address.com>`
797 """
797 """
798
798
799 # if author is already an instance use it for extraction
799 # if author is already an instance use it for extraction
800 if isinstance(author, User):
800 if isinstance(author, User):
801 return author
801 return author
802
802
803 # Valid email in the attribute passed, see if they're in the system
803 # Valid email in the attribute passed, see if they're in the system
804 _email = author_email(author)
804 _email = author_email(author)
805 if _email != '':
805 if _email != '':
806 user = User.get_by_email(_email, case_insensitive=True, cache=True)
806 user = User.get_by_email(_email, case_insensitive=True, cache=True)
807 if user is not None:
807 if user is not None:
808 return user
808 return user
809
809
810 # Maybe it's a username, we try to extract it and fetch by username ?
810 # Maybe it's a username, we try to extract it and fetch by username ?
811 _author = author_name(author)
811 _author = author_name(author)
812 user = User.get_by_username(_author, case_insensitive=True, cache=True)
812 user = User.get_by_username(_author, case_insensitive=True, cache=True)
813 if user is not None:
813 if user is not None:
814 return user
814 return user
815
815
816 return None
816 return None
817
817
818
818
819 def email_or_none(author):
819 def email_or_none(author):
820 # extract email from the commit string
820 # extract email from the commit string
821 _email = author_email(author)
821 _email = author_email(author)
822
822
823 # If we have an email, use it, otherwise
823 # If we have an email, use it, otherwise
824 # see if it contains a username we can get an email from
824 # see if it contains a username we can get an email from
825 if _email != '':
825 if _email != '':
826 return _email
826 return _email
827 else:
827 else:
828 user = User.get_by_username(
828 user = User.get_by_username(
829 author_name(author), case_insensitive=True, cache=True)
829 author_name(author), case_insensitive=True, cache=True)
830
830
831 if user is not None:
831 if user is not None:
832 return user.email
832 return user.email
833
833
834 # No valid email, not a valid user in the system, none!
834 # No valid email, not a valid user in the system, none!
835 return None
835 return None
836
836
837
837
838 def link_to_user(author, length=0, **kwargs):
838 def link_to_user(author, length=0, **kwargs):
839 user = discover_user(author)
839 user = discover_user(author)
840 # user can be None, but if we have it already it means we can re-use it
840 # user can be None, but if we have it already it means we can re-use it
841 # in the person() function, so we save 1 intensive-query
841 # in the person() function, so we save 1 intensive-query
842 if user:
842 if user:
843 author = user
843 author = user
844
844
845 display_person = person(author, 'username_or_name_or_email')
845 display_person = person(author, 'username_or_name_or_email')
846 if length:
846 if length:
847 display_person = shorter(display_person, length)
847 display_person = shorter(display_person, length)
848
848
849 if user:
849 if user:
850 return link_to(
850 return link_to(
851 escape(display_person),
851 escape(display_person),
852 route_path('user_profile', username=user.username),
852 route_path('user_profile', username=user.username),
853 **kwargs)
853 **kwargs)
854 else:
854 else:
855 return escape(display_person)
855 return escape(display_person)
856
856
857
857
858 def link_to_group(users_group_name, **kwargs):
858 def link_to_group(users_group_name, **kwargs):
859 return link_to(
859 return link_to(
860 escape(users_group_name),
860 escape(users_group_name),
861 route_path('user_group_profile', user_group_name=users_group_name),
861 route_path('user_group_profile', user_group_name=users_group_name),
862 **kwargs)
862 **kwargs)
863
863
864
864
865 def person(author, show_attr="username_and_name"):
865 def person(author, show_attr="username_and_name"):
866 user = discover_user(author)
866 user = discover_user(author)
867 if user:
867 if user:
868 return getattr(user, show_attr)
868 return getattr(user, show_attr)
869 else:
869 else:
870 _author = author_name(author)
870 _author = author_name(author)
871 _email = email(author)
871 _email = email(author)
872 return _author or _email
872 return _author or _email
873
873
874
874
875 def author_string(email):
875 def author_string(email):
876 if email:
876 if email:
877 user = User.get_by_email(email, case_insensitive=True, cache=True)
877 user = User.get_by_email(email, case_insensitive=True, cache=True)
878 if user:
878 if user:
879 if user.first_name or user.last_name:
879 if user.first_name or user.last_name:
880 return '%s %s &lt;%s&gt;' % (
880 return '%s %s &lt;%s&gt;' % (
881 user.first_name, user.last_name, email)
881 user.first_name, user.last_name, email)
882 else:
882 else:
883 return email
883 return email
884 else:
884 else:
885 return email
885 return email
886 else:
886 else:
887 return None
887 return None
888
888
889
889
890 def person_by_id(id_, show_attr="username_and_name"):
890 def person_by_id(id_, show_attr="username_and_name"):
891 # attr to return from fetched user
891 # attr to return from fetched user
892 person_getter = lambda usr: getattr(usr, show_attr)
892 person_getter = lambda usr: getattr(usr, show_attr)
893
893
894 #maybe it's an ID ?
894 #maybe it's an ID ?
895 if str(id_).isdigit() or isinstance(id_, int):
895 if str(id_).isdigit() or isinstance(id_, int):
896 id_ = int(id_)
896 id_ = int(id_)
897 user = User.get(id_)
897 user = User.get(id_)
898 if user is not None:
898 if user is not None:
899 return person_getter(user)
899 return person_getter(user)
900 return id_
900 return id_
901
901
902
902
903 def gravatar_with_user(request, author, show_disabled=False, tooltip=False):
903 def gravatar_with_user(request, author, show_disabled=False, tooltip=False):
904 _render = request.get_partial_renderer('rhodecode:templates/base/base.mako')
904 _render = request.get_partial_renderer('rhodecode:templates/base/base.mako')
905 return _render('gravatar_with_user', author, show_disabled=show_disabled, tooltip=tooltip)
905 return _render('gravatar_with_user', author, show_disabled=show_disabled, tooltip=tooltip)
906
906
907
907
908 tags_paterns = OrderedDict((
908 tags_paterns = OrderedDict((
909 ('lang', (re.compile(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+\.]*)\]'),
909 ('lang', (re.compile(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+\.]*)\]'),
910 '<div class="metatag" tag="lang">\\2</div>')),
910 '<div class="metatag" tag="lang">\\2</div>')),
911
911
912 ('see', (re.compile(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]'),
912 ('see', (re.compile(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]'),
913 '<div class="metatag" tag="see">see: \\1 </div>')),
913 '<div class="metatag" tag="see">see: \\1 </div>')),
914
914
915 ('url', (re.compile(r'\[url\ \=\&gt;\ \[([a-zA-Z0-9\ \.\-\_]+)\]\((http://|https://|/)(.*?)\)\]'),
915 ('url', (re.compile(r'\[url\ \=\&gt;\ \[([a-zA-Z0-9\ \.\-\_]+)\]\((http://|https://|/)(.*?)\)\]'),
916 '<div class="metatag" tag="url"> <a href="\\2\\3">\\1</a> </div>')),
916 '<div class="metatag" tag="url"> <a href="\\2\\3">\\1</a> </div>')),
917
917
918 ('license', (re.compile(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]'),
918 ('license', (re.compile(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]'),
919 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>')),
919 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>')),
920
920
921 ('ref', (re.compile(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]'),
921 ('ref', (re.compile(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]'),
922 '<div class="metatag" tag="ref \\1">\\1: <a href="/\\2">\\2</a></div>')),
922 '<div class="metatag" tag="ref \\1">\\1: <a href="/\\2">\\2</a></div>')),
923
923
924 ('state', (re.compile(r'\[(stable|featured|stale|dead|dev|deprecated)\]'),
924 ('state', (re.compile(r'\[(stable|featured|stale|dead|dev|deprecated)\]'),
925 '<div class="metatag" tag="state \\1">\\1</div>')),
925 '<div class="metatag" tag="state \\1">\\1</div>')),
926
926
927 # label in grey
927 # label in grey
928 ('label', (re.compile(r'\[([a-z]+)\]'),
928 ('label', (re.compile(r'\[([a-z]+)\]'),
929 '<div class="metatag" tag="label">\\1</div>')),
929 '<div class="metatag" tag="label">\\1</div>')),
930
930
931 # generic catch all in grey
931 # generic catch all in grey
932 ('generic', (re.compile(r'\[([a-zA-Z0-9\.\-\_]+)\]'),
932 ('generic', (re.compile(r'\[([a-zA-Z0-9\.\-\_]+)\]'),
933 '<div class="metatag" tag="generic">\\1</div>')),
933 '<div class="metatag" tag="generic">\\1</div>')),
934 ))
934 ))
935
935
936
936
937 def extract_metatags(value):
937 def extract_metatags(value):
938 """
938 """
939 Extract supported meta-tags from given text value
939 Extract supported meta-tags from given text value
940 """
940 """
941 tags = []
941 tags = []
942 if not value:
942 if not value:
943 return tags, ''
943 return tags, ''
944
944
945 for key, val in tags_paterns.items():
945 for key, val in tags_paterns.items():
946 pat, replace_html = val
946 pat, replace_html = val
947 tags.extend([(key, x.group()) for x in pat.finditer(value)])
947 tags.extend([(key, x.group()) for x in pat.finditer(value)])
948 value = pat.sub('', value)
948 value = pat.sub('', value)
949
949
950 return tags, value
950 return tags, value
951
951
952
952
953 def style_metatag(tag_type, value):
953 def style_metatag(tag_type, value):
954 """
954 """
955 converts tags from value into html equivalent
955 converts tags from value into html equivalent
956 """
956 """
957 if not value:
957 if not value:
958 return ''
958 return ''
959
959
960 html_value = value
960 html_value = value
961 tag_data = tags_paterns.get(tag_type)
961 tag_data = tags_paterns.get(tag_type)
962 if tag_data:
962 if tag_data:
963 pat, replace_html = tag_data
963 pat, replace_html = tag_data
964 # convert to plain `unicode` instead of a markup tag to be used in
964 # convert to plain `unicode` instead of a markup tag to be used in
965 # regex expressions. safe_unicode doesn't work here
965 # regex expressions. safe_unicode doesn't work here
966 html_value = pat.sub(replace_html, unicode(value))
966 html_value = pat.sub(replace_html, unicode(value))
967
967
968 return html_value
968 return html_value
969
969
970
970
971 def bool2icon(value, show_at_false=True):
971 def bool2icon(value, show_at_false=True):
972 """
972 """
973 Returns boolean value of a given value, represented as html element with
973 Returns boolean value of a given value, represented as html element with
974 classes that will represent icons
974 classes that will represent icons
975
975
976 :param value: given value to convert to html node
976 :param value: given value to convert to html node
977 """
977 """
978
978
979 if value: # does bool conversion
979 if value: # does bool conversion
980 return HTML.tag('i', class_="icon-true", title='True')
980 return HTML.tag('i', class_="icon-true", title='True')
981 else: # not true as bool
981 else: # not true as bool
982 if show_at_false:
982 if show_at_false:
983 return HTML.tag('i', class_="icon-false", title='False')
983 return HTML.tag('i', class_="icon-false", title='False')
984 return HTML.tag('i')
984 return HTML.tag('i')
985
985
986 #==============================================================================
986 #==============================================================================
987 # PERMS
987 # PERMS
988 #==============================================================================
988 #==============================================================================
989 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
989 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
990 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
990 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
991 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token, \
991 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token, \
992 csrf_token_key
992 csrf_token_key
993
993
994
994
995 #==============================================================================
995 #==============================================================================
996 # GRAVATAR URL
996 # GRAVATAR URL
997 #==============================================================================
997 #==============================================================================
998 class InitialsGravatar(object):
998 class InitialsGravatar(object):
999 def __init__(self, email_address, first_name, last_name, size=30,
999 def __init__(self, email_address, first_name, last_name, size=30,
1000 background=None, text_color='#fff'):
1000 background=None, text_color='#fff'):
1001 self.size = size
1001 self.size = size
1002 self.first_name = first_name
1002 self.first_name = first_name
1003 self.last_name = last_name
1003 self.last_name = last_name
1004 self.email_address = email_address
1004 self.email_address = email_address
1005 self.background = background or self.str2color(email_address)
1005 self.background = background or self.str2color(email_address)
1006 self.text_color = text_color
1006 self.text_color = text_color
1007
1007
1008 def get_color_bank(self):
1008 def get_color_bank(self):
1009 """
1009 """
1010 returns a predefined list of colors that gravatars can use.
1010 returns a predefined list of colors that gravatars can use.
1011 Those are randomized distinct colors that guarantee readability and
1011 Those are randomized distinct colors that guarantee readability and
1012 uniqueness.
1012 uniqueness.
1013
1013
1014 generated with: http://phrogz.net/css/distinct-colors.html
1014 generated with: http://phrogz.net/css/distinct-colors.html
1015 """
1015 """
1016 return [
1016 return [
1017 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
1017 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
1018 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
1018 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
1019 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
1019 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
1020 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
1020 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
1021 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
1021 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
1022 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
1022 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
1023 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
1023 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
1024 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
1024 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
1025 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
1025 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
1026 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
1026 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
1027 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
1027 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
1028 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
1028 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
1029 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
1029 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
1030 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
1030 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
1031 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
1031 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
1032 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
1032 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
1033 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
1033 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
1034 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
1034 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
1035 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
1035 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
1036 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
1036 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
1037 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
1037 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
1038 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
1038 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
1039 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
1039 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
1040 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
1040 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
1041 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
1041 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
1042 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
1042 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
1043 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
1043 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
1044 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
1044 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
1045 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
1045 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
1046 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
1046 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
1047 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
1047 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
1048 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
1048 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
1049 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
1049 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
1050 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
1050 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
1051 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
1051 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
1052 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
1052 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
1053 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
1053 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
1054 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
1054 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
1055 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
1055 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
1056 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
1056 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
1057 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
1057 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
1058 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
1058 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
1059 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
1059 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
1060 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
1060 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
1061 '#4f8c46', '#368dd9', '#5c0073'
1061 '#4f8c46', '#368dd9', '#5c0073'
1062 ]
1062 ]
1063
1063
1064 def rgb_to_hex_color(self, rgb_tuple):
1064 def rgb_to_hex_color(self, rgb_tuple):
1065 """
1065 """
1066 Converts an rgb_tuple passed to an hex color.
1066 Converts an rgb_tuple passed to an hex color.
1067
1067
1068 :param rgb_tuple: tuple with 3 ints represents rgb color space
1068 :param rgb_tuple: tuple with 3 ints represents rgb color space
1069 """
1069 """
1070 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
1070 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
1071
1071
1072 def email_to_int_list(self, email_str):
1072 def email_to_int_list(self, email_str):
1073 """
1073 """
1074 Get every byte of the hex digest value of email and turn it to integer.
1074 Get every byte of the hex digest value of email and turn it to integer.
1075 It's going to be always between 0-255
1075 It's going to be always between 0-255
1076 """
1076 """
1077 digest = md5_safe(email_str.lower())
1077 digest = md5_safe(email_str.lower())
1078 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
1078 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
1079
1079
1080 def pick_color_bank_index(self, email_str, color_bank):
1080 def pick_color_bank_index(self, email_str, color_bank):
1081 return self.email_to_int_list(email_str)[0] % len(color_bank)
1081 return self.email_to_int_list(email_str)[0] % len(color_bank)
1082
1082
1083 def str2color(self, email_str):
1083 def str2color(self, email_str):
1084 """
1084 """
1085 Tries to map in a stable algorithm an email to color
1085 Tries to map in a stable algorithm an email to color
1086
1086
1087 :param email_str:
1087 :param email_str:
1088 """
1088 """
1089 color_bank = self.get_color_bank()
1089 color_bank = self.get_color_bank()
1090 # pick position (module it's length so we always find it in the
1090 # pick position (module it's length so we always find it in the
1091 # bank even if it's smaller than 256 values
1091 # bank even if it's smaller than 256 values
1092 pos = self.pick_color_bank_index(email_str, color_bank)
1092 pos = self.pick_color_bank_index(email_str, color_bank)
1093 return color_bank[pos]
1093 return color_bank[pos]
1094
1094
1095 def normalize_email(self, email_address):
1095 def normalize_email(self, email_address):
1096 import unicodedata
1096 import unicodedata
1097 # default host used to fill in the fake/missing email
1097 # default host used to fill in the fake/missing email
1098 default_host = u'localhost'
1098 default_host = u'localhost'
1099
1099
1100 if not email_address:
1100 if not email_address:
1101 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1101 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1102
1102
1103 email_address = safe_unicode(email_address)
1103 email_address = safe_unicode(email_address)
1104
1104
1105 if u'@' not in email_address:
1105 if u'@' not in email_address:
1106 email_address = u'%s@%s' % (email_address, default_host)
1106 email_address = u'%s@%s' % (email_address, default_host)
1107
1107
1108 if email_address.endswith(u'@'):
1108 if email_address.endswith(u'@'):
1109 email_address = u'%s%s' % (email_address, default_host)
1109 email_address = u'%s%s' % (email_address, default_host)
1110
1110
1111 email_address = unicodedata.normalize('NFKD', email_address)\
1111 email_address = unicodedata.normalize('NFKD', email_address)\
1112 .encode('ascii', 'ignore')
1112 .encode('ascii', 'ignore')
1113 return email_address
1113 return email_address
1114
1114
1115 def get_initials(self):
1115 def get_initials(self):
1116 """
1116 """
1117 Returns 2 letter initials calculated based on the input.
1117 Returns 2 letter initials calculated based on the input.
1118 The algorithm picks first given email address, and takes first letter
1118 The algorithm picks first given email address, and takes first letter
1119 of part before @, and then the first letter of server name. In case
1119 of part before @, and then the first letter of server name. In case
1120 the part before @ is in a format of `somestring.somestring2` it replaces
1120 the part before @ is in a format of `somestring.somestring2` it replaces
1121 the server letter with first letter of somestring2
1121 the server letter with first letter of somestring2
1122
1122
1123 In case function was initialized with both first and lastname, this
1123 In case function was initialized with both first and lastname, this
1124 overrides the extraction from email by first letter of the first and
1124 overrides the extraction from email by first letter of the first and
1125 last name. We add special logic to that functionality, In case Full name
1125 last name. We add special logic to that functionality, In case Full name
1126 is compound, like Guido Von Rossum, we use last part of the last name
1126 is compound, like Guido Von Rossum, we use last part of the last name
1127 (Von Rossum) picking `R`.
1127 (Von Rossum) picking `R`.
1128
1128
1129 Function also normalizes the non-ascii characters to they ascii
1129 Function also normalizes the non-ascii characters to they ascii
1130 representation, eg Δ„ => A
1130 representation, eg Δ„ => A
1131 """
1131 """
1132 import unicodedata
1132 import unicodedata
1133 # replace non-ascii to ascii
1133 # replace non-ascii to ascii
1134 first_name = unicodedata.normalize(
1134 first_name = unicodedata.normalize(
1135 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1135 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1136 last_name = unicodedata.normalize(
1136 last_name = unicodedata.normalize(
1137 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1137 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1138
1138
1139 # do NFKD encoding, and also make sure email has proper format
1139 # do NFKD encoding, and also make sure email has proper format
1140 email_address = self.normalize_email(self.email_address)
1140 email_address = self.normalize_email(self.email_address)
1141
1141
1142 # first push the email initials
1142 # first push the email initials
1143 prefix, server = email_address.split('@', 1)
1143 prefix, server = email_address.split('@', 1)
1144
1144
1145 # check if prefix is maybe a 'first_name.last_name' syntax
1145 # check if prefix is maybe a 'first_name.last_name' syntax
1146 _dot_split = prefix.rsplit('.', 1)
1146 _dot_split = prefix.rsplit('.', 1)
1147 if len(_dot_split) == 2 and _dot_split[1]:
1147 if len(_dot_split) == 2 and _dot_split[1]:
1148 initials = [_dot_split[0][0], _dot_split[1][0]]
1148 initials = [_dot_split[0][0], _dot_split[1][0]]
1149 else:
1149 else:
1150 initials = [prefix[0], server[0]]
1150 initials = [prefix[0], server[0]]
1151
1151
1152 # then try to replace either first_name or last_name
1152 # then try to replace either first_name or last_name
1153 fn_letter = (first_name or " ")[0].strip()
1153 fn_letter = (first_name or " ")[0].strip()
1154 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1154 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1155
1155
1156 if fn_letter:
1156 if fn_letter:
1157 initials[0] = fn_letter
1157 initials[0] = fn_letter
1158
1158
1159 if ln_letter:
1159 if ln_letter:
1160 initials[1] = ln_letter
1160 initials[1] = ln_letter
1161
1161
1162 return ''.join(initials).upper()
1162 return ''.join(initials).upper()
1163
1163
1164 def get_img_data_by_type(self, font_family, img_type):
1164 def get_img_data_by_type(self, font_family, img_type):
1165 default_user = """
1165 default_user = """
1166 <svg xmlns="http://www.w3.org/2000/svg"
1166 <svg xmlns="http://www.w3.org/2000/svg"
1167 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1167 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1168 viewBox="-15 -10 439.165 429.164"
1168 viewBox="-15 -10 439.165 429.164"
1169
1169
1170 xml:space="preserve"
1170 xml:space="preserve"
1171 style="background:{background};" >
1171 style="background:{background};" >
1172
1172
1173 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1173 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1174 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1174 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1175 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1175 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1176 168.596,153.916,216.671,
1176 168.596,153.916,216.671,
1177 204.583,216.671z" fill="{text_color}"/>
1177 204.583,216.671z" fill="{text_color}"/>
1178 <path d="M407.164,374.717L360.88,
1178 <path d="M407.164,374.717L360.88,
1179 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1179 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1180 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1180 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1181 15.366-44.203,23.488-69.076,23.488c-24.877,
1181 15.366-44.203,23.488-69.076,23.488c-24.877,
1182 0-48.762-8.122-69.078-23.488
1182 0-48.762-8.122-69.078-23.488
1183 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1183 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1184 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1184 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1185 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1185 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1186 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1186 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1187 19.402-10.527 C409.699,390.129,
1187 19.402-10.527 C409.699,390.129,
1188 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1188 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1189 </svg>""".format(
1189 </svg>""".format(
1190 size=self.size,
1190 size=self.size,
1191 background='#979797', # @grey4
1191 background='#979797', # @grey4
1192 text_color=self.text_color,
1192 text_color=self.text_color,
1193 font_family=font_family)
1193 font_family=font_family)
1194
1194
1195 return {
1195 return {
1196 "default_user": default_user
1196 "default_user": default_user
1197 }[img_type]
1197 }[img_type]
1198
1198
1199 def get_img_data(self, svg_type=None):
1199 def get_img_data(self, svg_type=None):
1200 """
1200 """
1201 generates the svg metadata for image
1201 generates the svg metadata for image
1202 """
1202 """
1203 fonts = [
1203 fonts = [
1204 '-apple-system',
1204 '-apple-system',
1205 'BlinkMacSystemFont',
1205 'BlinkMacSystemFont',
1206 'Segoe UI',
1206 'Segoe UI',
1207 'Roboto',
1207 'Roboto',
1208 'Oxygen-Sans',
1208 'Oxygen-Sans',
1209 'Ubuntu',
1209 'Ubuntu',
1210 'Cantarell',
1210 'Cantarell',
1211 'Helvetica Neue',
1211 'Helvetica Neue',
1212 'sans-serif'
1212 'sans-serif'
1213 ]
1213 ]
1214 font_family = ','.join(fonts)
1214 font_family = ','.join(fonts)
1215 if svg_type:
1215 if svg_type:
1216 return self.get_img_data_by_type(font_family, svg_type)
1216 return self.get_img_data_by_type(font_family, svg_type)
1217
1217
1218 initials = self.get_initials()
1218 initials = self.get_initials()
1219 img_data = """
1219 img_data = """
1220 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1220 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1221 width="{size}" height="{size}"
1221 width="{size}" height="{size}"
1222 style="width: 100%; height: 100%; background-color: {background}"
1222 style="width: 100%; height: 100%; background-color: {background}"
1223 viewBox="0 0 {size} {size}">
1223 viewBox="0 0 {size} {size}">
1224 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1224 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1225 pointer-events="auto" fill="{text_color}"
1225 pointer-events="auto" fill="{text_color}"
1226 font-family="{font_family}"
1226 font-family="{font_family}"
1227 style="font-weight: 400; font-size: {f_size}px;">{text}
1227 style="font-weight: 400; font-size: {f_size}px;">{text}
1228 </text>
1228 </text>
1229 </svg>""".format(
1229 </svg>""".format(
1230 size=self.size,
1230 size=self.size,
1231 f_size=self.size/2.05, # scale the text inside the box nicely
1231 f_size=self.size/2.05, # scale the text inside the box nicely
1232 background=self.background,
1232 background=self.background,
1233 text_color=self.text_color,
1233 text_color=self.text_color,
1234 text=initials.upper(),
1234 text=initials.upper(),
1235 font_family=font_family)
1235 font_family=font_family)
1236
1236
1237 return img_data
1237 return img_data
1238
1238
1239 def generate_svg(self, svg_type=None):
1239 def generate_svg(self, svg_type=None):
1240 img_data = self.get_img_data(svg_type)
1240 img_data = self.get_img_data(svg_type)
1241 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1241 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1242
1242
1243
1243
1244 def initials_gravatar(email_address, first_name, last_name, size=30):
1244 def initials_gravatar(email_address, first_name, last_name, size=30):
1245 svg_type = None
1245 svg_type = None
1246 if email_address == User.DEFAULT_USER_EMAIL:
1246 if email_address == User.DEFAULT_USER_EMAIL:
1247 svg_type = 'default_user'
1247 svg_type = 'default_user'
1248 klass = InitialsGravatar(email_address, first_name, last_name, size)
1248 klass = InitialsGravatar(email_address, first_name, last_name, size)
1249 return klass.generate_svg(svg_type=svg_type)
1249 return klass.generate_svg(svg_type=svg_type)
1250
1250
1251
1251
1252 def gravatar_url(email_address, size=30, request=None):
1252 def gravatar_url(email_address, size=30, request=None):
1253 request = get_current_request()
1253 request = get_current_request()
1254 _use_gravatar = request.call_context.visual.use_gravatar
1254 _use_gravatar = request.call_context.visual.use_gravatar
1255 _gravatar_url = request.call_context.visual.gravatar_url
1255 _gravatar_url = request.call_context.visual.gravatar_url
1256
1256
1257 _gravatar_url = _gravatar_url or User.DEFAULT_GRAVATAR_URL
1257 _gravatar_url = _gravatar_url or User.DEFAULT_GRAVATAR_URL
1258
1258
1259 email_address = email_address or User.DEFAULT_USER_EMAIL
1259 email_address = email_address or User.DEFAULT_USER_EMAIL
1260 if isinstance(email_address, unicode):
1260 if isinstance(email_address, unicode):
1261 # hashlib crashes on unicode items
1261 # hashlib crashes on unicode items
1262 email_address = safe_str(email_address)
1262 email_address = safe_str(email_address)
1263
1263
1264 # empty email or default user
1264 # empty email or default user
1265 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1265 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1266 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1266 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1267
1267
1268 if _use_gravatar:
1268 if _use_gravatar:
1269 # TODO: Disuse pyramid thread locals. Think about another solution to
1269 # TODO: Disuse pyramid thread locals. Think about another solution to
1270 # get the host and schema here.
1270 # get the host and schema here.
1271 request = get_current_request()
1271 request = get_current_request()
1272 tmpl = safe_str(_gravatar_url)
1272 tmpl = safe_str(_gravatar_url)
1273 tmpl = tmpl.replace('{email}', email_address)\
1273 tmpl = tmpl.replace('{email}', email_address)\
1274 .replace('{md5email}', md5_safe(email_address.lower())) \
1274 .replace('{md5email}', md5_safe(email_address.lower())) \
1275 .replace('{netloc}', request.host)\
1275 .replace('{netloc}', request.host)\
1276 .replace('{scheme}', request.scheme)\
1276 .replace('{scheme}', request.scheme)\
1277 .replace('{size}', safe_str(size))
1277 .replace('{size}', safe_str(size))
1278 return tmpl
1278 return tmpl
1279 else:
1279 else:
1280 return initials_gravatar(email_address, '', '', size=size)
1280 return initials_gravatar(email_address, '', '', size=size)
1281
1281
1282
1282
1283 class Page(_Page):
1283 class Page(_Page):
1284 """
1284 """
1285 Custom pager to match rendering style with paginator
1285 Custom pager to match rendering style with paginator
1286 """
1286 """
1287
1287
1288 def _get_pos(self, cur_page, max_page, items):
1288 def _get_pos(self, cur_page, max_page, items):
1289 edge = (items / 2) + 1
1289 edge = (items / 2) + 1
1290 if (cur_page <= edge):
1290 if (cur_page <= edge):
1291 radius = max(items / 2, items - cur_page)
1291 radius = max(items / 2, items - cur_page)
1292 elif (max_page - cur_page) < edge:
1292 elif (max_page - cur_page) < edge:
1293 radius = (items - 1) - (max_page - cur_page)
1293 radius = (items - 1) - (max_page - cur_page)
1294 else:
1294 else:
1295 radius = items / 2
1295 radius = items / 2
1296
1296
1297 left = max(1, (cur_page - (radius)))
1297 left = max(1, (cur_page - (radius)))
1298 right = min(max_page, cur_page + (radius))
1298 right = min(max_page, cur_page + (radius))
1299 return left, cur_page, right
1299 return left, cur_page, right
1300
1300
1301 def _range(self, regexp_match):
1301 def _range(self, regexp_match):
1302 """
1302 """
1303 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1303 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1304
1304
1305 Arguments:
1305 Arguments:
1306
1306
1307 regexp_match
1307 regexp_match
1308 A "re" (regular expressions) match object containing the
1308 A "re" (regular expressions) match object containing the
1309 radius of linked pages around the current page in
1309 radius of linked pages around the current page in
1310 regexp_match.group(1) as a string
1310 regexp_match.group(1) as a string
1311
1311
1312 This function is supposed to be called as a callable in
1312 This function is supposed to be called as a callable in
1313 re.sub.
1313 re.sub.
1314
1314
1315 """
1315 """
1316 radius = int(regexp_match.group(1))
1316 radius = int(regexp_match.group(1))
1317
1317
1318 # Compute the first and last page number within the radius
1318 # Compute the first and last page number within the radius
1319 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1319 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1320 # -> leftmost_page = 5
1320 # -> leftmost_page = 5
1321 # -> rightmost_page = 9
1321 # -> rightmost_page = 9
1322 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1322 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1323 self.last_page,
1323 self.last_page,
1324 (radius * 2) + 1)
1324 (radius * 2) + 1)
1325 nav_items = []
1325 nav_items = []
1326
1326
1327 # Create a link to the first page (unless we are on the first page
1327 # Create a link to the first page (unless we are on the first page
1328 # or there would be no need to insert '..' spacers)
1328 # or there would be no need to insert '..' spacers)
1329 if self.page != self.first_page and self.first_page < leftmost_page:
1329 if self.page != self.first_page and self.first_page < leftmost_page:
1330 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1330 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1331
1331
1332 # Insert dots if there are pages between the first page
1332 # Insert dots if there are pages between the first page
1333 # and the currently displayed page range
1333 # and the currently displayed page range
1334 if leftmost_page - self.first_page > 1:
1334 if leftmost_page - self.first_page > 1:
1335 # Wrap in a SPAN tag if nolink_attr is set
1335 # Wrap in a SPAN tag if nolink_attr is set
1336 text = '..'
1336 text = '..'
1337 if self.dotdot_attr:
1337 if self.dotdot_attr:
1338 text = HTML.span(c=text, **self.dotdot_attr)
1338 text = HTML.span(c=text, **self.dotdot_attr)
1339 nav_items.append(text)
1339 nav_items.append(text)
1340
1340
1341 for thispage in xrange(leftmost_page, rightmost_page + 1):
1341 for thispage in xrange(leftmost_page, rightmost_page + 1):
1342 # Hilight the current page number and do not use a link
1342 # Hilight the current page number and do not use a link
1343 if thispage == self.page:
1343 if thispage == self.page:
1344 text = '%s' % (thispage,)
1344 text = '%s' % (thispage,)
1345 # Wrap in a SPAN tag if nolink_attr is set
1345 # Wrap in a SPAN tag if nolink_attr is set
1346 if self.curpage_attr:
1346 if self.curpage_attr:
1347 text = HTML.span(c=text, **self.curpage_attr)
1347 text = HTML.span(c=text, **self.curpage_attr)
1348 nav_items.append(text)
1348 nav_items.append(text)
1349 # Otherwise create just a link to that page
1349 # Otherwise create just a link to that page
1350 else:
1350 else:
1351 text = '%s' % (thispage,)
1351 text = '%s' % (thispage,)
1352 nav_items.append(self._pagerlink(thispage, text))
1352 nav_items.append(self._pagerlink(thispage, text))
1353
1353
1354 # Insert dots if there are pages between the displayed
1354 # Insert dots if there are pages between the displayed
1355 # page numbers and the end of the page range
1355 # page numbers and the end of the page range
1356 if self.last_page - rightmost_page > 1:
1356 if self.last_page - rightmost_page > 1:
1357 text = '..'
1357 text = '..'
1358 # Wrap in a SPAN tag if nolink_attr is set
1358 # Wrap in a SPAN tag if nolink_attr is set
1359 if self.dotdot_attr:
1359 if self.dotdot_attr:
1360 text = HTML.span(c=text, **self.dotdot_attr)
1360 text = HTML.span(c=text, **self.dotdot_attr)
1361 nav_items.append(text)
1361 nav_items.append(text)
1362
1362
1363 # Create a link to the very last page (unless we are on the last
1363 # Create a link to the very last page (unless we are on the last
1364 # page or there would be no need to insert '..' spacers)
1364 # page or there would be no need to insert '..' spacers)
1365 if self.page != self.last_page and rightmost_page < self.last_page:
1365 if self.page != self.last_page and rightmost_page < self.last_page:
1366 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1366 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1367
1367
1368 ## prerender links
1368 ## prerender links
1369 #_page_link = url.current()
1369 #_page_link = url.current()
1370 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1370 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1371 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1371 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1372 return self.separator.join(nav_items)
1372 return self.separator.join(nav_items)
1373
1373
1374 def pager(self, format='~2~', page_param='page', partial_param='partial',
1374 def pager(self, format='~2~', page_param='page', partial_param='partial',
1375 show_if_single_page=False, separator=' ', onclick=None,
1375 show_if_single_page=False, separator=' ', onclick=None,
1376 symbol_first='<<', symbol_last='>>',
1376 symbol_first='<<', symbol_last='>>',
1377 symbol_previous='<', symbol_next='>',
1377 symbol_previous='<', symbol_next='>',
1378 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1378 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1379 curpage_attr={'class': 'pager_curpage'},
1379 curpage_attr={'class': 'pager_curpage'},
1380 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1380 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1381
1381
1382 self.curpage_attr = curpage_attr
1382 self.curpage_attr = curpage_attr
1383 self.separator = separator
1383 self.separator = separator
1384 self.pager_kwargs = kwargs
1384 self.pager_kwargs = kwargs
1385 self.page_param = page_param
1385 self.page_param = page_param
1386 self.partial_param = partial_param
1386 self.partial_param = partial_param
1387 self.onclick = onclick
1387 self.onclick = onclick
1388 self.link_attr = link_attr
1388 self.link_attr = link_attr
1389 self.dotdot_attr = dotdot_attr
1389 self.dotdot_attr = dotdot_attr
1390
1390
1391 # Don't show navigator if there is no more than one page
1391 # Don't show navigator if there is no more than one page
1392 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1392 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1393 return ''
1393 return ''
1394
1394
1395 from string import Template
1395 from string import Template
1396 # Replace ~...~ in token format by range of pages
1396 # Replace ~...~ in token format by range of pages
1397 result = re.sub(r'~(\d+)~', self._range, format)
1397 result = re.sub(r'~(\d+)~', self._range, format)
1398
1398
1399 # Interpolate '%' variables
1399 # Interpolate '%' variables
1400 result = Template(result).safe_substitute({
1400 result = Template(result).safe_substitute({
1401 'first_page': self.first_page,
1401 'first_page': self.first_page,
1402 'last_page': self.last_page,
1402 'last_page': self.last_page,
1403 'page': self.page,
1403 'page': self.page,
1404 'page_count': self.page_count,
1404 'page_count': self.page_count,
1405 'items_per_page': self.items_per_page,
1405 'items_per_page': self.items_per_page,
1406 'first_item': self.first_item,
1406 'first_item': self.first_item,
1407 'last_item': self.last_item,
1407 'last_item': self.last_item,
1408 'item_count': self.item_count,
1408 'item_count': self.item_count,
1409 'link_first': self.page > self.first_page and \
1409 'link_first': self.page > self.first_page and \
1410 self._pagerlink(self.first_page, symbol_first) or '',
1410 self._pagerlink(self.first_page, symbol_first) or '',
1411 'link_last': self.page < self.last_page and \
1411 'link_last': self.page < self.last_page and \
1412 self._pagerlink(self.last_page, symbol_last) or '',
1412 self._pagerlink(self.last_page, symbol_last) or '',
1413 'link_previous': self.previous_page and \
1413 'link_previous': self.previous_page and \
1414 self._pagerlink(self.previous_page, symbol_previous) \
1414 self._pagerlink(self.previous_page, symbol_previous) \
1415 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1415 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1416 'link_next': self.next_page and \
1416 'link_next': self.next_page and \
1417 self._pagerlink(self.next_page, symbol_next) \
1417 self._pagerlink(self.next_page, symbol_next) \
1418 or HTML.span(symbol_next, class_="pg-next disabled")
1418 or HTML.span(symbol_next, class_="pg-next disabled")
1419 })
1419 })
1420
1420
1421 return literal(result)
1421 return literal(result)
1422
1422
1423
1423
1424 #==============================================================================
1424 #==============================================================================
1425 # REPO PAGER, PAGER FOR REPOSITORY
1425 # REPO PAGER, PAGER FOR REPOSITORY
1426 #==============================================================================
1426 #==============================================================================
1427 class RepoPage(Page):
1427 class RepoPage(Page):
1428
1428
1429 def __init__(self, collection, page=1, items_per_page=20,
1429 def __init__(self, collection, page=1, items_per_page=20,
1430 item_count=None, url=None, **kwargs):
1430 item_count=None, url=None, **kwargs):
1431
1431
1432 """Create a "RepoPage" instance. special pager for paging
1432 """Create a "RepoPage" instance. special pager for paging
1433 repository
1433 repository
1434 """
1434 """
1435 self._url_generator = url
1435 self._url_generator = url
1436
1436
1437 # Safe the kwargs class-wide so they can be used in the pager() method
1437 # Safe the kwargs class-wide so they can be used in the pager() method
1438 self.kwargs = kwargs
1438 self.kwargs = kwargs
1439
1439
1440 # Save a reference to the collection
1440 # Save a reference to the collection
1441 self.original_collection = collection
1441 self.original_collection = collection
1442
1442
1443 self.collection = collection
1443 self.collection = collection
1444
1444
1445 # The self.page is the number of the current page.
1445 # The self.page is the number of the current page.
1446 # The first page has the number 1!
1446 # The first page has the number 1!
1447 try:
1447 try:
1448 self.page = int(page) # make it int() if we get it as a string
1448 self.page = int(page) # make it int() if we get it as a string
1449 except (ValueError, TypeError):
1449 except (ValueError, TypeError):
1450 self.page = 1
1450 self.page = 1
1451
1451
1452 self.items_per_page = items_per_page
1452 self.items_per_page = items_per_page
1453
1453
1454 # Unless the user tells us how many items the collections has
1454 # Unless the user tells us how many items the collections has
1455 # we calculate that ourselves.
1455 # we calculate that ourselves.
1456 if item_count is not None:
1456 if item_count is not None:
1457 self.item_count = item_count
1457 self.item_count = item_count
1458 else:
1458 else:
1459 self.item_count = len(self.collection)
1459 self.item_count = len(self.collection)
1460
1460
1461 # Compute the number of the first and last available page
1461 # Compute the number of the first and last available page
1462 if self.item_count > 0:
1462 if self.item_count > 0:
1463 self.first_page = 1
1463 self.first_page = 1
1464 self.page_count = int(math.ceil(float(self.item_count) /
1464 self.page_count = int(math.ceil(float(self.item_count) /
1465 self.items_per_page))
1465 self.items_per_page))
1466 self.last_page = self.first_page + self.page_count - 1
1466 self.last_page = self.first_page + self.page_count - 1
1467
1467
1468 # Make sure that the requested page number is the range of
1468 # Make sure that the requested page number is the range of
1469 # valid pages
1469 # valid pages
1470 if self.page > self.last_page:
1470 if self.page > self.last_page:
1471 self.page = self.last_page
1471 self.page = self.last_page
1472 elif self.page < self.first_page:
1472 elif self.page < self.first_page:
1473 self.page = self.first_page
1473 self.page = self.first_page
1474
1474
1475 # Note: the number of items on this page can be less than
1475 # Note: the number of items on this page can be less than
1476 # items_per_page if the last page is not full
1476 # items_per_page if the last page is not full
1477 self.first_item = max(0, (self.item_count) - (self.page *
1477 self.first_item = max(0, (self.item_count) - (self.page *
1478 items_per_page))
1478 items_per_page))
1479 self.last_item = ((self.item_count - 1) - items_per_page *
1479 self.last_item = ((self.item_count - 1) - items_per_page *
1480 (self.page - 1))
1480 (self.page - 1))
1481
1481
1482 self.items = list(self.collection[self.first_item:self.last_item + 1])
1482 self.items = list(self.collection[self.first_item:self.last_item + 1])
1483
1483
1484 # Links to previous and next page
1484 # Links to previous and next page
1485 if self.page > self.first_page:
1485 if self.page > self.first_page:
1486 self.previous_page = self.page - 1
1486 self.previous_page = self.page - 1
1487 else:
1487 else:
1488 self.previous_page = None
1488 self.previous_page = None
1489
1489
1490 if self.page < self.last_page:
1490 if self.page < self.last_page:
1491 self.next_page = self.page + 1
1491 self.next_page = self.page + 1
1492 else:
1492 else:
1493 self.next_page = None
1493 self.next_page = None
1494
1494
1495 # No items available
1495 # No items available
1496 else:
1496 else:
1497 self.first_page = None
1497 self.first_page = None
1498 self.page_count = 0
1498 self.page_count = 0
1499 self.last_page = None
1499 self.last_page = None
1500 self.first_item = None
1500 self.first_item = None
1501 self.last_item = None
1501 self.last_item = None
1502 self.previous_page = None
1502 self.previous_page = None
1503 self.next_page = None
1503 self.next_page = None
1504 self.items = []
1504 self.items = []
1505
1505
1506 # This is a subclass of the 'list' type. Initialise the list now.
1506 # This is a subclass of the 'list' type. Initialise the list now.
1507 list.__init__(self, reversed(self.items))
1507 list.__init__(self, reversed(self.items))
1508
1508
1509
1509
1510 def breadcrumb_repo_link(repo):
1510 def breadcrumb_repo_link(repo):
1511 """
1511 """
1512 Makes a breadcrumbs path link to repo
1512 Makes a breadcrumbs path link to repo
1513
1513
1514 ex::
1514 ex::
1515 group >> subgroup >> repo
1515 group >> subgroup >> repo
1516
1516
1517 :param repo: a Repository instance
1517 :param repo: a Repository instance
1518 """
1518 """
1519
1519
1520 path = [
1520 path = [
1521 link_to(group.name, route_path('repo_group_home', repo_group_name=group.group_name),
1521 link_to(group.name, route_path('repo_group_home', repo_group_name=group.group_name),
1522 title='last change:{}'.format(format_date(group.last_commit_change)))
1522 title='last change:{}'.format(format_date(group.last_commit_change)))
1523 for group in repo.groups_with_parents
1523 for group in repo.groups_with_parents
1524 ] + [
1524 ] + [
1525 link_to(repo.just_name, route_path('repo_summary', repo_name=repo.repo_name),
1525 link_to(repo.just_name, route_path('repo_summary', repo_name=repo.repo_name),
1526 title='last change:{}'.format(format_date(repo.last_commit_change)))
1526 title='last change:{}'.format(format_date(repo.last_commit_change)))
1527 ]
1527 ]
1528
1528
1529 return literal(' &raquo; '.join(path))
1529 return literal(' &raquo; '.join(path))
1530
1530
1531
1531
1532 def breadcrumb_repo_group_link(repo_group):
1532 def breadcrumb_repo_group_link(repo_group):
1533 """
1533 """
1534 Makes a breadcrumbs path link to repo
1534 Makes a breadcrumbs path link to repo
1535
1535
1536 ex::
1536 ex::
1537 group >> subgroup
1537 group >> subgroup
1538
1538
1539 :param repo_group: a Repository Group instance
1539 :param repo_group: a Repository Group instance
1540 """
1540 """
1541
1541
1542 path = [
1542 path = [
1543 link_to(group.name,
1543 link_to(group.name,
1544 route_path('repo_group_home', repo_group_name=group.group_name),
1544 route_path('repo_group_home', repo_group_name=group.group_name),
1545 title='last change:{}'.format(format_date(group.last_commit_change)))
1545 title='last change:{}'.format(format_date(group.last_commit_change)))
1546 for group in repo_group.parents
1546 for group in repo_group.parents
1547 ] + [
1547 ] + [
1548 link_to(repo_group.name,
1548 link_to(repo_group.name,
1549 route_path('repo_group_home', repo_group_name=repo_group.group_name),
1549 route_path('repo_group_home', repo_group_name=repo_group.group_name),
1550 title='last change:{}'.format(format_date(repo_group.last_commit_change)))
1550 title='last change:{}'.format(format_date(repo_group.last_commit_change)))
1551 ]
1551 ]
1552
1552
1553 return literal(' &raquo; '.join(path))
1553 return literal(' &raquo; '.join(path))
1554
1554
1555
1555
1556 def format_byte_size_binary(file_size):
1556 def format_byte_size_binary(file_size):
1557 """
1557 """
1558 Formats file/folder sizes to standard.
1558 Formats file/folder sizes to standard.
1559 """
1559 """
1560 if file_size is None:
1560 if file_size is None:
1561 file_size = 0
1561 file_size = 0
1562
1562
1563 formatted_size = format_byte_size(file_size, binary=True)
1563 formatted_size = format_byte_size(file_size, binary=True)
1564 return formatted_size
1564 return formatted_size
1565
1565
1566
1566
1567 def urlify_text(text_, safe=True):
1567 def urlify_text(text_, safe=True):
1568 """
1568 """
1569 Extrac urls from text and make html links out of them
1569 Extrac urls from text and make html links out of them
1570
1570
1571 :param text_:
1571 :param text_:
1572 """
1572 """
1573
1573
1574 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1574 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1575 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1575 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1576
1576
1577 def url_func(match_obj):
1577 def url_func(match_obj):
1578 url_full = match_obj.groups()[0]
1578 url_full = match_obj.groups()[0]
1579 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1579 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1580 _new_text = url_pat.sub(url_func, text_)
1580 _new_text = url_pat.sub(url_func, text_)
1581 if safe:
1581 if safe:
1582 return literal(_new_text)
1582 return literal(_new_text)
1583 return _new_text
1583 return _new_text
1584
1584
1585
1585
1586 def urlify_commits(text_, repo_name):
1586 def urlify_commits(text_, repo_name):
1587 """
1587 """
1588 Extract commit ids from text and make link from them
1588 Extract commit ids from text and make link from them
1589
1589
1590 :param text_:
1590 :param text_:
1591 :param repo_name: repo name to build the URL with
1591 :param repo_name: repo name to build the URL with
1592 """
1592 """
1593
1593
1594 url_pat = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1594 url_pat = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1595
1595
1596 def url_func(match_obj):
1596 def url_func(match_obj):
1597 commit_id = match_obj.groups()[1]
1597 commit_id = match_obj.groups()[1]
1598 pref = match_obj.groups()[0]
1598 pref = match_obj.groups()[0]
1599 suf = match_obj.groups()[2]
1599 suf = match_obj.groups()[2]
1600
1600
1601 tmpl = (
1601 tmpl = (
1602 '%(pref)s<a class="tooltip-hovercard %(cls)s" href="%(url)s" data-hovercard-alt="%(hovercard_alt)s" data-hovercard-url="%(hovercard_url)s">'
1602 '%(pref)s<a class="tooltip-hovercard %(cls)s" href="%(url)s" data-hovercard-alt="%(hovercard_alt)s" data-hovercard-url="%(hovercard_url)s">'
1603 '%(commit_id)s</a>%(suf)s'
1603 '%(commit_id)s</a>%(suf)s'
1604 )
1604 )
1605 return tmpl % {
1605 return tmpl % {
1606 'pref': pref,
1606 'pref': pref,
1607 'cls': 'revision-link',
1607 'cls': 'revision-link',
1608 'url': route_url(
1608 'url': route_url(
1609 'repo_commit', repo_name=repo_name, commit_id=commit_id),
1609 'repo_commit', repo_name=repo_name, commit_id=commit_id),
1610 'commit_id': commit_id,
1610 'commit_id': commit_id,
1611 'suf': suf,
1611 'suf': suf,
1612 'hovercard_alt': 'Commit: {}'.format(commit_id),
1612 'hovercard_alt': 'Commit: {}'.format(commit_id),
1613 'hovercard_url': route_url(
1613 'hovercard_url': route_url(
1614 'hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)
1614 'hovercard_repo_commit', repo_name=repo_name, commit_id=commit_id)
1615 }
1615 }
1616
1616
1617 new_text = url_pat.sub(url_func, text_)
1617 new_text = url_pat.sub(url_func, text_)
1618
1618
1619 return new_text
1619 return new_text
1620
1620
1621
1621
1622 def _process_url_func(match_obj, repo_name, uid, entry,
1622 def _process_url_func(match_obj, repo_name, uid, entry,
1623 return_raw_data=False, link_format='html'):
1623 return_raw_data=False, link_format='html'):
1624 pref = ''
1624 pref = ''
1625 if match_obj.group().startswith(' '):
1625 if match_obj.group().startswith(' '):
1626 pref = ' '
1626 pref = ' '
1627
1627
1628 issue_id = ''.join(match_obj.groups())
1628 issue_id = ''.join(match_obj.groups())
1629
1629
1630 if link_format == 'html':
1630 if link_format == 'html':
1631 tmpl = (
1631 tmpl = (
1632 '%(pref)s<a class="tooltip %(cls)s" href="%(url)s" title="%(title)s">'
1632 '%(pref)s<a class="tooltip %(cls)s" href="%(url)s" title="%(title)s">'
1633 '%(issue-prefix)s%(id-repr)s'
1633 '%(issue-prefix)s%(id-repr)s'
1634 '</a>')
1634 '</a>')
1635 elif link_format == 'html+hovercard':
1635 elif link_format == 'html+hovercard':
1636 tmpl = (
1636 tmpl = (
1637 '%(pref)s<a class="tooltip-hovercard %(cls)s" href="%(url)s" data-hovercard-url="%(hovercard_url)s">'
1637 '%(pref)s<a class="tooltip-hovercard %(cls)s" href="%(url)s" data-hovercard-url="%(hovercard_url)s">'
1638 '%(issue-prefix)s%(id-repr)s'
1638 '%(issue-prefix)s%(id-repr)s'
1639 '</a>')
1639 '</a>')
1640 elif link_format in ['rst', 'rst+hovercard']:
1640 elif link_format in ['rst', 'rst+hovercard']:
1641 tmpl = '`%(issue-prefix)s%(id-repr)s <%(url)s>`_'
1641 tmpl = '`%(issue-prefix)s%(id-repr)s <%(url)s>`_'
1642 elif link_format in ['markdown', 'markdown+hovercard']:
1642 elif link_format in ['markdown', 'markdown+hovercard']:
1643 tmpl = '[%(pref)s%(issue-prefix)s%(id-repr)s](%(url)s)'
1643 tmpl = '[%(pref)s%(issue-prefix)s%(id-repr)s](%(url)s)'
1644 else:
1644 else:
1645 raise ValueError('Bad link_format:{}'.format(link_format))
1645 raise ValueError('Bad link_format:{}'.format(link_format))
1646
1646
1647 (repo_name_cleaned,
1647 (repo_name_cleaned,
1648 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(repo_name)
1648 parent_group_name) = RepoGroupModel()._get_group_name_and_parent(repo_name)
1649
1649
1650 # variables replacement
1650 # variables replacement
1651 named_vars = {
1651 named_vars = {
1652 'id': issue_id,
1652 'id': issue_id,
1653 'repo': repo_name,
1653 'repo': repo_name,
1654 'repo_name': repo_name_cleaned,
1654 'repo_name': repo_name_cleaned,
1655 'group_name': parent_group_name,
1655 'group_name': parent_group_name,
1656 # set dummy keys so we always have them
1656 # set dummy keys so we always have them
1657 'hostname': '',
1657 'hostname': '',
1658 'netloc': '',
1658 'netloc': '',
1659 'scheme': ''
1659 'scheme': ''
1660 }
1660 }
1661
1661
1662 request = get_current_request()
1662 request = get_current_request()
1663 if request:
1663 if request:
1664 # exposes, hostname, netloc, scheme
1664 # exposes, hostname, netloc, scheme
1665 host_data = get_host_info(request)
1665 host_data = get_host_info(request)
1666 named_vars.update(host_data)
1666 named_vars.update(host_data)
1667
1667
1668 # named regex variables
1668 # named regex variables
1669 named_vars.update(match_obj.groupdict())
1669 named_vars.update(match_obj.groupdict())
1670 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1670 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1671 desc = string.Template(entry['desc']).safe_substitute(**named_vars)
1671 desc = string.Template(entry['desc']).safe_substitute(**named_vars)
1672 hovercard_url = string.Template(entry.get('hovercard_url', '')).safe_substitute(**named_vars)
1672 hovercard_url = string.Template(entry.get('hovercard_url', '')).safe_substitute(**named_vars)
1673
1673
1674 def quote_cleaner(input_str):
1674 def quote_cleaner(input_str):
1675 """Remove quotes as it's HTML"""
1675 """Remove quotes as it's HTML"""
1676 return input_str.replace('"', '')
1676 return input_str.replace('"', '')
1677
1677
1678 data = {
1678 data = {
1679 'pref': pref,
1679 'pref': pref,
1680 'cls': quote_cleaner('issue-tracker-link'),
1680 'cls': quote_cleaner('issue-tracker-link'),
1681 'url': quote_cleaner(_url),
1681 'url': quote_cleaner(_url),
1682 'id-repr': issue_id,
1682 'id-repr': issue_id,
1683 'issue-prefix': entry['pref'],
1683 'issue-prefix': entry['pref'],
1684 'serv': entry['url'],
1684 'serv': entry['url'],
1685 'title': desc,
1685 'title': desc,
1686 'hovercard_url': hovercard_url
1686 'hovercard_url': hovercard_url
1687 }
1687 }
1688
1688
1689 if return_raw_data:
1689 if return_raw_data:
1690 return {
1690 return {
1691 'id': issue_id,
1691 'id': issue_id,
1692 'url': _url
1692 'url': _url
1693 }
1693 }
1694 return tmpl % data
1694 return tmpl % data
1695
1695
1696
1696
1697 def get_active_pattern_entries(repo_name):
1697 def get_active_pattern_entries(repo_name):
1698 repo = None
1698 repo = None
1699 if repo_name:
1699 if repo_name:
1700 # Retrieving repo_name to avoid invalid repo_name to explode on
1700 # Retrieving repo_name to avoid invalid repo_name to explode on
1701 # IssueTrackerSettingsModel but still passing invalid name further down
1701 # IssueTrackerSettingsModel but still passing invalid name further down
1702 repo = Repository.get_by_repo_name(repo_name, cache=True)
1702 repo = Repository.get_by_repo_name(repo_name, cache=True)
1703
1703
1704 settings_model = IssueTrackerSettingsModel(repo=repo)
1704 settings_model = IssueTrackerSettingsModel(repo=repo)
1705 active_entries = settings_model.get_settings(cache=True)
1705 active_entries = settings_model.get_settings(cache=True)
1706 return active_entries
1706 return active_entries
1707
1707
1708
1708
1709 def process_patterns(text_string, repo_name, link_format='html', active_entries=None):
1709 def process_patterns(text_string, repo_name, link_format='html', active_entries=None):
1710
1710
1711 allowed_formats = ['html', 'rst', 'markdown',
1711 allowed_formats = ['html', 'rst', 'markdown',
1712 'html+hovercard', 'rst+hovercard', 'markdown+hovercard']
1712 'html+hovercard', 'rst+hovercard', 'markdown+hovercard']
1713 if link_format not in allowed_formats:
1713 if link_format not in allowed_formats:
1714 raise ValueError('Link format can be only one of:{} got {}'.format(
1714 raise ValueError('Link format can be only one of:{} got {}'.format(
1715 allowed_formats, link_format))
1715 allowed_formats, link_format))
1716
1716
1717 active_entries = active_entries or get_active_pattern_entries(repo_name)
1717 active_entries = active_entries or get_active_pattern_entries(repo_name)
1718 issues_data = []
1718 issues_data = []
1719 new_text = text_string
1719 new_text = text_string
1720
1720
1721 log.debug('Got %s entries to process', len(active_entries))
1721 log.debug('Got %s entries to process', len(active_entries))
1722 for uid, entry in active_entries.items():
1722 for uid, entry in active_entries.items():
1723 log.debug('found issue tracker entry with uid %s', uid)
1723 log.debug('found issue tracker entry with uid %s', uid)
1724
1724
1725 if not (entry['pat'] and entry['url']):
1725 if not (entry['pat'] and entry['url']):
1726 log.debug('skipping due to missing data')
1726 log.debug('skipping due to missing data')
1727 continue
1727 continue
1728
1728
1729 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s',
1729 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s',
1730 uid, entry['pat'], entry['url'], entry['pref'])
1730 uid, entry['pat'], entry['url'], entry['pref'])
1731
1731
1732 try:
1732 try:
1733 pattern = re.compile(r'%s' % entry['pat'])
1733 pattern = re.compile(r'%s' % entry['pat'])
1734 except re.error:
1734 except re.error:
1735 log.exception('issue tracker pattern: `%s` failed to compile', entry['pat'])
1735 log.exception('issue tracker pattern: `%s` failed to compile', entry['pat'])
1736 continue
1736 continue
1737
1737
1738 data_func = partial(
1738 data_func = partial(
1739 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1739 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1740 return_raw_data=True)
1740 return_raw_data=True)
1741
1741
1742 for match_obj in pattern.finditer(text_string):
1742 for match_obj in pattern.finditer(text_string):
1743 issues_data.append(data_func(match_obj))
1743 issues_data.append(data_func(match_obj))
1744
1744
1745 url_func = partial(
1745 url_func = partial(
1746 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1746 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1747 link_format=link_format)
1747 link_format=link_format)
1748
1748
1749 new_text = pattern.sub(url_func, new_text)
1749 new_text = pattern.sub(url_func, new_text)
1750 log.debug('processed prefix:uid `%s`', uid)
1750 log.debug('processed prefix:uid `%s`', uid)
1751
1751
1752 # finally use global replace, eg !123 -> pr-link, those will not catch
1752 # finally use global replace, eg !123 -> pr-link, those will not catch
1753 # if already similar pattern exists
1753 # if already similar pattern exists
1754 server_url = '${scheme}://${netloc}'
1754 server_url = '${scheme}://${netloc}'
1755 pr_entry = {
1755 pr_entry = {
1756 'pref': '!',
1756 'pref': '!',
1757 'url': server_url + '/_admin/pull-requests/${id}',
1757 'url': server_url + '/_admin/pull-requests/${id}',
1758 'desc': 'Pull Request !${id}',
1758 'desc': 'Pull Request !${id}',
1759 'hovercard_url': server_url + '/_hovercard/pull_request/${id}'
1759 'hovercard_url': server_url + '/_hovercard/pull_request/${id}'
1760 }
1760 }
1761 pr_url_func = partial(
1761 pr_url_func = partial(
1762 _process_url_func, repo_name=repo_name, entry=pr_entry, uid=None,
1762 _process_url_func, repo_name=repo_name, entry=pr_entry, uid=None,
1763 link_format=link_format+'+hovercard')
1763 link_format=link_format+'+hovercard')
1764 new_text = re.compile(r'(?:(?:^!)|(?: !))(\d+)').sub(pr_url_func, new_text)
1764 new_text = re.compile(r'(?:(?:^!)|(?: !))(\d+)').sub(pr_url_func, new_text)
1765 log.debug('processed !pr pattern')
1765 log.debug('processed !pr pattern')
1766
1766
1767 return new_text, issues_data
1767 return new_text, issues_data
1768
1768
1769
1769
1770 def urlify_commit_message(commit_text, repository=None, active_pattern_entries=None):
1770 def urlify_commit_message(commit_text, repository=None, active_pattern_entries=None):
1771 """
1771 """
1772 Parses given text message and makes proper links.
1772 Parses given text message and makes proper links.
1773 issues are linked to given issue-server, and rest is a commit link
1773 issues are linked to given issue-server, and rest is a commit link
1774 """
1774 """
1775 def escaper(_text):
1775 def escaper(_text):
1776 return _text.replace('<', '&lt;').replace('>', '&gt;')
1776 return _text.replace('<', '&lt;').replace('>', '&gt;')
1777
1777
1778 new_text = escaper(commit_text)
1778 new_text = escaper(commit_text)
1779
1779
1780 # extract http/https links and make them real urls
1780 # extract http/https links and make them real urls
1781 new_text = urlify_text(new_text, safe=False)
1781 new_text = urlify_text(new_text, safe=False)
1782
1782
1783 # urlify commits - extract commit ids and make link out of them, if we have
1783 # urlify commits - extract commit ids and make link out of them, if we have
1784 # the scope of repository present.
1784 # the scope of repository present.
1785 if repository:
1785 if repository:
1786 new_text = urlify_commits(new_text, repository)
1786 new_text = urlify_commits(new_text, repository)
1787
1787
1788 # process issue tracker patterns
1788 # process issue tracker patterns
1789 new_text, issues = process_patterns(new_text, repository or '',
1789 new_text, issues = process_patterns(new_text, repository or '',
1790 active_entries=active_pattern_entries)
1790 active_entries=active_pattern_entries)
1791
1791
1792 return literal(new_text)
1792 return literal(new_text)
1793
1793
1794
1794
1795 def render_binary(repo_name, file_obj):
1795 def render_binary(repo_name, file_obj):
1796 """
1796 """
1797 Choose how to render a binary file
1797 Choose how to render a binary file
1798 """
1798 """
1799
1799
1800 filename = file_obj.name
1800 filename = file_obj.name
1801
1801
1802 # images
1802 # images
1803 for ext in ['*.png', '*.jpg', '*.ico', '*.gif']:
1803 for ext in ['*.png', '*.jpg', '*.ico', '*.gif']:
1804 if fnmatch.fnmatch(filename, pat=ext):
1804 if fnmatch.fnmatch(filename, pat=ext):
1805 alt = escape(filename)
1805 alt = escape(filename)
1806 src = route_path(
1806 src = route_path(
1807 'repo_file_raw', repo_name=repo_name,
1807 'repo_file_raw', repo_name=repo_name,
1808 commit_id=file_obj.commit.raw_id,
1808 commit_id=file_obj.commit.raw_id,
1809 f_path=file_obj.path)
1809 f_path=file_obj.path)
1810 return literal(
1810 return literal(
1811 '<img class="rendered-binary" alt="{}" src="{}">'.format(alt, src))
1811 '<img class="rendered-binary" alt="{}" src="{}">'.format(alt, src))
1812
1812
1813
1813
1814 def renderer_from_filename(filename, exclude=None):
1814 def renderer_from_filename(filename, exclude=None):
1815 """
1815 """
1816 choose a renderer based on filename, this works only for text based files
1816 choose a renderer based on filename, this works only for text based files
1817 """
1817 """
1818
1818
1819 # ipython
1819 # ipython
1820 for ext in ['*.ipynb']:
1820 for ext in ['*.ipynb']:
1821 if fnmatch.fnmatch(filename, pat=ext):
1821 if fnmatch.fnmatch(filename, pat=ext):
1822 return 'jupyter'
1822 return 'jupyter'
1823
1823
1824 is_markup = MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1824 is_markup = MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1825 if is_markup:
1825 if is_markup:
1826 return is_markup
1826 return is_markup
1827 return None
1827 return None
1828
1828
1829
1829
1830 def render(source, renderer='rst', mentions=False, relative_urls=None,
1830 def render(source, renderer='rst', mentions=False, relative_urls=None,
1831 repo_name=None):
1831 repo_name=None):
1832
1832
1833 def maybe_convert_relative_links(html_source):
1833 def maybe_convert_relative_links(html_source):
1834 if relative_urls:
1834 if relative_urls:
1835 return relative_links(html_source, relative_urls)
1835 return relative_links(html_source, relative_urls)
1836 return html_source
1836 return html_source
1837
1837
1838 if renderer == 'plain':
1838 if renderer == 'plain':
1839 return literal(
1839 return literal(
1840 MarkupRenderer.plain(source, leading_newline=False))
1840 MarkupRenderer.plain(source, leading_newline=False))
1841
1841
1842 elif renderer == 'rst':
1842 elif renderer == 'rst':
1843 if repo_name:
1843 if repo_name:
1844 # process patterns on comments if we pass in repo name
1844 # process patterns on comments if we pass in repo name
1845 source, issues = process_patterns(
1845 source, issues = process_patterns(
1846 source, repo_name, link_format='rst')
1846 source, repo_name, link_format='rst')
1847
1847
1848 return literal(
1848 return literal(
1849 '<div class="rst-block">%s</div>' %
1849 '<div class="rst-block">%s</div>' %
1850 maybe_convert_relative_links(
1850 maybe_convert_relative_links(
1851 MarkupRenderer.rst(source, mentions=mentions)))
1851 MarkupRenderer.rst(source, mentions=mentions)))
1852
1852
1853 elif renderer == 'markdown':
1853 elif renderer == 'markdown':
1854 if repo_name:
1854 if repo_name:
1855 # process patterns on comments if we pass in repo name
1855 # process patterns on comments if we pass in repo name
1856 source, issues = process_patterns(
1856 source, issues = process_patterns(
1857 source, repo_name, link_format='markdown')
1857 source, repo_name, link_format='markdown')
1858
1858
1859 return literal(
1859 return literal(
1860 '<div class="markdown-block">%s</div>' %
1860 '<div class="markdown-block">%s</div>' %
1861 maybe_convert_relative_links(
1861 maybe_convert_relative_links(
1862 MarkupRenderer.markdown(source, flavored=True,
1862 MarkupRenderer.markdown(source, flavored=True,
1863 mentions=mentions)))
1863 mentions=mentions)))
1864
1864
1865 elif renderer == 'jupyter':
1865 elif renderer == 'jupyter':
1866 return literal(
1866 return literal(
1867 '<div class="ipynb">%s</div>' %
1867 '<div class="ipynb">%s</div>' %
1868 maybe_convert_relative_links(
1868 maybe_convert_relative_links(
1869 MarkupRenderer.jupyter(source)))
1869 MarkupRenderer.jupyter(source)))
1870
1870
1871 # None means just show the file-source
1871 # None means just show the file-source
1872 return None
1872 return None
1873
1873
1874
1874
1875 def commit_status(repo, commit_id):
1875 def commit_status(repo, commit_id):
1876 return ChangesetStatusModel().get_status(repo, commit_id)
1876 return ChangesetStatusModel().get_status(repo, commit_id)
1877
1877
1878
1878
1879 def commit_status_lbl(commit_status):
1879 def commit_status_lbl(commit_status):
1880 return dict(ChangesetStatus.STATUSES).get(commit_status)
1880 return dict(ChangesetStatus.STATUSES).get(commit_status)
1881
1881
1882
1882
1883 def commit_time(repo_name, commit_id):
1883 def commit_time(repo_name, commit_id):
1884 repo = Repository.get_by_repo_name(repo_name)
1884 repo = Repository.get_by_repo_name(repo_name)
1885 commit = repo.get_commit(commit_id=commit_id)
1885 commit = repo.get_commit(commit_id=commit_id)
1886 return commit.date
1886 return commit.date
1887
1887
1888
1888
1889 def get_permission_name(key):
1889 def get_permission_name(key):
1890 return dict(Permission.PERMS).get(key)
1890 return dict(Permission.PERMS).get(key)
1891
1891
1892
1892
1893 def journal_filter_help(request):
1893 def journal_filter_help(request):
1894 _ = request.translate
1894 _ = request.translate
1895 from rhodecode.lib.audit_logger import ACTIONS
1895 from rhodecode.lib.audit_logger import ACTIONS
1896 actions = '\n'.join(textwrap.wrap(', '.join(sorted(ACTIONS.keys())), 80))
1896 actions = '\n'.join(textwrap.wrap(', '.join(sorted(ACTIONS.keys())), 80))
1897
1897
1898 return _(
1898 return _(
1899 'Example filter terms:\n' +
1899 'Example filter terms:\n' +
1900 ' repository:vcs\n' +
1900 ' repository:vcs\n' +
1901 ' username:marcin\n' +
1901 ' username:marcin\n' +
1902 ' username:(NOT marcin)\n' +
1902 ' username:(NOT marcin)\n' +
1903 ' action:*push*\n' +
1903 ' action:*push*\n' +
1904 ' ip:127.0.0.1\n' +
1904 ' ip:127.0.0.1\n' +
1905 ' date:20120101\n' +
1905 ' date:20120101\n' +
1906 ' date:[20120101100000 TO 20120102]\n' +
1906 ' date:[20120101100000 TO 20120102]\n' +
1907 '\n' +
1907 '\n' +
1908 'Actions: {actions}\n' +
1908 'Actions: {actions}\n' +
1909 '\n' +
1909 '\n' +
1910 'Generate wildcards using \'*\' character:\n' +
1910 'Generate wildcards using \'*\' character:\n' +
1911 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1911 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1912 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1912 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1913 '\n' +
1913 '\n' +
1914 'Optional AND / OR operators in queries\n' +
1914 'Optional AND / OR operators in queries\n' +
1915 ' "repository:vcs OR repository:test"\n' +
1915 ' "repository:vcs OR repository:test"\n' +
1916 ' "username:test AND repository:test*"\n'
1916 ' "username:test AND repository:test*"\n'
1917 ).format(actions=actions)
1917 ).format(actions=actions)
1918
1918
1919
1919
1920 def not_mapped_error(repo_name):
1920 def not_mapped_error(repo_name):
1921 from rhodecode.translation import _
1921 from rhodecode.translation import _
1922 flash(_('%s repository is not mapped to db perhaps'
1922 flash(_('%s repository is not mapped to db perhaps'
1923 ' it was created or renamed from the filesystem'
1923 ' it was created or renamed from the filesystem'
1924 ' please run the application again'
1924 ' please run the application again'
1925 ' in order to rescan repositories') % repo_name, category='error')
1925 ' in order to rescan repositories') % repo_name, category='error')
1926
1926
1927
1927
1928 def ip_range(ip_addr):
1928 def ip_range(ip_addr):
1929 from rhodecode.model.db import UserIpMap
1929 from rhodecode.model.db import UserIpMap
1930 s, e = UserIpMap._get_ip_range(ip_addr)
1930 s, e = UserIpMap._get_ip_range(ip_addr)
1931 return '%s - %s' % (s, e)
1931 return '%s - %s' % (s, e)
1932
1932
1933
1933
1934 def form(url, method='post', needs_csrf_token=True, **attrs):
1934 def form(url, method='post', needs_csrf_token=True, **attrs):
1935 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1935 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1936 if method.lower() != 'get' and needs_csrf_token:
1936 if method.lower() != 'get' and needs_csrf_token:
1937 raise Exception(
1937 raise Exception(
1938 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1938 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1939 'CSRF token. If the endpoint does not require such token you can ' +
1939 'CSRF token. If the endpoint does not require such token you can ' +
1940 'explicitly set the parameter needs_csrf_token to false.')
1940 'explicitly set the parameter needs_csrf_token to false.')
1941
1941
1942 return wh_form(url, method=method, **attrs)
1942 return wh_form(url, method=method, **attrs)
1943
1943
1944
1944
1945 def secure_form(form_url, method="POST", multipart=False, **attrs):
1945 def secure_form(form_url, method="POST", multipart=False, **attrs):
1946 """Start a form tag that points the action to an url. This
1946 """Start a form tag that points the action to an url. This
1947 form tag will also include the hidden field containing
1947 form tag will also include the hidden field containing
1948 the auth token.
1948 the auth token.
1949
1949
1950 The url options should be given either as a string, or as a
1950 The url options should be given either as a string, or as a
1951 ``url()`` function. The method for the form defaults to POST.
1951 ``url()`` function. The method for the form defaults to POST.
1952
1952
1953 Options:
1953 Options:
1954
1954
1955 ``multipart``
1955 ``multipart``
1956 If set to True, the enctype is set to "multipart/form-data".
1956 If set to True, the enctype is set to "multipart/form-data".
1957 ``method``
1957 ``method``
1958 The method to use when submitting the form, usually either
1958 The method to use when submitting the form, usually either
1959 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1959 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1960 hidden input with name _method is added to simulate the verb
1960 hidden input with name _method is added to simulate the verb
1961 over POST.
1961 over POST.
1962
1962
1963 """
1963 """
1964 from webhelpers.pylonslib.secure_form import insecure_form
1964 from webhelpers.pylonslib.secure_form import insecure_form
1965
1965
1966 if 'request' in attrs:
1966 if 'request' in attrs:
1967 session = attrs['request'].session
1967 session = attrs['request'].session
1968 del attrs['request']
1968 del attrs['request']
1969 else:
1969 else:
1970 raise ValueError(
1970 raise ValueError(
1971 'Calling this form requires request= to be passed as argument')
1971 'Calling this form requires request= to be passed as argument')
1972
1972
1973 form = insecure_form(form_url, method, multipart, **attrs)
1973 form = insecure_form(form_url, method, multipart, **attrs)
1974 token = literal(
1974 token = literal(
1975 '<input type="hidden" id="{}" name="{}" value="{}">'.format(
1975 '<input type="hidden" id="{}" name="{}" value="{}">'.format(
1976 csrf_token_key, csrf_token_key, get_csrf_token(session)))
1976 csrf_token_key, csrf_token_key, get_csrf_token(session)))
1977
1977
1978 return literal("%s\n%s" % (form, token))
1978 return literal("%s\n%s" % (form, token))
1979
1979
1980
1980
1981 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1981 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1982 select_html = select(name, selected, options, **attrs)
1982 select_html = select(name, selected, options, **attrs)
1983
1983
1984 select2 = """
1984 select2 = """
1985 <script>
1985 <script>
1986 $(document).ready(function() {
1986 $(document).ready(function() {
1987 $('#%s').select2({
1987 $('#%s').select2({
1988 containerCssClass: 'drop-menu %s',
1988 containerCssClass: 'drop-menu %s',
1989 dropdownCssClass: 'drop-menu-dropdown',
1989 dropdownCssClass: 'drop-menu-dropdown',
1990 dropdownAutoWidth: true%s
1990 dropdownAutoWidth: true%s
1991 });
1991 });
1992 });
1992 });
1993 </script>
1993 </script>
1994 """
1994 """
1995
1995
1996 filter_option = """,
1996 filter_option = """,
1997 minimumResultsForSearch: -1
1997 minimumResultsForSearch: -1
1998 """
1998 """
1999 input_id = attrs.get('id') or name
1999 input_id = attrs.get('id') or name
2000 extra_classes = ' '.join(attrs.pop('extra_classes', []))
2000 extra_classes = ' '.join(attrs.pop('extra_classes', []))
2001 filter_enabled = "" if enable_filter else filter_option
2001 filter_enabled = "" if enable_filter else filter_option
2002 select_script = literal(select2 % (input_id, extra_classes, filter_enabled))
2002 select_script = literal(select2 % (input_id, extra_classes, filter_enabled))
2003
2003
2004 return literal(select_html+select_script)
2004 return literal(select_html+select_script)
2005
2005
2006
2006
2007 def get_visual_attr(tmpl_context_var, attr_name):
2007 def get_visual_attr(tmpl_context_var, attr_name):
2008 """
2008 """
2009 A safe way to get a variable from visual variable of template context
2009 A safe way to get a variable from visual variable of template context
2010
2010
2011 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
2011 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
2012 :param attr_name: name of the attribute we fetch from the c.visual
2012 :param attr_name: name of the attribute we fetch from the c.visual
2013 """
2013 """
2014 visual = getattr(tmpl_context_var, 'visual', None)
2014 visual = getattr(tmpl_context_var, 'visual', None)
2015 if not visual:
2015 if not visual:
2016 return
2016 return
2017 else:
2017 else:
2018 return getattr(visual, attr_name, None)
2018 return getattr(visual, attr_name, None)
2019
2019
2020
2020
2021 def get_last_path_part(file_node):
2021 def get_last_path_part(file_node):
2022 if not file_node.path:
2022 if not file_node.path:
2023 return u'/'
2023 return u'/'
2024
2024
2025 path = safe_unicode(file_node.path.split('/')[-1])
2025 path = safe_unicode(file_node.path.split('/')[-1])
2026 return u'../' + path
2026 return u'../' + path
2027
2027
2028
2028
2029 def route_url(*args, **kwargs):
2029 def route_url(*args, **kwargs):
2030 """
2030 """
2031 Wrapper around pyramids `route_url` (fully qualified url) function.
2031 Wrapper around pyramids `route_url` (fully qualified url) function.
2032 """
2032 """
2033 req = get_current_request()
2033 req = get_current_request()
2034 return req.route_url(*args, **kwargs)
2034 return req.route_url(*args, **kwargs)
2035
2035
2036
2036
2037 def route_path(*args, **kwargs):
2037 def route_path(*args, **kwargs):
2038 """
2038 """
2039 Wrapper around pyramids `route_path` function.
2039 Wrapper around pyramids `route_path` function.
2040 """
2040 """
2041 req = get_current_request()
2041 req = get_current_request()
2042 return req.route_path(*args, **kwargs)
2042 return req.route_path(*args, **kwargs)
2043
2043
2044
2044
2045 def route_path_or_none(*args, **kwargs):
2045 def route_path_or_none(*args, **kwargs):
2046 try:
2046 try:
2047 return route_path(*args, **kwargs)
2047 return route_path(*args, **kwargs)
2048 except KeyError:
2048 except KeyError:
2049 return None
2049 return None
2050
2050
2051
2051
2052 def current_route_path(request, **kw):
2052 def current_route_path(request, **kw):
2053 new_args = request.GET.mixed()
2053 new_args = request.GET.mixed()
2054 new_args.update(kw)
2054 new_args.update(kw)
2055 return request.current_route_path(_query=new_args)
2055 return request.current_route_path(_query=new_args)
2056
2056
2057
2057
2058 def curl_api_example(method, args):
2058 def curl_api_example(method, args):
2059 args_json = json.dumps(OrderedDict([
2059 args_json = json.dumps(OrderedDict([
2060 ('id', 1),
2060 ('id', 1),
2061 ('auth_token', 'SECRET'),
2061 ('auth_token', 'SECRET'),
2062 ('method', method),
2062 ('method', method),
2063 ('args', args)
2063 ('args', args)
2064 ]))
2064 ]))
2065
2065
2066 return "curl {api_url} -X POST -H 'content-type:text/plain' --data-binary '{args_json}'".format(
2066 return "curl {api_url} -X POST -H 'content-type:text/plain' --data-binary '{args_json}'".format(
2067 api_url=route_url('apiv2'),
2067 api_url=route_url('apiv2'),
2068 args_json=args_json
2068 args_json=args_json
2069 )
2069 )
2070
2070
2071
2071
2072 def api_call_example(method, args):
2072 def api_call_example(method, args):
2073 """
2073 """
2074 Generates an API call example via CURL
2074 Generates an API call example via CURL
2075 """
2075 """
2076 curl_call = curl_api_example(method, args)
2076 curl_call = curl_api_example(method, args)
2077
2077
2078 return literal(
2078 return literal(
2079 curl_call +
2079 curl_call +
2080 "<br/><br/>SECRET can be found in <a href=\"{token_url}\">auth-tokens</a> page, "
2080 "<br/><br/>SECRET can be found in <a href=\"{token_url}\">auth-tokens</a> page, "
2081 "and needs to be of `api calls` role."
2081 "and needs to be of `api calls` role."
2082 .format(token_url=route_url('my_account_auth_tokens')))
2082 .format(token_url=route_url('my_account_auth_tokens')))
2083
2083
2084
2084
2085 def notification_description(notification, request):
2085 def notification_description(notification, request):
2086 """
2086 """
2087 Generate notification human readable description based on notification type
2087 Generate notification human readable description based on notification type
2088 """
2088 """
2089 from rhodecode.model.notification import NotificationModel
2089 from rhodecode.model.notification import NotificationModel
2090 return NotificationModel().make_description(
2090 return NotificationModel().make_description(
2091 notification, translate=request.translate)
2091 notification, translate=request.translate)
2092
2092
2093
2093
2094 def go_import_header(request, db_repo=None):
2094 def go_import_header(request, db_repo=None):
2095 """
2095 """
2096 Creates a header for go-import functionality in Go Lang
2096 Creates a header for go-import functionality in Go Lang
2097 """
2097 """
2098
2098
2099 if not db_repo:
2099 if not db_repo:
2100 return
2100 return
2101 if 'go-get' not in request.GET:
2101 if 'go-get' not in request.GET:
2102 return
2102 return
2103
2103
2104 clone_url = db_repo.clone_url()
2104 clone_url = db_repo.clone_url()
2105 prefix = re.split(r'^https?:\/\/', clone_url)[-1]
2105 prefix = re.split(r'^https?:\/\/', clone_url)[-1]
2106 # we have a repo and go-get flag,
2106 # we have a repo and go-get flag,
2107 return literal('<meta name="go-import" content="{} {} {}">'.format(
2107 return literal('<meta name="go-import" content="{} {} {}">'.format(
2108 prefix, db_repo.repo_type, clone_url))
2108 prefix, db_repo.repo_type, clone_url))
2109
2109
2110
2110
2111 def reviewer_as_json(*args, **kwargs):
2111 def reviewer_as_json(*args, **kwargs):
2112 from rhodecode.apps.repository.utils import reviewer_as_json as _reviewer_as_json
2112 from rhodecode.apps.repository.utils import reviewer_as_json as _reviewer_as_json
2113 return _reviewer_as_json(*args, **kwargs)
2113 return _reviewer_as_json(*args, **kwargs)
2114
2114
2115
2115
2116 def get_repo_view_type(request):
2116 def get_repo_view_type(request):
2117 route_name = request.matched_route.name
2117 route_name = request.matched_route.name
2118 route_to_view_type = {
2118 route_to_view_type = {
2119 'repo_changelog': 'commits',
2119 'repo_changelog': 'commits',
2120 'repo_commits': 'commits',
2120 'repo_commits': 'commits',
2121 'repo_files': 'files',
2121 'repo_files': 'files',
2122 'repo_summary': 'summary',
2122 'repo_summary': 'summary',
2123 'repo_commit': 'commit'
2123 'repo_commit': 'commit'
2124 }
2124 }
2125
2125
2126 return route_to_view_type.get(route_name)
2126 return route_to_view_type.get(route_name)
2127
2128
2129 def is_active(menu_entry, selected):
2130 """
2131 Returns active class for selecting menus in templates
2132 <li class=${h.is_active('settings', current_active)}></li>
2133 """
2134 if not isinstance(menu_entry, list):
2135 menu_entry = [menu_entry]
2136
2137 if selected in menu_entry:
2138 return "active"
@@ -1,43 +1,43 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Repositories defaults')}
5 ${_('Repositories defaults')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Repositories defaults')}
14 ${_('Repositories defaults')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="menu_bar_subnav()">
21 <%def name="menu_bar_subnav()">
22 ${self.admin_menu(active='defaults')}
22 ${self.admin_menu(active='defaults')}
23 </%def>
23 </%def>
24
24
25 <%def name="main()">
25 <%def name="main()">
26 <div class="box">
26 <div class="box">
27
27
28 ##main
28 ##main
29 <div class="sidebar-col-wrapper">
29 <div class="sidebar-col-wrapper">
30 <div class="sidebar">
30 <div class="sidebar">
31 <ul class="nav nav-pills nav-stacked">
31 <ul class="nav nav-pills nav-stacked">
32 <li class="${'active' if c.active=='repositories' else ''}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Repository')}</a></li>
32 <li class="${h.is_active('repositories', c.active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Repository')}</a></li>
33 </ul>
33 </ul>
34 </div>
34 </div>
35
35
36 <div class="main-content-full-width">
36 <div class="main-content-full-width">
37 <%include file="/admin/defaults/defaults_${c.active}.mako"/>
37 <%include file="/admin/defaults/defaults_${c.active}.mako"/>
38 </div>
38 </div>
39
39
40 </div>
40 </div>
41 </div>
41 </div>
42
42
43 </%def>
43 </%def>
@@ -1,139 +1,139 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 %if c.show_private:
5 %if c.show_private:
6 ${_('Private Gists for user {}').format(c.rhodecode_user.username)}
6 ${_('Private Gists for user {}').format(c.rhodecode_user.username)}
7 %elif c.show_public:
7 %elif c.show_public:
8 ${_('Public Gists for user {}').format(c.rhodecode_user.username)}
8 ${_('Public Gists for user {}').format(c.rhodecode_user.username)}
9 %else:
9 %else:
10 ${_('Public Gists')}
10 ${_('Public Gists')}
11 %endif
11 %endif
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 &middot; ${h.branding(c.rhodecode_name)}
13 &middot; ${h.branding(c.rhodecode_name)}
14 %endif
14 %endif
15 </%def>
15 </%def>
16
16
17 <%def name="breadcrumbs_links()"></%def>
17 <%def name="breadcrumbs_links()"></%def>
18
18
19 <%def name="menu_bar_nav()">
19 <%def name="menu_bar_nav()">
20 ${self.menu_items(active='gists')}
20 ${self.menu_items(active='gists')}
21 </%def>
21 </%def>
22
22
23 <%def name="main()">
23 <%def name="main()">
24
24
25 <div class="box">
25 <div class="box">
26 <div class="title">
26 <div class="title">
27
27
28 <ul class="button-links">
28 <ul class="button-links">
29 % if c.is_super_admin:
29 % if c.is_super_admin:
30 <li class="btn ${('active' if c.active=='all' else '')}"><a href="${h.route_path('gists_show', _query={'all': 1})}">${_('All gists')}</a></li>
30 <li class="btn ${h.is_active('all', c.active)}"><a href="${h.route_path('gists_show', _query={'all': 1})}">${_('All gists')}</a></li>
31 %endif
31 %endif
32 <li class="btn ${('active' if c.active=='public' else '')}"><a href="${h.route_path('gists_show')}">${_('All public')}</a></li>
32 <li class="btn ${h.is_active('public', c.active)}"><a href="${h.route_path('gists_show')}">${_('All public')}</a></li>
33 %if c.rhodecode_user.username != h.DEFAULT_USER:
33 %if c.rhodecode_user.username != h.DEFAULT_USER:
34 <li class="btn ${('active' if c.active=='my_all' else '')}"><a href="${h.route_path('gists_show', _query={'public':1, 'private': 1})}">${_('My gists')}</a></li>
34 <li class="btn ${h.is_active('my_all', c.active)}"><a href="${h.route_path('gists_show', _query={'public':1, 'private': 1})}">${_('My gists')}</a></li>
35 <li class="btn ${('active' if c.active=='my_private' else '')}"><a href="${h.route_path('gists_show', _query={'private': 1})}">${_('My private')}</a></li>
35 <li class="btn ${h.is_active('my_private', c.active)}"><a href="${h.route_path('gists_show', _query={'private': 1})}">${_('My private')}</a></li>
36 <li class="btn ${('active' if c.active=='my_public' else '')}"><a href="${h.route_path('gists_show', _query={'public': 1})}">${_('My public')}</a></li>
36 <li class="btn ${h.is_active('my_public', c.active)}"><a href="${h.route_path('gists_show', _query={'public': 1})}">${_('My public')}</a></li>
37 %endif
37 %endif
38 </ul>
38 </ul>
39
39
40 <div class="grid-quick-filter">
40 <div class="grid-quick-filter">
41 <ul class="grid-filter-box">
41 <ul class="grid-filter-box">
42 <li class="grid-filter-box-icon">
42 <li class="grid-filter-box-icon">
43 <i class="icon-search"></i>
43 <i class="icon-search"></i>
44 </li>
44 </li>
45 <li class="grid-filter-box-input">
45 <li class="grid-filter-box-input">
46 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
46 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
47 </li>
47 </li>
48 </ul>
48 </ul>
49 </div>
49 </div>
50
50
51 </div>
51 </div>
52
52
53 <div class="main-content-full-width">
53 <div class="main-content-full-width">
54 <div id="repos_list_wrap">
54 <div id="repos_list_wrap">
55 <table id="gist_list_table" class="display"></table>
55 <table id="gist_list_table" class="display"></table>
56 </div>
56 </div>
57 </div>
57 </div>
58
58
59 </div>
59 </div>
60
60
61 <script type="text/javascript">
61 <script type="text/javascript">
62 $(document).ready(function() {
62 $(document).ready(function() {
63
63
64 var get_datatable_count = function(){
64 var get_datatable_count = function(){
65 var api = $('#gist_list_table').dataTable().api();
65 var api = $('#gist_list_table').dataTable().api();
66 $('#gists_count').text(api.page.info().recordsDisplay);
66 $('#gists_count').text(api.page.info().recordsDisplay);
67 };
67 };
68
68
69
69
70 // custom filter that filters by access_id, description or author
70 // custom filter that filters by access_id, description or author
71 $.fn.dataTable.ext.search.push(
71 $.fn.dataTable.ext.search.push(
72 function( settings, data, dataIndex ) {
72 function( settings, data, dataIndex ) {
73 var query = $('#q_filter').val();
73 var query = $('#q_filter').val();
74 var author = data[0].strip();
74 var author = data[0].strip();
75 var access_id = data[2].strip();
75 var access_id = data[2].strip();
76 var description = data[3].strip();
76 var description = data[3].strip();
77
77
78 var query_str = (access_id + " " + author + " " + description).toLowerCase();
78 var query_str = (access_id + " " + author + " " + description).toLowerCase();
79
79
80 if(query_str.indexOf(query.toLowerCase()) !== -1){
80 if(query_str.indexOf(query.toLowerCase()) !== -1){
81 return true;
81 return true;
82 }
82 }
83 return false;
83 return false;
84 }
84 }
85 );
85 );
86
86
87 // gists list
87 // gists list
88 $('#gist_list_table').DataTable({
88 $('#gist_list_table').DataTable({
89 data: ${c.data|n},
89 data: ${c.data|n},
90 dom: 'rtp',
90 dom: 'rtp',
91 pageLength: ${c.visual.dashboard_items},
91 pageLength: ${c.visual.dashboard_items},
92 order: [[ 4, "desc" ]],
92 order: [[ 4, "desc" ]],
93 columns: [
93 columns: [
94 { data: {"_": "author",
94 { data: {"_": "author",
95 "sort": "author_raw"}, title: "${_("Author")}", width: "250px", className: "td-user" },
95 "sort": "author_raw"}, title: "${_("Author")}", width: "250px", className: "td-user" },
96 { data: {"_": "type",
96 { data: {"_": "type",
97 "sort": "type"}, title: "${_("Type")}", width: "70px", className: "td-tags" },
97 "sort": "type"}, title: "${_("Type")}", width: "70px", className: "td-tags" },
98 { data: {"_": "access_id",
98 { data: {"_": "access_id",
99 "sort": "access_id"}, title: "${_("Name")}", width:"150px", className: "td-componentname" },
99 "sort": "access_id"}, title: "${_("Name")}", width:"150px", className: "td-componentname" },
100 { data: {"_": "description",
100 { data: {"_": "description",
101 "sort": "description"}, title: "${_("Description")}", width: "250px", className: "td-description" },
101 "sort": "description"}, title: "${_("Description")}", width: "250px", className: "td-description" },
102 { data: {"_": "created_on",
102 { data: {"_": "created_on",
103 "sort": "created_on_raw"}, title: "${_("Created on")}", className: "td-time" },
103 "sort": "created_on_raw"}, title: "${_("Created on")}", className: "td-time" },
104 { data: {"_": "expires",
104 { data: {"_": "expires",
105 "sort": "expires"}, title: "${_("Expires")}", className: "td-exp" }
105 "sort": "expires"}, title: "${_("Expires")}", className: "td-exp" }
106 ],
106 ],
107 language: {
107 language: {
108 paginate: DEFAULT_GRID_PAGINATION,
108 paginate: DEFAULT_GRID_PAGINATION,
109 emptyTable: _gettext("No gists available yet.")
109 emptyTable: _gettext("No gists available yet.")
110 },
110 },
111 "initComplete": function( settings, json ) {
111 "initComplete": function( settings, json ) {
112 timeagoActivate();
112 timeagoActivate();
113 tooltipActivate();
113 tooltipActivate();
114 get_datatable_count();
114 get_datatable_count();
115 }
115 }
116 });
116 });
117
117
118 // update the counter when things change
118 // update the counter when things change
119 $('#gist_list_table').on('draw.dt', function() {
119 $('#gist_list_table').on('draw.dt', function() {
120 timeagoActivate();
120 timeagoActivate();
121 tooltipActivate();
121 tooltipActivate();
122 get_datatable_count();
122 get_datatable_count();
123 });
123 });
124
124
125 // filter, filter both grids
125 // filter, filter both grids
126 $('#q_filter').on( 'keyup', function () {
126 $('#q_filter').on( 'keyup', function () {
127 var repo_api = $('#gist_list_table').dataTable().api();
127 var repo_api = $('#gist_list_table').dataTable().api();
128 repo_api
128 repo_api
129 .draw();
129 .draw();
130 });
130 });
131
131
132 // refilter table if page load via back button
132 // refilter table if page load via back button
133 $("#q_filter").trigger('keyup');
133 $("#q_filter").trigger('keyup');
134
134
135 });
135 });
136
136
137 </script>
137 </script>
138 </%def>
138 </%def>
139
139
@@ -1,57 +1,57 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('My account')} ${c.rhodecode_user.username}
5 ${_('My account')} ${c.rhodecode_user.username}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${_('My Account')}
12 ${_('My Account')}
13 </%def>
13 </%def>
14
14
15 <%def name="menu_bar_nav()">
15 <%def name="menu_bar_nav()">
16 ${self.menu_items(active='my_account')}
16 ${self.menu_items(active='my_account')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24
24
25 <div class="sidebar-col-wrapper scw-small">
25 <div class="sidebar-col-wrapper scw-small">
26 ##main
26 ##main
27 <div class="sidebar">
27 <div class="sidebar">
28 <ul class="nav nav-pills nav-stacked">
28 <ul class="nav nav-pills nav-stacked">
29 <li class="${'active' if c.active=='profile' or c.active=='profile_edit' else ''}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
29 <li class="${h.is_active(['profile', 'profile_edit'], c.active)}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
30 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
30 <li class="${h.is_active('emails', c.active)}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
31 <li class="${'active' if c.active=='password' else ''}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
31 <li class="${h.is_active('password', c.active)}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
32 <li class="${'active' if c.active=='bookmarks' else ''}"><a href="${h.route_path('my_account_bookmarks')}">${_('Bookmarks')}</a></li>
32 <li class="${h.is_active('bookmarks', c.active)}"><a href="${h.route_path('my_account_bookmarks')}">${_('Bookmarks')}</a></li>
33 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
33 <li class="${h.is_active('auth_tokens', c.active)}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
34 <li class="${'active' if c.active in ['ssh_keys', 'ssh_keys_generate'] else ''}"><a href="${h.route_path('my_account_ssh_keys')}">${_('SSH Keys')}</a></li>
34 <li class="${h.is_active(['ssh_keys', 'ssh_keys_generate'], c.active)}"><a href="${h.route_path('my_account_ssh_keys')}">${_('SSH Keys')}</a></li>
35 <li class="${'active' if c.active=='user_group_membership' else ''}"><a href="${h.route_path('my_account_user_group_membership')}">${_('User Group Membership')}</a></li>
35 <li class="${h.is_active('user_group_membership', c.active)}"><a href="${h.route_path('my_account_user_group_membership')}">${_('User Group Membership')}</a></li>
36
36
37 ## TODO: Find a better integration of oauth/saml views into navigation.
37 ## TODO: Find a better integration of oauth/saml views into navigation.
38 <% my_account_external_url = h.route_path_or_none('my_account_external_identity') %>
38 <% my_account_external_url = h.route_path_or_none('my_account_external_identity') %>
39 % if my_account_external_url:
39 % if my_account_external_url:
40 <li class="${'active' if c.active=='external_identity' else ''}"><a href="${my_account_external_url}">${_('External Identities')}</a></li>
40 <li class="${h.is_active('external_identity', c.active)}"><a href="${my_account_external_url}">${_('External Identities')}</a></li>
41 % endif
41 % endif
42
42
43 <li class="${'active' if c.active=='repos' else ''}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li>
43 <li class="${h.is_active('repos', c.active)}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li>
44 <li class="${'active' if c.active=='watched' else ''}"><a href="${h.route_path('my_account_watched')}">${_('Watched')}</a></li>
44 <li class="${h.is_active('watched', c.active)}"><a href="${h.route_path('my_account_watched')}">${_('Watched')}</a></li>
45 <li class="${'active' if c.active=='pullrequests' else ''}"><a href="${h.route_path('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
45 <li class="${h.is_active('pullrequests', c.active)}"><a href="${h.route_path('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
46 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
46 <li class="${h.is_active('perms', c.active)}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
47 <li class="${'active' if c.active=='my_notifications' else ''}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
47 <li class="${h.is_active('my_notifications', c.active)}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
48 </ul>
48 </ul>
49 </div>
49 </div>
50
50
51 <div class="main-content-full-width">
51 <div class="main-content-full-width">
52 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
52 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
53 </div>
53 </div>
54 </div>
54 </div>
55 </div>
55 </div>
56
56
57 </%def>
57 </%def>
@@ -1,65 +1,65 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Permissions Administration')}
5 ${_('Permissions Administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Permissions')}
14 ${_('Permissions')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="menu_bar_subnav()">
21 <%def name="menu_bar_subnav()">
22 ${self.admin_menu(active='permissions')}
22 ${self.admin_menu(active='permissions')}
23 </%def>
23 </%def>
24
24
25 <%def name="main()">
25 <%def name="main()">
26 <div class="box">
26 <div class="box">
27
27
28 <div class="sidebar-col-wrapper scw-small">
28 <div class="sidebar-col-wrapper scw-small">
29 ##main
29 ##main
30 <div class="sidebar">
30 <div class="sidebar">
31 <ul class="nav nav-pills nav-stacked">
31 <ul class="nav nav-pills nav-stacked">
32 <li class="${'active' if c.active=='application' else ''}">
32 <li class="${h.is_active('application', c.active)}">
33 <a href="${h.route_path('admin_permissions_application')}">${_('Application')}</a>
33 <a href="${h.route_path('admin_permissions_application')}">${_('Application')}</a>
34 </li>
34 </li>
35 <li class="${'active' if c.active=='global' else ''}">
35 <li class="${h.is_active('global', c.active)}">
36 <a href="${h.route_path('admin_permissions_global')}">${_('Global')}</a>
36 <a href="${h.route_path('admin_permissions_global')}">${_('Global')}</a>
37 </li>
37 </li>
38 <li class="${'active' if c.active=='objects' else ''}">
38 <li class="${h.is_active('objects', c.active)}">
39 <a href="${h.route_path('admin_permissions_object')}">${_('Object')}</a>
39 <a href="${h.route_path('admin_permissions_object')}">${_('Object')}</a>
40 </li>
40 </li>
41 <li class="${'active' if c.active=='branch' else ''}">
41 <li class="${h.is_active('branch', c.active)}">
42 <a href="${h.route_path('admin_permissions_branch')}">${_('Branch')}</a>
42 <a href="${h.route_path('admin_permissions_branch')}">${_('Branch')}</a>
43 </li>
43 </li>
44 <li class="${'active' if c.active=='ips' else ''}">
44 <li class="${h.is_active('ips', c.active)}">
45 <a href="${h.route_path('admin_permissions_ips')}">${_('IP Whitelist')}</a>
45 <a href="${h.route_path('admin_permissions_ips')}">${_('IP Whitelist')}</a>
46 </li>
46 </li>
47 <li class="${'active' if c.active=='auth_token_access' else ''}">
47 <li class="${h.is_active('auth_token_access', c.active)}">
48 <a href="${h.route_path('admin_permissions_auth_token_access')}">${_('AuthToken Access')}</a>
48 <a href="${h.route_path('admin_permissions_auth_token_access')}">${_('AuthToken Access')}</a>
49 </li>
49 </li>
50 <li class="${'active' if c.active=='ssh_keys' else ''}">
50 <li class="${h.is_active('ssh_keys', c.active)}">
51 <a href="${h.route_path('admin_permissions_ssh_keys')}">${_('SSH Keys')}</a>
51 <a href="${h.route_path('admin_permissions_ssh_keys')}">${_('SSH Keys')}</a>
52 </li>
52 </li>
53 <li class="${'active' if c.active=='perms' else ''}">
53 <li class="${h.is_active('perms', c.active)}">
54 <a href="${h.route_path('admin_permissions_overview')}">${_('Overview')}</a>
54 <a href="${h.route_path('admin_permissions_overview')}">${_('Overview')}</a>
55 </li>
55 </li>
56 </ul>
56 </ul>
57 </div>
57 </div>
58
58
59 <div class="main-content-full-width">
59 <div class="main-content-full-width">
60 <%include file="/admin/permissions/permissions_${c.active}.mako"/>
60 <%include file="/admin/permissions/permissions_${c.active}.mako"/>
61 </div>
61 </div>
62 </div>
62 </div>
63 </div>
63 </div>
64
64
65 </%def>
65 </%def>
@@ -1,44 +1,44 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s repository group settings') % c.repo_group.name}
5 ${_('%s repository group settings') % c.repo_group.name}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="menu_bar_nav()">
11 <%def name="menu_bar_nav()">
12 ${self.menu_items(active='admin')}
12 ${self.menu_items(active='admin')}
13 </%def>
13 </%def>
14
14
15 <%def name="menu_bar_subnav()">
15 <%def name="menu_bar_subnav()">
16 ${self.repo_group_menu(active='settings')}
16 ${self.repo_group_menu(active='settings')}
17 </%def>
17 </%def>
18
18
19 <%def name="main_content()">
19 <%def name="main_content()">
20 <%include file="/admin/repo_groups/repo_group_edit_${c.active}.mako"/>
20 <%include file="/admin/repo_groups/repo_group_edit_${c.active}.mako"/>
21 </%def>
21 </%def>
22
22
23 <%def name="main()">
23 <%def name="main()">
24
24
25 <div class="box">
25 <div class="box">
26 <div class="sidebar-col-wrapper">
26 <div class="sidebar-col-wrapper">
27 ##main
27 ##main
28 <div class="sidebar">
28 <div class="sidebar">
29 <ul class="nav nav-pills nav-stacked">
29 <ul class="nav nav-pills nav-stacked">
30 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
30 <li class="${h.is_active('settings', c.active)}"><a href="${h.route_path('edit_repo_group', repo_group_name=c.repo_group.group_name)}">${_('Settings')}</a></li>
31 <li class="${'active' if c.active=='permissions' else ''}"><a href="${h.route_path('edit_repo_group_perms', repo_group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
31 <li class="${h.is_active('permissions', c.active)}"><a href="${h.route_path('edit_repo_group_perms', repo_group_name=c.repo_group.group_name)}">${_('Permissions')}</a></li>
32 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.route_path('edit_repo_group_advanced', repo_group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
32 <li class="${h.is_active('advanced', c.active)}"><a href="${h.route_path('edit_repo_group_advanced', repo_group_name=c.repo_group.group_name)}">${_('Advanced')}</a></li>
33 <li class="${'active' if c.active=='integrations' else ''}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
33 <li class="${h.is_active('integrations', c.active)}"><a href="${h.route_path('repo_group_integrations_home', repo_group_name=c.repo_group.group_name)}">${_('Integrations')}</a></li>
34 </ul>
34 </ul>
35 </div>
35 </div>
36
36
37 <div class="main-content-full-width">
37 <div class="main-content-full-width">
38 ${self.main_content()}
38 ${self.main_content()}
39 </div>
39 </div>
40
40
41 </div>
41 </div>
42 </div>
42 </div>
43
43
44 </%def>
44 </%def>
@@ -1,104 +1,104 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 ##
2 ##
3 ## See also repo_settings.html
3 ## See also repo_settings.html
4 ##
4 ##
5 <%inherit file="/base/base.mako"/>
5 <%inherit file="/base/base.mako"/>
6
6
7 <%def name="title()">
7 <%def name="title()">
8 ${_('%s repository settings') % c.rhodecode_db_repo.repo_name}
8 ${_('{} repository settings').format(c.rhodecode_db_repo.repo_name)}
9 %if c.rhodecode_name:
9 %if c.rhodecode_name:
10 &middot; ${h.branding(c.rhodecode_name)}
10 &middot; ${h.branding(c.rhodecode_name)}
11 %endif
11 %endif
12 </%def>
12 </%def>
13
13
14 <%def name="breadcrumbs_links()">
14 <%def name="breadcrumbs_links()">
15 ${_('Settings')}
15 ${_('Settings')}
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='repositories')}
19 ${self.menu_items(active='repositories')}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_subnav()">
22 <%def name="menu_bar_subnav()">
23 ${self.repo_menu(active='settings')}
23 ${self.repo_menu(active='settings')}
24 </%def>
24 </%def>
25
25
26 <%def name="main_content()">
26 <%def name="main_content()">
27 % if hasattr(c, 'repo_edit_template'):
27 % if hasattr(c, 'repo_edit_template'):
28 <%include file="${c.repo_edit_template}"/>
28 <%include file="${c.repo_edit_template}"/>
29 % else:
29 % else:
30 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
30 <%include file="/admin/repos/repo_edit_${c.active}.mako"/>
31 % endif
31 % endif
32 </%def>
32 </%def>
33
33
34
34
35 <%def name="main()">
35 <%def name="main()">
36 <div class="box">
36 <div class="box">
37
37
38 <div class="sidebar-col-wrapper scw-small">
38 <div class="sidebar-col-wrapper scw-small">
39 <div class="sidebar">
39 <div class="sidebar">
40 <ul class="nav nav-pills nav-stacked">
40 <ul class="nav nav-pills nav-stacked">
41 <li class="${'active' if c.active=='settings' else ''}">
41 <li class="${h.is_active('settings', c.active)}">
42 <a href="${h.route_path('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
42 <a href="${h.route_path('edit_repo', repo_name=c.repo_name)}">${_('Settings')}</a>
43 </li>
43 </li>
44 <li class="${'active' if c.active=='permissions' else ''}">
44 <li class="${h.is_active('permissions', c.active)}">
45 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Permissions')}</a>
45 <a href="${h.route_path('edit_repo_perms', repo_name=c.repo_name)}">${_('Access Permissions')}</a>
46 </li>
46 </li>
47 <li class="${'active' if c.active=='permissions_branch' else ''}">
47 <li class="${h.is_active('permissions_branch', c.active)}">
48 <a href="${h.route_path('edit_repo_perms_branch', repo_name=c.repo_name)}">${_('Branch Permissions')}</a>
48 <a href="${h.route_path('edit_repo_perms_branch', repo_name=c.repo_name)}">${_('Branch Permissions')}</a>
49 </li>
49 </li>
50 <li class="${'active' if c.active=='advanced' else ''}">
50 <li class="${h.is_active('advanced', c.active)}">
51 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
51 <a href="${h.route_path('edit_repo_advanced', repo_name=c.repo_name)}">${_('Advanced')}</a>
52 </li>
52 </li>
53 <li class="${'active' if c.active=='vcs' else ''}">
53 <li class="${h.is_active('vcs', c.active)}">
54 <a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">${_('VCS')}</a>
54 <a href="${h.route_path('edit_repo_vcs', repo_name=c.repo_name)}">${_('VCS')}</a>
55 </li>
55 </li>
56 <li class="${'active' if c.active=='fields' else ''}">
56 <li class="${h.is_active('fields', c.active)}">
57 <a href="${h.route_path('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
57 <a href="${h.route_path('edit_repo_fields', repo_name=c.repo_name)}">${_('Extra Fields')}</a>
58 </li>
58 </li>
59 <li class="${'active' if c.active=='issuetracker' else ''}">
59 <li class="${h.is_active('issuetracker', c.active)}">
60 <a href="${h.route_path('edit_repo_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
60 <a href="${h.route_path('edit_repo_issuetracker', repo_name=c.repo_name)}">${_('Issue Tracker')}</a>
61 </li>
61 </li>
62 <li class="${'active' if c.active=='caches' else ''}">
62 <li class="${h.is_active('caches', c.active)}">
63 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
63 <a href="${h.route_path('edit_repo_caches', repo_name=c.repo_name)}">${_('Caches')}</a>
64 </li>
64 </li>
65 %if c.rhodecode_db_repo.repo_type != 'svn':
65 %if c.rhodecode_db_repo.repo_type != 'svn':
66 <li class="${'active' if c.active=='remote' else ''}">
66 <li class="${h.is_active('remote', c.active)}">
67 <a href="${h.route_path('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote sync')}</a>
67 <a href="${h.route_path('edit_repo_remote', repo_name=c.repo_name)}">${_('Remote sync')}</a>
68 </li>
68 </li>
69 %endif
69 %endif
70 <li class="${'active' if c.active=='statistics' else ''}">
70 <li class="${h.is_active('statistics', c.active)}">
71 <a href="${h.route_path('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
71 <a href="${h.route_path('edit_repo_statistics', repo_name=c.repo_name)}">${_('Statistics')}</a>
72 </li>
72 </li>
73 <li class="${'active' if c.active=='integrations' else ''}">
73 <li class="${h.is_active('integrations', c.active)}">
74 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
74 <a href="${h.route_path('repo_integrations_home', repo_name=c.repo_name)}">${_('Integrations')}</a>
75 </li>
75 </li>
76 %if c.rhodecode_db_repo.repo_type != 'svn':
76 %if c.rhodecode_db_repo.repo_type != 'svn':
77 <li class="${'active' if c.active=='reviewers' else ''}">
77 <li class="${h.is_active('reviewers', c.active)}">
78 <a href="${h.route_path('repo_reviewers', repo_name=c.repo_name)}">${_('Reviewer Rules')}</a>
78 <a href="${h.route_path('repo_reviewers', repo_name=c.repo_name)}">${_('Reviewer Rules')}</a>
79 </li>
79 </li>
80 %endif
80 %endif
81 <li class="${'active' if c.active=='automation' else ''}">
81 <li class="${h.is_active('automation', c.active)}">
82 <a href="${h.route_path('repo_automation', repo_name=c.repo_name)}">${_('Automation')}</a>
82 <a href="${h.route_path('repo_automation', repo_name=c.repo_name)}">${_('Automation')}</a>
83 </li>
83 </li>
84 <li class="${'active' if c.active=='maintenance' else ''}">
84 <li class="${h.is_active('maintenance', c.active)}">
85 <a href="${h.route_path('edit_repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
85 <a href="${h.route_path('edit_repo_maintenance', repo_name=c.repo_name)}">${_('Maintenance')}</a>
86 </li>
86 </li>
87 <li class="${'active' if c.active=='strip' else ''}">
87 <li class="${h.is_active('strip', c.active)}">
88 <a href="${h.route_path('edit_repo_strip', repo_name=c.repo_name)}">${_('Strip')}</a>
88 <a href="${h.route_path('edit_repo_strip', repo_name=c.repo_name)}">${_('Strip')}</a>
89 </li>
89 </li>
90 <li class="${'active' if c.active=='audit' else ''}">
90 <li class="${h.is_active('audit', c.active)}">
91 <a href="${h.route_path('edit_repo_audit_logs', repo_name=c.repo_name)}">${_('Audit logs')}</a>
91 <a href="${h.route_path('edit_repo_audit_logs', repo_name=c.repo_name)}">${_('Audit logs')}</a>
92 </li>
92 </li>
93
93
94 </ul>
94 </ul>
95 </div>
95 </div>
96
96
97 <div class="main-content-full-width">
97 <div class="main-content-full-width">
98 ${self.main_content()}
98 ${self.main_content()}
99 </div>
99 </div>
100
100
101 </div>
101 </div>
102 </div>
102 </div>
103
103
104 </%def> No newline at end of file
104 </%def>
@@ -1,54 +1,54 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Settings administration')}
5 ${_('Settings administration')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Settings')}
14 ${_('Settings')}
15 </%def>
15 </%def>
16 ##
16 ##
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="menu_bar_subnav()">
21 <%def name="menu_bar_subnav()">
22 ${self.admin_menu(active='settings')}
22 ${self.admin_menu(active='settings')}
23 </%def>
23 </%def>
24
24
25 <%def name="side_bar_nav()">
25 <%def name="side_bar_nav()">
26 % for navitem in c.navlist:
26 % for navitem in c.navlist:
27 <li class="${'active' if c.active in navitem.active_list else ''}">
27 <li class="${h.is_active(navitem.active_list, c.active)}">
28 <a href="${navitem.url}">${navitem.name}</a>
28 <a href="${navitem.url}">${navitem.name}</a>
29 </li>
29 </li>
30 % endfor
30 % endfor
31 </%def>
31 </%def>
32
32
33 <%def name="main_content()">
33 <%def name="main_content()">
34 <%include file="/admin/settings/settings_${c.active}.mako"/>
34 <%include file="/admin/settings/settings_${c.active}.mako"/>
35 </%def>
35 </%def>
36
36
37 <%def name="main()">
37 <%def name="main()">
38 <div class="box">
38 <div class="box">
39
39
40 ##main
40 ##main
41 <div class='sidebar-col-wrapper'>
41 <div class='sidebar-col-wrapper'>
42 <div class="sidebar">
42 <div class="sidebar">
43 <ul class="nav nav-pills nav-stacked">
43 <ul class="nav nav-pills nav-stacked">
44 ${self.side_bar_nav()}
44 ${self.side_bar_nav()}
45 </ul>
45 </ul>
46 </div>
46 </div>
47
47
48 <div class="main-content-auto-width">
48 <div class="main-content-auto-width">
49 ${self.main_content()}
49 ${self.main_content()}
50 </div>
50 </div>
51 </div>
51 </div>
52 </div>
52 </div>
53
53
54 </%def> No newline at end of file
54 </%def>
@@ -1,47 +1,47 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s user group settings') % c.user_group.users_group_name}
5 ${_('{} user group settings').format(c.user_group.users_group_name)}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('User Groups'),h.route_path('user_groups'))}
14 ${h.link_to(_('User Groups'),h.route_path('user_groups'))}
15 &raquo;
15 &raquo;
16 ${c.user_group.users_group_name}
16 ${c.user_group.users_group_name}
17 </%def>
17 </%def>
18
18
19 <%def name="menu_bar_nav()">
19 <%def name="menu_bar_nav()">
20 ${self.menu_items(active='admin')}
20 ${self.menu_items(active='admin')}
21 </%def>
21 </%def>
22
22
23 <%def name="menu_bar_subnav()">
23 <%def name="menu_bar_subnav()">
24 ${self.admin_menu(active='user_groups')}
24 ${self.admin_menu(active='user_groups')}
25 </%def>
25 </%def>
26
26
27 <%def name="main()">
27 <%def name="main()">
28 <div class="box">
28 <div class="box">
29
29
30 ##main
30 ##main
31 <div class="sidebar-col-wrapper">
31 <div class="sidebar-col-wrapper">
32 <div class="sidebar">
32 <div class="sidebar">
33 <ul class="nav nav-pills nav-stacked">
33 <ul class="nav nav-pills nav-stacked">
34 <li class="${'active' if c.active=='settings' else ''}"><a href="${h.route_path('edit_user_group', user_group_id=c.user_group.users_group_id)}">${_('Settings')}</a></li>
34 <li class="${h.is_active('settings', c.active)}"><a href="${h.route_path('edit_user_group', user_group_id=c.user_group.users_group_id)}">${_('Settings')}</a></li>
35 <li class="${'active' if c.active=='perms' else ''}"><a href="${h.route_path('edit_user_group_perms', user_group_id=c.user_group.users_group_id)}">${_('Permissions')}</a></li>
35 <li class="${h.is_active('perms', c.active)}"><a href="${h.route_path('edit_user_group_perms', user_group_id=c.user_group.users_group_id)}">${_('Permissions')}</a></li>
36 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.route_path('edit_user_group_advanced', user_group_id=c.user_group.users_group_id)}">${_('Advanced')}</a></li>
36 <li class="${h.is_active('advanced', c.active)}"><a href="${h.route_path('edit_user_group_advanced', user_group_id=c.user_group.users_group_id)}">${_('Advanced')}</a></li>
37 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.route_path('edit_user_group_global_perms', user_group_id=c.user_group.users_group_id)}">${_('Global permissions')}</a></li>
37 <li class="${h.is_active('global_perms', c.active)}"><a href="${h.route_path('edit_user_group_global_perms', user_group_id=c.user_group.users_group_id)}">${_('Global permissions')}</a></li>
38 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.route_path('edit_user_group_perms_summary', user_group_id=c.user_group.users_group_id)}">${_('Permissions summary')}</a></li>
38 <li class="${h.is_active('perms_summary', c.active)}"><a href="${h.route_path('edit_user_group_perms_summary', user_group_id=c.user_group.users_group_id)}">${_('Permissions summary')}</a></li>
39 </ul>
39 </ul>
40 </div>
40 </div>
41
41
42 <div class="main-content-full-width">
42 <div class="main-content-full-width">
43 <%include file="/admin/user_groups/user_group_edit_${c.active}.mako"/>
43 <%include file="/admin/user_groups/user_group_edit_${c.active}.mako"/>
44 </div>
44 </div>
45 </div>
45 </div>
46 </div>
46 </div>
47 </%def>
47 </%def>
@@ -1,59 +1,59 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('%s user settings') % c.user.username}
5 ${_('{} user settings').format(c.user.username)}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${h.link_to(_('Users'),h.route_path('users'))}
14 ${h.link_to(_('Users'),h.route_path('users'))}
15 &raquo;
15 &raquo;
16 </%def>
16 </%def>
17
17
18 <%def name="menu_bar_nav()">
18 <%def name="menu_bar_nav()">
19 ${self.menu_items(active='admin')}
19 ${self.menu_items(active='admin')}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_subnav()">
22 <%def name="menu_bar_subnav()">
23 ${self.admin_menu(active='users')}
23 ${self.admin_menu(active='users')}
24 </%def>
24 </%def>
25
25
26
26
27 <%def name="main()">
27 <%def name="main()">
28 <div class="box user_settings">
28 <div class="box user_settings">
29 % if not c.user.active:
29 % if not c.user.active:
30 <div class="alert alert-warning text-center">
30 <div class="alert alert-warning text-center">
31 <strong>${_('This user is set as disabled')}</strong>
31 <strong>${_('This user is set as disabled')}</strong>
32 </div>
32 </div>
33 % endif
33 % endif
34
34
35 ##main
35 ##main
36 <div class="sidebar-col-wrapper">
36 <div class="sidebar-col-wrapper">
37 <div class="sidebar">
37 <div class="sidebar">
38 <ul class="nav nav-pills nav-stacked">
38 <ul class="nav nav-pills nav-stacked">
39 <li class="${'active' if c.active=='profile' else ''}"><a href="${h.route_path('user_edit', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
39 <li class="${h.is_active('profile', c.active)}"><a href="${h.route_path('user_edit', user_id=c.user.user_id)}">${_('User Profile')}</a></li>
40 <li class="${'active' if c.active=='auth_tokens' else ''}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
40 <li class="${h.is_active('auth_tokens', c.active)}"><a href="${h.route_path('edit_user_auth_tokens', user_id=c.user.user_id)}">${_('Auth tokens')}</a></li>
41 <li class="${'active' if c.active in ['ssh_keys','ssh_keys_generate'] else ''}"><a href="${h.route_path('edit_user_ssh_keys', user_id=c.user.user_id)}">${_('SSH Keys')}</a></li>
41 <li class="${h.is_active(['ssh_keys','ssh_keys_generate'], c.active)}"><a href="${h.route_path('edit_user_ssh_keys', user_id=c.user.user_id)}">${_('SSH Keys')}</a></li>
42 <li class="${'active' if c.active=='advanced' else ''}"><a href="${h.route_path('user_edit_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
42 <li class="${h.is_active('advanced', c.active)}"><a href="${h.route_path('user_edit_advanced', user_id=c.user.user_id)}">${_('Advanced')}</a></li>
43 <li class="${'active' if c.active=='global_perms' else ''}"><a href="${h.route_path('user_edit_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
43 <li class="${h.is_active('global_perms', c.active)}"><a href="${h.route_path('user_edit_global_perms', user_id=c.user.user_id)}">${_('Global permissions')}</a></li>
44 <li class="${'active' if c.active=='perms_summary' else ''}"><a href="${h.route_path('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
44 <li class="${h.is_active('perms_summary', c.active)}"><a href="${h.route_path('edit_user_perms_summary', user_id=c.user.user_id)}">${_('Permissions summary')}</a></li>
45 <li class="${'active' if c.active=='emails' else ''}"><a href="${h.route_path('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
45 <li class="${h.is_active('emails', c.active)}"><a href="${h.route_path('edit_user_emails', user_id=c.user.user_id)}">${_('Emails')}</a></li>
46 <li class="${'active' if c.active=='ips' else ''}"><a href="${h.route_path('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
46 <li class="${h.is_active('ips', c.active)}"><a href="${h.route_path('edit_user_ips', user_id=c.user.user_id)}">${_('Ip Whitelist')}</a></li>
47 <li class="${'active' if c.active=='groups' else ''}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
47 <li class="${h.is_active('groups', c.active)}"><a href="${h.route_path('edit_user_groups_management', user_id=c.user.user_id)}">${_('User Groups Management')}</a></li>
48 <li class="${'active' if c.active=='audit' else ''}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('Audit logs')}</a></li>
48 <li class="${h.is_active('audit', c.active)}"><a href="${h.route_path('edit_user_audit_logs', user_id=c.user.user_id)}">${_('Audit logs')}</a></li>
49 <li class="${'active' if c.active=='caches' else ''}"><a href="${h.route_path('edit_user_caches', user_id=c.user.user_id)}">${_('Caches')}</a></li>
49 <li class="${h.is_active('caches', c.active)}"><a href="${h.route_path('edit_user_caches', user_id=c.user.user_id)}">${_('Caches')}</a></li>
50 </ul>
50 </ul>
51 </div>
51 </div>
52
52
53 <div class="main-content-full-width">
53 <div class="main-content-full-width">
54 <%include file="/admin/users/user_edit_${c.active}.mako"/>
54 <%include file="/admin/users/user_edit_${c.active}.mako"/>
55 </div>
55 </div>
56 </div>
56 </div>
57 </div>
57 </div>
58
58
59 </%def>
59 </%def>
@@ -1,1141 +1,1123 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 ## base64 filter
4 <%!
3 <%!
4 ## base64 filter e.g ${ example | base64 }
5 def base64(text):
5 def base64(text):
6 import base64
6 import base64
7 from rhodecode.lib.helpers import safe_str
7 from rhodecode.lib.helpers import safe_str
8 return base64.encodestring(safe_str(text))
8 return base64.encodestring(safe_str(text))
9 %>
9 %>
10
10
11 <%inherit file="root.mako"/>
11 <%inherit file="root.mako"/>
12
12
13 <%include file="/ejs_templates/templates.html"/>
13 <%include file="/ejs_templates/templates.html"/>
14
14
15 <div class="outerwrapper">
15 <div class="outerwrapper">
16 <!-- HEADER -->
16 <!-- HEADER -->
17 <div class="header">
17 <div class="header">
18 <div id="header-inner" class="wrapper">
18 <div id="header-inner" class="wrapper">
19 <div id="logo">
19 <div id="logo">
20 <div class="logo-wrapper">
20 <div class="logo-wrapper">
21 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
21 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
22 </div>
22 </div>
23 % if c.rhodecode_name:
23 % if c.rhodecode_name:
24 <div class="branding">
24 <div class="branding">
25 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
25 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
26 </div>
26 </div>
27 % endif
27 % endif
28 </div>
28 </div>
29 <!-- MENU BAR NAV -->
29 <!-- MENU BAR NAV -->
30 ${self.menu_bar_nav()}
30 ${self.menu_bar_nav()}
31 <!-- END MENU BAR NAV -->
31 <!-- END MENU BAR NAV -->
32 </div>
32 </div>
33 </div>
33 </div>
34 ${self.menu_bar_subnav()}
34 ${self.menu_bar_subnav()}
35 <!-- END HEADER -->
35 <!-- END HEADER -->
36
36
37 <!-- CONTENT -->
37 <!-- CONTENT -->
38 <div id="content" class="wrapper">
38 <div id="content" class="wrapper">
39
39
40 <rhodecode-toast id="notifications"></rhodecode-toast>
40 <rhodecode-toast id="notifications"></rhodecode-toast>
41
41
42 <div class="main">
42 <div class="main">
43 ${next.main()}
43 ${next.main()}
44 </div>
44 </div>
45 </div>
45 </div>
46 <!-- END CONTENT -->
46 <!-- END CONTENT -->
47
47
48 </div>
48 </div>
49 <!-- FOOTER -->
49 <!-- FOOTER -->
50 <div id="footer">
50 <div id="footer">
51 <div id="footer-inner" class="title wrapper">
51 <div id="footer-inner" class="title wrapper">
52 <div>
52 <div>
53 <p class="footer-link-right">
53 <p class="footer-link-right">
54 % if c.visual.show_version:
54 % if c.visual.show_version:
55 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
55 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
56 % endif
56 % endif
57 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
57 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
58 % if c.visual.rhodecode_support_url:
58 % if c.visual.rhodecode_support_url:
59 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
59 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
60 % endif
60 % endif
61 </p>
61 </p>
62 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
62 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
63 <p class="server-instance" style="display:${sid}">
63 <p class="server-instance" style="display:${sid}">
64 ## display hidden instance ID if specially defined
64 ## display hidden instance ID if specially defined
65 % if c.rhodecode_instanceid:
65 % if c.rhodecode_instanceid:
66 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
66 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
67 % endif
67 % endif
68 </p>
68 </p>
69 </div>
69 </div>
70 </div>
70 </div>
71 </div>
71 </div>
72
72
73 <!-- END FOOTER -->
73 <!-- END FOOTER -->
74
74
75 ### MAKO DEFS ###
75 ### MAKO DEFS ###
76
76
77 <%def name="menu_bar_subnav()">
77 <%def name="menu_bar_subnav()">
78 </%def>
78 </%def>
79
79
80 <%def name="breadcrumbs(class_='breadcrumbs')">
80 <%def name="breadcrumbs(class_='breadcrumbs')">
81 <div class="${class_}">
81 <div class="${class_}">
82 ${self.breadcrumbs_links()}
82 ${self.breadcrumbs_links()}
83 </div>
83 </div>
84 </%def>
84 </%def>
85
85
86 <%def name="admin_menu(active=None)">
86 <%def name="admin_menu(active=None)">
87 <%
88 def is_active(selected):
89 if selected == active:
90 return "active"
91 %>
92
87
93 <div id="context-bar">
88 <div id="context-bar">
94 <div class="wrapper">
89 <div class="wrapper">
95 <div class="title">
90 <div class="title">
96 <div class="title-content">
91 <div class="title-content">
97 <div class="title-main">
92 <div class="title-main">
98 % if c.is_super_admin:
93 % if c.is_super_admin:
99 ${_('Super Admin Panel')}
94 ${_('Super Admin Panel')}
100 % else:
95 % else:
101 ${_('Delegated Admin Panel')}
96 ${_('Delegated Admin Panel')}
102 % endif
97 % endif
103 </div>
98 </div>
104 </div>
99 </div>
105 </div>
100 </div>
106
101
107 <ul id="context-pages" class="navigation horizontal-list">
102 <ul id="context-pages" class="navigation horizontal-list">
108
103
109 ## super admin case
104 ## super admin case
110 % if c.is_super_admin:
105 % if c.is_super_admin:
111 <li class="${is_active('audit_logs')}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
106 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
112 <li class="${is_active('repositories')}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
107 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
113 <li class="${is_active('repository_groups')}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
108 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
114 <li class="${is_active('users')}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
109 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
115 <li class="${is_active('user_groups')}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
110 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
116 <li class="${is_active('permissions')}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
111 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
117 <li class="${is_active('authentication')}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
112 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
118 <li class="${is_active('integrations')}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
113 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
119 <li class="${is_active('defaults')}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
114 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
120 <li class="${is_active('settings')}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
115 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
121
116
122 ## delegated admin
117 ## delegated admin
123 % elif c.is_delegated_admin:
118 % elif c.is_delegated_admin:
124 <%
119 <%
125 repositories=c.auth_user.repositories_admin or c.can_create_repo
120 repositories=c.auth_user.repositories_admin or c.can_create_repo
126 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
121 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
127 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
122 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
128 %>
123 %>
129
124
130 %if repositories:
125 %if repositories:
131 <li class="${is_active('repositories')} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
126 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
132 %endif
127 %endif
133 %if repository_groups:
128 %if repository_groups:
134 <li class="${is_active('repository_groups')} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
129 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
135 %endif
130 %endif
136 %if user_groups:
131 %if user_groups:
137 <li class="${is_active('user_groups')} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
132 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
138 %endif
133 %endif
139 % endif
134 % endif
140 </ul>
135 </ul>
141
136
142 </div>
137 </div>
143 <div class="clear"></div>
138 <div class="clear"></div>
144 </div>
139 </div>
145 </%def>
140 </%def>
146
141
147 <%def name="dt_info_panel(elements)">
142 <%def name="dt_info_panel(elements)">
148 <dl class="dl-horizontal">
143 <dl class="dl-horizontal">
149 %for dt, dd, title, show_items in elements:
144 %for dt, dd, title, show_items in elements:
150 <dt>${dt}:</dt>
145 <dt>${dt}:</dt>
151 <dd title="${h.tooltip(title)}">
146 <dd title="${h.tooltip(title)}">
152 %if callable(dd):
147 %if callable(dd):
153 ## allow lazy evaluation of elements
148 ## allow lazy evaluation of elements
154 ${dd()}
149 ${dd()}
155 %else:
150 %else:
156 ${dd}
151 ${dd}
157 %endif
152 %endif
158 %if show_items:
153 %if show_items:
159 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
154 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
160 %endif
155 %endif
161 </dd>
156 </dd>
162
157
163 %if show_items:
158 %if show_items:
164 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
159 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
165 %for item in show_items:
160 %for item in show_items:
166 <dt></dt>
161 <dt></dt>
167 <dd>${item}</dd>
162 <dd>${item}</dd>
168 %endfor
163 %endfor
169 </div>
164 </div>
170 %endif
165 %endif
171
166
172 %endfor
167 %endfor
173 </dl>
168 </dl>
174 </%def>
169 </%def>
175
170
176 <%def name="tr_info_entry(element)">
171 <%def name="tr_info_entry(element)">
177 <% key, val, title, show_items = element %>
172 <% key, val, title, show_items = element %>
178
173
179 <tr>
174 <tr>
180 <td style="vertical-align: top">${key}</td>
175 <td style="vertical-align: top">${key}</td>
181 <td title="${h.tooltip(title)}">
176 <td title="${h.tooltip(title)}">
182 %if callable(val):
177 %if callable(val):
183 ## allow lazy evaluation of elements
178 ## allow lazy evaluation of elements
184 ${val()}
179 ${val()}
185 %else:
180 %else:
186 ${val}
181 ${val}
187 %endif
182 %endif
188 %if show_items:
183 %if show_items:
189 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
184 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
190 % for item in show_items:
185 % for item in show_items:
191 <dt></dt>
186 <dt></dt>
192 <dd>${item}</dd>
187 <dd>${item}</dd>
193 % endfor
188 % endfor
194 </div>
189 </div>
195 %endif
190 %endif
196 </td>
191 </td>
197 <td style="vertical-align: top">
192 <td style="vertical-align: top">
198 %if show_items:
193 %if show_items:
199 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
194 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
200 %endif
195 %endif
201 </td>
196 </td>
202 </tr>
197 </tr>
203
198
204 </%def>
199 </%def>
205
200
206 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None)">
201 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None)">
207 <%
202 <%
208 if size > 16:
203 if size > 16:
209 gravatar_class = ['gravatar','gravatar-large']
204 gravatar_class = ['gravatar','gravatar-large']
210 else:
205 else:
211 gravatar_class = ['gravatar']
206 gravatar_class = ['gravatar']
212
207
213 data_hovercard_url = ''
208 data_hovercard_url = ''
214 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
209 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
215
210
216 if tooltip:
211 if tooltip:
217 gravatar_class += ['tooltip-hovercard']
212 gravatar_class += ['tooltip-hovercard']
218
213
219 if tooltip and user:
214 if tooltip and user:
220 if user.username == h.DEFAULT_USER:
215 if user.username == h.DEFAULT_USER:
221 gravatar_class.pop(-1)
216 gravatar_class.pop(-1)
222 else:
217 else:
223 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
218 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
224 gravatar_class = ' '.join(gravatar_class)
219 gravatar_class = ' '.join(gravatar_class)
225
220
226 %>
221 %>
227 <%doc>
222 <%doc>
228 TODO: johbo: For now we serve double size images to make it smooth
223 TODO: johbo: For now we serve double size images to make it smooth
229 for retina. This is how it worked until now. Should be replaced
224 for retina. This is how it worked until now. Should be replaced
230 with a better solution at some point.
225 with a better solution at some point.
231 </%doc>
226 </%doc>
232
227
233 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
228 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
234 </%def>
229 </%def>
235
230
236
231
237 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False)">
232 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False)">
238 <%
233 <%
239 email = h.email_or_none(contact)
234 email = h.email_or_none(contact)
240 rc_user = h.discover_user(contact)
235 rc_user = h.discover_user(contact)
241 %>
236 %>
242
237
243 <div class="rc-user">
238 <div class="rc-user">
244 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
239 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
245 <span class="${('user user-disabled' if show_disabled else 'user')}"> ${h.link_to_user(rc_user or contact)}</span>
240 <span class="${('user user-disabled' if show_disabled else 'user')}"> ${h.link_to_user(rc_user or contact)}</span>
246 </div>
241 </div>
247 </%def>
242 </%def>
248
243
249
244
250 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
245 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
251 <%
246 <%
252 if (size > 16):
247 if (size > 16):
253 gravatar_class = 'icon-user-group-alt'
248 gravatar_class = 'icon-user-group-alt'
254 else:
249 else:
255 gravatar_class = 'icon-user-group-alt'
250 gravatar_class = 'icon-user-group-alt'
256
251
257 if tooltip:
252 if tooltip:
258 gravatar_class += ' tooltip-hovercard'
253 gravatar_class += ' tooltip-hovercard'
259
254
260 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
255 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
261 %>
256 %>
262 <%doc>
257 <%doc>
263 TODO: johbo: For now we serve double size images to make it smooth
258 TODO: johbo: For now we serve double size images to make it smooth
264 for retina. This is how it worked until now. Should be replaced
259 for retina. This is how it worked until now. Should be replaced
265 with a better solution at some point.
260 with a better solution at some point.
266 </%doc>
261 </%doc>
267
262
268 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
263 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
269 </%def>
264 </%def>
270
265
271 <%def name="repo_page_title(repo_instance)">
266 <%def name="repo_page_title(repo_instance)">
272 <div class="title-content repo-title">
267 <div class="title-content repo-title">
273
268
274 <div class="title-main">
269 <div class="title-main">
275 ## SVN/HG/GIT icons
270 ## SVN/HG/GIT icons
276 %if h.is_hg(repo_instance):
271 %if h.is_hg(repo_instance):
277 <i class="icon-hg"></i>
272 <i class="icon-hg"></i>
278 %endif
273 %endif
279 %if h.is_git(repo_instance):
274 %if h.is_git(repo_instance):
280 <i class="icon-git"></i>
275 <i class="icon-git"></i>
281 %endif
276 %endif
282 %if h.is_svn(repo_instance):
277 %if h.is_svn(repo_instance):
283 <i class="icon-svn"></i>
278 <i class="icon-svn"></i>
284 %endif
279 %endif
285
280
286 ## public/private
281 ## public/private
287 %if repo_instance.private:
282 %if repo_instance.private:
288 <i class="icon-repo-private"></i>
283 <i class="icon-repo-private"></i>
289 %else:
284 %else:
290 <i class="icon-repo-public"></i>
285 <i class="icon-repo-public"></i>
291 %endif
286 %endif
292
287
293 ## repo name with group name
288 ## repo name with group name
294 ${h.breadcrumb_repo_link(repo_instance)}
289 ${h.breadcrumb_repo_link(repo_instance)}
295
290
296 ## Context Actions
291 ## Context Actions
297 <div class="pull-right">
292 <div class="pull-right">
298 %if c.rhodecode_user.username != h.DEFAULT_USER:
293 %if c.rhodecode_user.username != h.DEFAULT_USER:
299 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
294 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
300
295
301 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
296 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
302 % if c.repository_is_user_following:
297 % if c.repository_is_user_following:
303 <i class="icon-eye-off"></i>${_('Unwatch')}
298 <i class="icon-eye-off"></i>${_('Unwatch')}
304 % else:
299 % else:
305 <i class="icon-eye"></i>${_('Watch')}
300 <i class="icon-eye"></i>${_('Watch')}
306 % endif
301 % endif
307
302
308 </a>
303 </a>
309 %else:
304 %else:
310 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
305 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
311 %endif
306 %endif
312 </div>
307 </div>
313
308
314 </div>
309 </div>
315
310
316 ## FORKED
311 ## FORKED
317 %if repo_instance.fork:
312 %if repo_instance.fork:
318 <p class="discreet">
313 <p class="discreet">
319 <i class="icon-code-fork"></i> ${_('Fork of')}
314 <i class="icon-code-fork"></i> ${_('Fork of')}
320 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
315 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
321 </p>
316 </p>
322 %endif
317 %endif
323
318
324 ## IMPORTED FROM REMOTE
319 ## IMPORTED FROM REMOTE
325 %if repo_instance.clone_uri:
320 %if repo_instance.clone_uri:
326 <p class="discreet">
321 <p class="discreet">
327 <i class="icon-code-fork"></i> ${_('Clone from')}
322 <i class="icon-code-fork"></i> ${_('Clone from')}
328 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
323 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
329 </p>
324 </p>
330 %endif
325 %endif
331
326
332 ## LOCKING STATUS
327 ## LOCKING STATUS
333 %if repo_instance.locked[0]:
328 %if repo_instance.locked[0]:
334 <p class="locking_locked discreet">
329 <p class="locking_locked discreet">
335 <i class="icon-repo-lock"></i>
330 <i class="icon-repo-lock"></i>
336 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
331 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
337 </p>
332 </p>
338 %elif repo_instance.enable_locking:
333 %elif repo_instance.enable_locking:
339 <p class="locking_unlocked discreet">
334 <p class="locking_unlocked discreet">
340 <i class="icon-repo-unlock"></i>
335 <i class="icon-repo-unlock"></i>
341 ${_('Repository not locked. Pull repository to lock it.')}
336 ${_('Repository not locked. Pull repository to lock it.')}
342 </p>
337 </p>
343 %endif
338 %endif
344
339
345 </div>
340 </div>
346 </%def>
341 </%def>
347
342
348 <%def name="repo_menu(active=None)">
343 <%def name="repo_menu(active=None)">
349 <%
344 <%
350 def is_active(selected):
351 if selected == active:
352 return "active"
353 ## determine if we have "any" option available
345 ## determine if we have "any" option available
354 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
346 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
355 has_actions = can_lock
347 has_actions = can_lock
356
348
357 %>
349 %>
358 % if c.rhodecode_db_repo.archived:
350 % if c.rhodecode_db_repo.archived:
359 <div class="alert alert-warning text-center">
351 <div class="alert alert-warning text-center">
360 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
352 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
361 </div>
353 </div>
362 % endif
354 % endif
363
355
364 <!--- REPO CONTEXT BAR -->
356 <!--- REPO CONTEXT BAR -->
365 <div id="context-bar">
357 <div id="context-bar">
366 <div class="wrapper">
358 <div class="wrapper">
367
359
368 <div class="title">
360 <div class="title">
369 ${self.repo_page_title(c.rhodecode_db_repo)}
361 ${self.repo_page_title(c.rhodecode_db_repo)}
370 </div>
362 </div>
371
363
372 <ul id="context-pages" class="navigation horizontal-list">
364 <ul id="context-pages" class="navigation horizontal-list">
373 <li class="${is_active('summary')}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
365 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
374 <li class="${is_active('commits')}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
366 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
375 <li class="${is_active('files')}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
367 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.route_path('repo_files', repo_name=c.repo_name, commit_id=c.rhodecode_db_repo.landing_rev[1], f_path='')}"><div class="menulabel">${_('Files')}</div></a></li>
376 <li class="${is_active('compare')}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
368 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
377
369
378 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
370 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
379 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
371 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
380 <li class="${is_active('showpullrequest')}">
372 <li class="${h.is_active('showpullrequest', active)}">
381 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
373 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
382 <div class="menulabel">
374 <div class="menulabel">
383 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
375 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
384 </div>
376 </div>
385 </a>
377 </a>
386 </li>
378 </li>
387 %endif
379 %endif
388
380
389 <li class="${is_active('artifacts')}">
381 <li class="${h.is_active('artifacts', active)}">
390 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
382 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
391 <div class="menulabel">
383 <div class="menulabel">
392 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
384 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
393 </div>
385 </div>
394 </a>
386 </a>
395 </li>
387 </li>
396
388
397 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
389 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
398 <li class="${is_active('settings')}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
390 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
399 %endif
391 %endif
400
392
401 <li class="${is_active('options')}">
393 <li class="${h.is_active('options', active)}">
402 % if has_actions:
394 % if has_actions:
403 <a class="menulink dropdown">
395 <a class="menulink dropdown">
404 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
396 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
405 </a>
397 </a>
406 <ul class="submenu">
398 <ul class="submenu">
407 %if can_lock:
399 %if can_lock:
408 %if c.rhodecode_db_repo.locked[0]:
400 %if c.rhodecode_db_repo.locked[0]:
409 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
401 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
410 %else:
402 %else:
411 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
403 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
412 %endif
404 %endif
413 %endif
405 %endif
414 </ul>
406 </ul>
415 % else:
407 % else:
416 <a class="menulink disabled">
408 <a class="menulink disabled">
417 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
409 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
418 </a>
410 </a>
419 % endif
411 % endif
420 </li>
412 </li>
421
413
422 </ul>
414 </ul>
423 </div>
415 </div>
424 <div class="clear"></div>
416 <div class="clear"></div>
425 </div>
417 </div>
426
418
427 <!--- REPO END CONTEXT BAR -->
419 <!--- REPO END CONTEXT BAR -->
428
420
429 </%def>
421 </%def>
430
422
431 <%def name="repo_group_page_title(repo_group_instance)">
423 <%def name="repo_group_page_title(repo_group_instance)">
432 <div class="title-content">
424 <div class="title-content">
433 <div class="title-main">
425 <div class="title-main">
434 ## Repository Group icon
426 ## Repository Group icon
435 <i class="icon-repo-group"></i>
427 <i class="icon-repo-group"></i>
436
428
437 ## repo name with group name
429 ## repo name with group name
438 ${h.breadcrumb_repo_group_link(repo_group_instance)}
430 ${h.breadcrumb_repo_group_link(repo_group_instance)}
439 </div>
431 </div>
440
432
441 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
433 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
442 <div class="repo-group-desc discreet">
434 <div class="repo-group-desc discreet">
443 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
435 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
444 </div>
436 </div>
445
437
446 </div>
438 </div>
447 </%def>
439 </%def>
448
440
449
441
450 <%def name="repo_group_menu(active=None)">
442 <%def name="repo_group_menu(active=None)">
451 <%
443 <%
452 def is_active(selected):
453 if selected == active:
454 return "active"
455
456 gr_name = c.repo_group.group_name if c.repo_group else None
444 gr_name = c.repo_group.group_name if c.repo_group else None
457 # create repositories with write permission on group is set to true
445 # create repositories with write permission on group is set to true
458 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
446 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
459
447
460 %>
448 %>
461
449
462
450
463 <!--- REPO GROUP CONTEXT BAR -->
451 <!--- REPO GROUP CONTEXT BAR -->
464 <div id="context-bar">
452 <div id="context-bar">
465 <div class="wrapper">
453 <div class="wrapper">
466 <div class="title">
454 <div class="title">
467 ${self.repo_group_page_title(c.repo_group)}
455 ${self.repo_group_page_title(c.repo_group)}
468 </div>
456 </div>
469
457
470 <ul id="context-pages" class="navigation horizontal-list">
458 <ul id="context-pages" class="navigation horizontal-list">
471 <li class="${is_active('home')}">
459 <li class="${h.is_active('home', active)}">
472 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
460 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
473 </li>
461 </li>
474 % if c.is_super_admin or group_admin:
462 % if c.is_super_admin or group_admin:
475 <li class="${is_active('settings')}">
463 <li class="${h.is_active('settings', active)}">
476 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
464 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
477 </li>
465 </li>
478 % endif
466 % endif
479
467
480 </ul>
468 </ul>
481 </div>
469 </div>
482 <div class="clear"></div>
470 <div class="clear"></div>
483 </div>
471 </div>
484
472
485 <!--- REPO GROUP CONTEXT BAR -->
473 <!--- REPO GROUP CONTEXT BAR -->
486
474
487 </%def>
475 </%def>
488
476
489
477
490 <%def name="usermenu(active=False)">
478 <%def name="usermenu(active=False)">
491 <%
479 <%
492 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
480 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
493
481
494 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
482 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
495 # create repositories with write permission on group is set to true
483 # create repositories with write permission on group is set to true
496
484
497 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
485 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
498 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
486 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
499 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
487 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
500 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
488 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
501
489
502 can_create_repos = c.is_super_admin or c.can_create_repo
490 can_create_repos = c.is_super_admin or c.can_create_repo
503 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
491 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
504
492
505 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
493 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
506 can_create_repo_groups_in_group = c.is_super_admin or group_admin
494 can_create_repo_groups_in_group = c.is_super_admin or group_admin
507 %>
495 %>
508
496
509 % if not_anonymous:
497 % if not_anonymous:
510 <%
498 <%
511 default_target_group = dict()
499 default_target_group = dict()
512 if c.rhodecode_user.personal_repo_group:
500 if c.rhodecode_user.personal_repo_group:
513 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
501 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
514 %>
502 %>
515
503
516 ## create action
504 ## create action
517 <li>
505 <li>
518 <a href="#create-actions" onclick="return false;" class="menulink childs">
506 <a href="#create-actions" onclick="return false;" class="menulink childs">
519 <i class="icon-plus-circled"></i>
507 <i class="icon-plus-circled"></i>
520 </a>
508 </a>
521
509
522 <div class="action-menu submenu">
510 <div class="action-menu submenu">
523
511
524 <ol>
512 <ol>
525 ## scope of within a repository
513 ## scope of within a repository
526 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
514 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
527 <li class="submenu-title">${_('This Repository')}</li>
515 <li class="submenu-title">${_('This Repository')}</li>
528 <li>
516 <li>
529 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
517 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
530 </li>
518 </li>
531 % if can_fork:
519 % if can_fork:
532 <li>
520 <li>
533 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
521 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
534 </li>
522 </li>
535 % endif
523 % endif
536 % endif
524 % endif
537
525
538 ## scope of within repository groups
526 ## scope of within repository groups
539 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
527 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
540 <li class="submenu-title">${_('This Repository Group')}</li>
528 <li class="submenu-title">${_('This Repository Group')}</li>
541
529
542 % if can_create_repos_in_group:
530 % if can_create_repos_in_group:
543 <li>
531 <li>
544 <a href="${h.route_path('repo_new',_query=default_target_group)}">${_('New Repository')}</a>
532 <a href="${h.route_path('repo_new',_query=default_target_group)}">${_('New Repository')}</a>
545 </li>
533 </li>
546 % endif
534 % endif
547
535
548 % if can_create_repo_groups_in_group:
536 % if can_create_repo_groups_in_group:
549 <li>
537 <li>
550 <a href="${h.route_path('repo_group_new',_query=default_target_group)}">${_(u'New Repository Group')}</a>
538 <a href="${h.route_path('repo_group_new',_query=default_target_group)}">${_(u'New Repository Group')}</a>
551 </li>
539 </li>
552 % endif
540 % endif
553 % endif
541 % endif
554
542
555 ## personal group
543 ## personal group
556 % if c.rhodecode_user.personal_repo_group:
544 % if c.rhodecode_user.personal_repo_group:
557 <li class="submenu-title">Personal Group</li>
545 <li class="submenu-title">Personal Group</li>
558
546
559 <li>
547 <li>
560 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
548 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
561 </li>
549 </li>
562
550
563 <li>
551 <li>
564 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
552 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
565 </li>
553 </li>
566 % endif
554 % endif
567
555
568 ## Global actions
556 ## Global actions
569 <li class="submenu-title">RhodeCode</li>
557 <li class="submenu-title">RhodeCode</li>
570 % if can_create_repos:
558 % if can_create_repos:
571 <li>
559 <li>
572 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
560 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
573 </li>
561 </li>
574 % endif
562 % endif
575
563
576 % if can_create_repo_groups:
564 % if can_create_repo_groups:
577 <li>
565 <li>
578 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
566 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
579 </li>
567 </li>
580 % endif
568 % endif
581
569
582 <li>
570 <li>
583 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
571 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
584 </li>
572 </li>
585
573
586 </ol>
574 </ol>
587
575
588 </div>
576 </div>
589 </li>
577 </li>
590
578
591 ## notifications
579 ## notifications
592 <li>
580 <li>
593 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
581 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
594 ${c.unread_notifications}
582 ${c.unread_notifications}
595 </a>
583 </a>
596 </li>
584 </li>
597 % endif
585 % endif
598
586
599 ## USER MENU
587 ## USER MENU
600 <li id="quick_login_li" class="${'active' if active else ''}">
588 <li id="quick_login_li" class="${'active' if active else ''}">
601 % if c.rhodecode_user.username == h.DEFAULT_USER:
589 % if c.rhodecode_user.username == h.DEFAULT_USER:
602 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
590 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
603 ${gravatar(c.rhodecode_user.email, 20)}
591 ${gravatar(c.rhodecode_user.email, 20)}
604 <span class="user">
592 <span class="user">
605 <span>${_('Sign in')}</span>
593 <span>${_('Sign in')}</span>
606 </span>
594 </span>
607 </a>
595 </a>
608 % else:
596 % else:
609 ## logged in user
597 ## logged in user
610 <a id="quick_login_link" class="menulink childs">
598 <a id="quick_login_link" class="menulink childs">
611 ${gravatar(c.rhodecode_user.email, 20)}
599 ${gravatar(c.rhodecode_user.email, 20)}
612 <span class="user">
600 <span class="user">
613 <span class="menu_link_user">${c.rhodecode_user.username}</span>
601 <span class="menu_link_user">${c.rhodecode_user.username}</span>
614 <div class="show_more"></div>
602 <div class="show_more"></div>
615 </span>
603 </span>
616 </a>
604 </a>
617 ## subnav with menu for logged in user
605 ## subnav with menu for logged in user
618 <div class="user-menu submenu">
606 <div class="user-menu submenu">
619 <div id="quick_login">
607 <div id="quick_login">
620 %if c.rhodecode_user.username != h.DEFAULT_USER:
608 %if c.rhodecode_user.username != h.DEFAULT_USER:
621 <div class="">
609 <div class="">
622 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
610 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
623 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
611 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
624 <div class="email">${c.rhodecode_user.email}</div>
612 <div class="email">${c.rhodecode_user.email}</div>
625 </div>
613 </div>
626 <div class="">
614 <div class="">
627 <ol class="links">
615 <ol class="links">
628 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
616 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
629 % if c.rhodecode_user.personal_repo_group:
617 % if c.rhodecode_user.personal_repo_group:
630 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
618 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
631 % endif
619 % endif
632 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
620 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
633
621
634 % if c.debug_style:
622 % if c.debug_style:
635 <li>
623 <li>
636 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
624 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
637 <div class="menulabel">${_('[Style]')}</div>
625 <div class="menulabel">${_('[Style]')}</div>
638 </a>
626 </a>
639 </li>
627 </li>
640 % endif
628 % endif
641
629
642 ## bookmark-items
630 ## bookmark-items
643 <li class="bookmark-items">
631 <li class="bookmark-items">
644 ${_('Bookmarks')}
632 ${_('Bookmarks')}
645 <div class="pull-right">
633 <div class="pull-right">
646 <a href="${h.route_path('my_account_bookmarks')}">
634 <a href="${h.route_path('my_account_bookmarks')}">
647
635
648 <i class="icon-cog"></i>
636 <i class="icon-cog"></i>
649 </a>
637 </a>
650 </div>
638 </div>
651 </li>
639 </li>
652 % if not c.bookmark_items:
640 % if not c.bookmark_items:
653 <li>
641 <li>
654 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
642 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
655 </li>
643 </li>
656 % endif
644 % endif
657 % for item in c.bookmark_items:
645 % for item in c.bookmark_items:
658 <li>
646 <li>
659 % if item.repository:
647 % if item.repository:
660 <div>
648 <div>
661 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
649 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
662 <code>${item.position}</code>
650 <code>${item.position}</code>
663 % if item.repository.repo_type == 'hg':
651 % if item.repository.repo_type == 'hg':
664 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
652 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
665 % elif item.repository.repo_type == 'git':
653 % elif item.repository.repo_type == 'git':
666 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
654 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
667 % elif item.repository.repo_type == 'svn':
655 % elif item.repository.repo_type == 'svn':
668 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
656 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
669 % endif
657 % endif
670 ${(item.title or h.shorter(item.repository.repo_name, 30))}
658 ${(item.title or h.shorter(item.repository.repo_name, 30))}
671 </a>
659 </a>
672 </div>
660 </div>
673 % elif item.repository_group:
661 % elif item.repository_group:
674 <div>
662 <div>
675 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
663 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
676 <code>${item.position}</code>
664 <code>${item.position}</code>
677 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
665 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
678 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
666 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
679 </a>
667 </a>
680 </div>
668 </div>
681 % else:
669 % else:
682 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
670 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
683 <code>${item.position}</code>
671 <code>${item.position}</code>
684 ${item.title}
672 ${item.title}
685 </a>
673 </a>
686 % endif
674 % endif
687 </li>
675 </li>
688 % endfor
676 % endfor
689
677
690 <li class="logout">
678 <li class="logout">
691 ${h.secure_form(h.route_path('logout'), request=request)}
679 ${h.secure_form(h.route_path('logout'), request=request)}
692 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
680 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
693 ${h.end_form()}
681 ${h.end_form()}
694 </li>
682 </li>
695 </ol>
683 </ol>
696 </div>
684 </div>
697 %endif
685 %endif
698 </div>
686 </div>
699 </div>
687 </div>
700
688
701 % endif
689 % endif
702 </li>
690 </li>
703 </%def>
691 </%def>
704
692
705 <%def name="menu_items(active=None)">
693 <%def name="menu_items(active=None)">
706 <%
707 def is_active(selected):
708 if selected == active:
709 return "active"
710 return ""
711 %>
712
694
713 <ul id="quick" class="main_nav navigation horizontal-list">
695 <ul id="quick" class="main_nav navigation horizontal-list">
714 ## notice box for important system messages
696 ## notice box for important system messages
715 <li style="display: none">
697 <li style="display: none">
716 <a class="notice-box" href="#openNotice" onclick="return false">
698 <a class="notice-box" href="#openNotice" onclick="return false">
717 <div class="menulabel-notice" >
699 <div class="menulabel-notice" >
718 0
700 0
719 </div>
701 </div>
720 </a>
702 </a>
721 </li>
703 </li>
722
704
723 ## Main filter
705 ## Main filter
724 <li>
706 <li>
725 <div class="menulabel main_filter_box">
707 <div class="menulabel main_filter_box">
726 <div class="main_filter_input_box">
708 <div class="main_filter_input_box">
727 <ul class="searchItems">
709 <ul class="searchItems">
728
710
729 % if c.template_context['search_context']['repo_id']:
711 % if c.template_context['search_context']['repo_id']:
730 <li class="searchTag searchTagFilter searchTagHidable" >
712 <li class="searchTag searchTagFilter searchTagHidable" >
731 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
713 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
732 <span class="tag">
714 <span class="tag">
733 This repo
715 This repo
734 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
716 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
735 </span>
717 </span>
736 ##</a>
718 ##</a>
737 </li>
719 </li>
738 % elif c.template_context['search_context']['repo_group_id']:
720 % elif c.template_context['search_context']['repo_group_id']:
739 <li class="searchTag searchTagFilter searchTagHidable">
721 <li class="searchTag searchTagFilter searchTagHidable">
740 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
722 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
741 <span class="tag">
723 <span class="tag">
742 This group
724 This group
743 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
725 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
744 </span>
726 </span>
745 ##</a>
727 ##</a>
746 </li>
728 </li>
747 % endif
729 % endif
748
730
749 <li class="searchTagInput">
731 <li class="searchTagInput">
750 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
732 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
751 </li>
733 </li>
752 <li class="searchTag searchTagHelp">
734 <li class="searchTag searchTagHelp">
753 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
735 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
754 </li>
736 </li>
755 </ul>
737 </ul>
756 </div>
738 </div>
757 </div>
739 </div>
758
740
759 <div id="main_filter_help" style="display: none">
741 <div id="main_filter_help" style="display: none">
760 - Use '/' key to quickly access this field.
742 - Use '/' key to quickly access this field.
761
743
762 - Enter a name of repository, or repository group for quick search.
744 - Enter a name of repository, or repository group for quick search.
763
745
764 - Prefix query to allow special search:
746 - Prefix query to allow special search:
765
747
766 user:admin, to search for usernames, always global
748 user:admin, to search for usernames, always global
767
749
768 user_group:devops, to search for user groups, always global
750 user_group:devops, to search for user groups, always global
769
751
770 commit:efced4, to search for commits, scoped to repositories or groups
752 commit:efced4, to search for commits, scoped to repositories or groups
771
753
772 file:models.py, to search for file paths, scoped to repositories or groups
754 file:models.py, to search for file paths, scoped to repositories or groups
773
755
774 % if c.template_context['search_context']['repo_id']:
756 % if c.template_context['search_context']['repo_id']:
775 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
757 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
776 % elif c.template_context['search_context']['repo_group_id']:
758 % elif c.template_context['search_context']['repo_group_id']:
777 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
759 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
778 % else:
760 % else:
779 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
761 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
780 % endif
762 % endif
781 </div>
763 </div>
782 </li>
764 </li>
783
765
784 ## ROOT MENU
766 ## ROOT MENU
785 <li class="${is_active('home')}">
767 <li class="${h.is_active('home', active)}">
786 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
768 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
787 <div class="menulabel">${_('Home')}</div>
769 <div class="menulabel">${_('Home')}</div>
788 </a>
770 </a>
789 </li>
771 </li>
790
772
791 %if c.rhodecode_user.username != h.DEFAULT_USER:
773 %if c.rhodecode_user.username != h.DEFAULT_USER:
792 <li class="${is_active('journal')}">
774 <li class="${h.is_active('journal', active)}">
793 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
775 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
794 <div class="menulabel">${_('Journal')}</div>
776 <div class="menulabel">${_('Journal')}</div>
795 </a>
777 </a>
796 </li>
778 </li>
797 %else:
779 %else:
798 <li class="${is_active('journal')}">
780 <li class="${h.is_active('journal', active)}">
799 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
781 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
800 <div class="menulabel">${_('Public journal')}</div>
782 <div class="menulabel">${_('Public journal')}</div>
801 </a>
783 </a>
802 </li>
784 </li>
803 %endif
785 %endif
804
786
805 <li class="${is_active('gists')}">
787 <li class="${h.is_active('gists', active)}">
806 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
788 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
807 <div class="menulabel">${_('Gists')}</div>
789 <div class="menulabel">${_('Gists')}</div>
808 </a>
790 </a>
809 </li>
791 </li>
810
792
811 % if c.is_super_admin or c.is_delegated_admin:
793 % if c.is_super_admin or c.is_delegated_admin:
812 <li class="${is_active('admin')}">
794 <li class="${h.is_active('admin', active)}">
813 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
795 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
814 <div class="menulabel">${_('Admin')} </div>
796 <div class="menulabel">${_('Admin')} </div>
815 </a>
797 </a>
816 </li>
798 </li>
817 % endif
799 % endif
818
800
819 ## render extra user menu
801 ## render extra user menu
820 ${usermenu(active=(active=='my_account'))}
802 ${usermenu(active=(active=='my_account'))}
821
803
822 </ul>
804 </ul>
823
805
824 <script type="text/javascript">
806 <script type="text/javascript">
825 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
807 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
826
808
827 var formatRepoResult = function(result, container, query, escapeMarkup) {
809 var formatRepoResult = function(result, container, query, escapeMarkup) {
828 return function(data, escapeMarkup) {
810 return function(data, escapeMarkup) {
829 if (!data.repo_id){
811 if (!data.repo_id){
830 return data.text; // optgroup text Repositories
812 return data.text; // optgroup text Repositories
831 }
813 }
832
814
833 var tmpl = '';
815 var tmpl = '';
834 var repoType = data['repo_type'];
816 var repoType = data['repo_type'];
835 var repoName = data['text'];
817 var repoName = data['text'];
836
818
837 if(data && data.type == 'repo'){
819 if(data && data.type == 'repo'){
838 if(repoType === 'hg'){
820 if(repoType === 'hg'){
839 tmpl += '<i class="icon-hg"></i> ';
821 tmpl += '<i class="icon-hg"></i> ';
840 }
822 }
841 else if(repoType === 'git'){
823 else if(repoType === 'git'){
842 tmpl += '<i class="icon-git"></i> ';
824 tmpl += '<i class="icon-git"></i> ';
843 }
825 }
844 else if(repoType === 'svn'){
826 else if(repoType === 'svn'){
845 tmpl += '<i class="icon-svn"></i> ';
827 tmpl += '<i class="icon-svn"></i> ';
846 }
828 }
847 if(data['private']){
829 if(data['private']){
848 tmpl += '<i class="icon-lock" ></i> ';
830 tmpl += '<i class="icon-lock" ></i> ';
849 }
831 }
850 else if(visualShowPublicIcon){
832 else if(visualShowPublicIcon){
851 tmpl += '<i class="icon-unlock-alt"></i> ';
833 tmpl += '<i class="icon-unlock-alt"></i> ';
852 }
834 }
853 }
835 }
854 tmpl += escapeMarkup(repoName);
836 tmpl += escapeMarkup(repoName);
855 return tmpl;
837 return tmpl;
856
838
857 }(result, escapeMarkup);
839 }(result, escapeMarkup);
858 };
840 };
859
841
860 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
842 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
861 return function(data, escapeMarkup) {
843 return function(data, escapeMarkup) {
862 if (!data.repo_group_id){
844 if (!data.repo_group_id){
863 return data.text; // optgroup text Repositories
845 return data.text; // optgroup text Repositories
864 }
846 }
865
847
866 var tmpl = '';
848 var tmpl = '';
867 var repoGroupName = data['text'];
849 var repoGroupName = data['text'];
868
850
869 if(data){
851 if(data){
870
852
871 tmpl += '<i class="icon-repo-group"></i> ';
853 tmpl += '<i class="icon-repo-group"></i> ';
872
854
873 }
855 }
874 tmpl += escapeMarkup(repoGroupName);
856 tmpl += escapeMarkup(repoGroupName);
875 return tmpl;
857 return tmpl;
876
858
877 }(result, escapeMarkup);
859 }(result, escapeMarkup);
878 };
860 };
879
861
880 var escapeRegExChars = function (value) {
862 var escapeRegExChars = function (value) {
881 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
863 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
882 };
864 };
883
865
884 var getRepoIcon = function(repo_type) {
866 var getRepoIcon = function(repo_type) {
885 if (repo_type === 'hg') {
867 if (repo_type === 'hg') {
886 return '<i class="icon-hg"></i> ';
868 return '<i class="icon-hg"></i> ';
887 }
869 }
888 else if (repo_type === 'git') {
870 else if (repo_type === 'git') {
889 return '<i class="icon-git"></i> ';
871 return '<i class="icon-git"></i> ';
890 }
872 }
891 else if (repo_type === 'svn') {
873 else if (repo_type === 'svn') {
892 return '<i class="icon-svn"></i> ';
874 return '<i class="icon-svn"></i> ';
893 }
875 }
894 return ''
876 return ''
895 };
877 };
896
878
897 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
879 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
898
880
899 if (value.split(':').length === 2) {
881 if (value.split(':').length === 2) {
900 value = value.split(':')[1]
882 value = value.split(':')[1]
901 }
883 }
902
884
903 var searchType = data['type'];
885 var searchType = data['type'];
904 var searchSubType = data['subtype'];
886 var searchSubType = data['subtype'];
905 var valueDisplay = data['value_display'];
887 var valueDisplay = data['value_display'];
906
888
907 var pattern = '(' + escapeRegExChars(value) + ')';
889 var pattern = '(' + escapeRegExChars(value) + ')';
908
890
909 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
891 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
910
892
911 // highlight match
893 // highlight match
912 if (searchType != 'text') {
894 if (searchType != 'text') {
913 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
895 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
914 }
896 }
915
897
916 var icon = '';
898 var icon = '';
917
899
918 if (searchType === 'hint') {
900 if (searchType === 'hint') {
919 icon += '<i class="icon-repo-group"></i> ';
901 icon += '<i class="icon-repo-group"></i> ';
920 }
902 }
921 // full text search/hints
903 // full text search/hints
922 else if (searchType === 'search') {
904 else if (searchType === 'search') {
923 icon += '<i class="icon-more"></i> ';
905 icon += '<i class="icon-more"></i> ';
924 if (searchSubType !== undefined && searchSubType == 'repo') {
906 if (searchSubType !== undefined && searchSubType == 'repo') {
925 valueDisplay += '<div class="pull-right tag">repository</div>';
907 valueDisplay += '<div class="pull-right tag">repository</div>';
926 }
908 }
927 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
909 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
928 valueDisplay += '<div class="pull-right tag">repo group</div>';
910 valueDisplay += '<div class="pull-right tag">repo group</div>';
929 }
911 }
930 }
912 }
931 // repository
913 // repository
932 else if (searchType === 'repo') {
914 else if (searchType === 'repo') {
933
915
934 var repoIcon = getRepoIcon(data['repo_type']);
916 var repoIcon = getRepoIcon(data['repo_type']);
935 icon += repoIcon;
917 icon += repoIcon;
936
918
937 if (data['private']) {
919 if (data['private']) {
938 icon += '<i class="icon-lock" ></i> ';
920 icon += '<i class="icon-lock" ></i> ';
939 }
921 }
940 else if (visualShowPublicIcon) {
922 else if (visualShowPublicIcon) {
941 icon += '<i class="icon-unlock-alt"></i> ';
923 icon += '<i class="icon-unlock-alt"></i> ';
942 }
924 }
943 }
925 }
944 // repository groups
926 // repository groups
945 else if (searchType === 'repo_group') {
927 else if (searchType === 'repo_group') {
946 icon += '<i class="icon-repo-group"></i> ';
928 icon += '<i class="icon-repo-group"></i> ';
947 }
929 }
948 // user group
930 // user group
949 else if (searchType === 'user_group') {
931 else if (searchType === 'user_group') {
950 icon += '<i class="icon-group"></i> ';
932 icon += '<i class="icon-group"></i> ';
951 }
933 }
952 // user
934 // user
953 else if (searchType === 'user') {
935 else if (searchType === 'user') {
954 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
936 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
955 }
937 }
956 // commit
938 // commit
957 else if (searchType === 'commit') {
939 else if (searchType === 'commit') {
958 var repo_data = data['repo_data'];
940 var repo_data = data['repo_data'];
959 var repoIcon = getRepoIcon(repo_data['repository_type']);
941 var repoIcon = getRepoIcon(repo_data['repository_type']);
960 if (repoIcon) {
942 if (repoIcon) {
961 icon += repoIcon;
943 icon += repoIcon;
962 } else {
944 } else {
963 icon += '<i class="icon-tag"></i>';
945 icon += '<i class="icon-tag"></i>';
964 }
946 }
965 }
947 }
966 // file
948 // file
967 else if (searchType === 'file') {
949 else if (searchType === 'file') {
968 var repo_data = data['repo_data'];
950 var repo_data = data['repo_data'];
969 var repoIcon = getRepoIcon(repo_data['repository_type']);
951 var repoIcon = getRepoIcon(repo_data['repository_type']);
970 if (repoIcon) {
952 if (repoIcon) {
971 icon += repoIcon;
953 icon += repoIcon;
972 } else {
954 } else {
973 icon += '<i class="icon-tag"></i>';
955 icon += '<i class="icon-tag"></i>';
974 }
956 }
975 }
957 }
976 // generic text
958 // generic text
977 else if (searchType === 'text') {
959 else if (searchType === 'text') {
978 icon = '';
960 icon = '';
979 }
961 }
980
962
981 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
963 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
982 return tmpl.format(icon, valueDisplay);
964 return tmpl.format(icon, valueDisplay);
983 };
965 };
984
966
985 var handleSelect = function(element, suggestion) {
967 var handleSelect = function(element, suggestion) {
986 if (suggestion.type === "hint") {
968 if (suggestion.type === "hint") {
987 // we skip action
969 // we skip action
988 $('#main_filter').focus();
970 $('#main_filter').focus();
989 }
971 }
990 else if (suggestion.type === "text") {
972 else if (suggestion.type === "text") {
991 // we skip action
973 // we skip action
992 $('#main_filter').focus();
974 $('#main_filter').focus();
993
975
994 } else {
976 } else {
995 window.location = suggestion['url'];
977 window.location = suggestion['url'];
996 }
978 }
997 };
979 };
998
980
999 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
981 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
1000 if (queryLowerCase.split(':').length === 2) {
982 if (queryLowerCase.split(':').length === 2) {
1001 queryLowerCase = queryLowerCase.split(':')[1]
983 queryLowerCase = queryLowerCase.split(':')[1]
1002 }
984 }
1003 if (suggestion.type === "text") {
985 if (suggestion.type === "text") {
1004 // special case we don't want to "skip" display for
986 // special case we don't want to "skip" display for
1005 return true
987 return true
1006 }
988 }
1007 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
989 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1008 };
990 };
1009
991
1010 var cleanContext = {
992 var cleanContext = {
1011 repo_view_type: null,
993 repo_view_type: null,
1012
994
1013 repo_id: null,
995 repo_id: null,
1014 repo_name: "",
996 repo_name: "",
1015
997
1016 repo_group_id: null,
998 repo_group_id: null,
1017 repo_group_name: null
999 repo_group_name: null
1018 };
1000 };
1019 var removeGoToFilter = function () {
1001 var removeGoToFilter = function () {
1020 $('.searchTagHidable').hide();
1002 $('.searchTagHidable').hide();
1021 $('#main_filter').autocomplete(
1003 $('#main_filter').autocomplete(
1022 'setOptions', {params:{search_context: cleanContext}});
1004 'setOptions', {params:{search_context: cleanContext}});
1023 };
1005 };
1024
1006
1025 $('#main_filter').autocomplete({
1007 $('#main_filter').autocomplete({
1026 serviceUrl: pyroutes.url('goto_switcher_data'),
1008 serviceUrl: pyroutes.url('goto_switcher_data'),
1027 params: {
1009 params: {
1028 "search_context": templateContext.search_context
1010 "search_context": templateContext.search_context
1029 },
1011 },
1030 minChars:2,
1012 minChars:2,
1031 maxHeight:400,
1013 maxHeight:400,
1032 deferRequestBy: 300, //miliseconds
1014 deferRequestBy: 300, //miliseconds
1033 tabDisabled: true,
1015 tabDisabled: true,
1034 autoSelectFirst: false,
1016 autoSelectFirst: false,
1035 containerClass: 'autocomplete-qfilter-suggestions',
1017 containerClass: 'autocomplete-qfilter-suggestions',
1036 formatResult: autocompleteMainFilterFormatResult,
1018 formatResult: autocompleteMainFilterFormatResult,
1037 lookupFilter: autocompleteMainFilterResult,
1019 lookupFilter: autocompleteMainFilterResult,
1038 onSelect: function (element, suggestion) {
1020 onSelect: function (element, suggestion) {
1039 handleSelect(element, suggestion);
1021 handleSelect(element, suggestion);
1040 return false;
1022 return false;
1041 },
1023 },
1042 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1024 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1043 if (jqXHR !== 'abort') {
1025 if (jqXHR !== 'abort') {
1044 alert("Error during search.\nError code: {0}".format(textStatus));
1026 alert("Error during search.\nError code: {0}".format(textStatus));
1045 window.location = '';
1027 window.location = '';
1046 }
1028 }
1047 }
1029 }
1048 });
1030 });
1049
1031
1050 showMainFilterBox = function () {
1032 showMainFilterBox = function () {
1051 $('#main_filter_help').toggle();
1033 $('#main_filter_help').toggle();
1052 };
1034 };
1053
1035
1054 $('#main_filter').on('keydown.autocomplete', function (e) {
1036 $('#main_filter').on('keydown.autocomplete', function (e) {
1055
1037
1056 var BACKSPACE = 8;
1038 var BACKSPACE = 8;
1057 var el = $(e.currentTarget);
1039 var el = $(e.currentTarget);
1058 if(e.which === BACKSPACE){
1040 if(e.which === BACKSPACE){
1059 var inputVal = el.val();
1041 var inputVal = el.val();
1060 if (inputVal === ""){
1042 if (inputVal === ""){
1061 removeGoToFilter()
1043 removeGoToFilter()
1062 }
1044 }
1063 }
1045 }
1064 });
1046 });
1065
1047
1066 </script>
1048 </script>
1067 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1049 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1068 </%def>
1050 </%def>
1069
1051
1070 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1052 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1071 <div class="modal-dialog">
1053 <div class="modal-dialog">
1072 <div class="modal-content">
1054 <div class="modal-content">
1073 <div class="modal-header">
1055 <div class="modal-header">
1074 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1056 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1075 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1057 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1076 </div>
1058 </div>
1077 <div class="modal-body">
1059 <div class="modal-body">
1078 <div class="block-left">
1060 <div class="block-left">
1079 <table class="keyboard-mappings">
1061 <table class="keyboard-mappings">
1080 <tbody>
1062 <tbody>
1081 <tr>
1063 <tr>
1082 <th></th>
1064 <th></th>
1083 <th>${_('Site-wide shortcuts')}</th>
1065 <th>${_('Site-wide shortcuts')}</th>
1084 </tr>
1066 </tr>
1085 <%
1067 <%
1086 elems = [
1068 elems = [
1087 ('/', 'Use quick search box'),
1069 ('/', 'Use quick search box'),
1088 ('g h', 'Goto home page'),
1070 ('g h', 'Goto home page'),
1089 ('g g', 'Goto my private gists page'),
1071 ('g g', 'Goto my private gists page'),
1090 ('g G', 'Goto my public gists page'),
1072 ('g G', 'Goto my public gists page'),
1091 ('g 0-9', 'Goto bookmarked items from 0-9'),
1073 ('g 0-9', 'Goto bookmarked items from 0-9'),
1092 ('n r', 'New repository page'),
1074 ('n r', 'New repository page'),
1093 ('n g', 'New gist page'),
1075 ('n g', 'New gist page'),
1094 ]
1076 ]
1095 %>
1077 %>
1096 %for key, desc in elems:
1078 %for key, desc in elems:
1097 <tr>
1079 <tr>
1098 <td class="keys">
1080 <td class="keys">
1099 <span class="key tag">${key}</span>
1081 <span class="key tag">${key}</span>
1100 </td>
1082 </td>
1101 <td>${desc}</td>
1083 <td>${desc}</td>
1102 </tr>
1084 </tr>
1103 %endfor
1085 %endfor
1104 </tbody>
1086 </tbody>
1105 </table>
1087 </table>
1106 </div>
1088 </div>
1107 <div class="block-left">
1089 <div class="block-left">
1108 <table class="keyboard-mappings">
1090 <table class="keyboard-mappings">
1109 <tbody>
1091 <tbody>
1110 <tr>
1092 <tr>
1111 <th></th>
1093 <th></th>
1112 <th>${_('Repositories')}</th>
1094 <th>${_('Repositories')}</th>
1113 </tr>
1095 </tr>
1114 <%
1096 <%
1115 elems = [
1097 elems = [
1116 ('g s', 'Goto summary page'),
1098 ('g s', 'Goto summary page'),
1117 ('g c', 'Goto changelog page'),
1099 ('g c', 'Goto changelog page'),
1118 ('g f', 'Goto files page'),
1100 ('g f', 'Goto files page'),
1119 ('g F', 'Goto files page with file search activated'),
1101 ('g F', 'Goto files page with file search activated'),
1120 ('g p', 'Goto pull requests page'),
1102 ('g p', 'Goto pull requests page'),
1121 ('g o', 'Goto repository settings'),
1103 ('g o', 'Goto repository settings'),
1122 ('g O', 'Goto repository access permissions settings'),
1104 ('g O', 'Goto repository access permissions settings'),
1123 ]
1105 ]
1124 %>
1106 %>
1125 %for key, desc in elems:
1107 %for key, desc in elems:
1126 <tr>
1108 <tr>
1127 <td class="keys">
1109 <td class="keys">
1128 <span class="key tag">${key}</span>
1110 <span class="key tag">${key}</span>
1129 </td>
1111 </td>
1130 <td>${desc}</td>
1112 <td>${desc}</td>
1131 </tr>
1113 </tr>
1132 %endfor
1114 %endfor
1133 </tbody>
1115 </tbody>
1134 </table>
1116 </table>
1135 </div>
1117 </div>
1136 </div>
1118 </div>
1137 <div class="modal-footer">
1119 <div class="modal-footer">
1138 </div>
1120 </div>
1139 </div><!-- /.modal-content -->
1121 </div><!-- /.modal-content -->
1140 </div><!-- /.modal-dialog -->
1122 </div><!-- /.modal-dialog -->
1141 </div><!-- /.modal -->
1123 </div><!-- /.modal -->
@@ -1,143 +1,143 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${_('%s Pull Requests') % c.repo_name}
4 ${_('{} Pull Requests').format(c.repo_name)}
5 %if c.rhodecode_name:
5 %if c.rhodecode_name:
6 &middot; ${h.branding(c.rhodecode_name)}
6 &middot; ${h.branding(c.rhodecode_name)}
7 %endif
7 %endif
8 </%def>
8 </%def>
9
9
10 <%def name="breadcrumbs_links()"></%def>
10 <%def name="breadcrumbs_links()"></%def>
11
11
12 <%def name="menu_bar_nav()">
12 <%def name="menu_bar_nav()">
13 ${self.menu_items(active='repositories')}
13 ${self.menu_items(active='repositories')}
14 </%def>
14 </%def>
15
15
16
16
17 <%def name="menu_bar_subnav()">
17 <%def name="menu_bar_subnav()">
18 ${self.repo_menu(active='showpullrequest')}
18 ${self.repo_menu(active='showpullrequest')}
19 </%def>
19 </%def>
20
20
21
21
22 <%def name="main()">
22 <%def name="main()">
23
23
24 <div class="box">
24 <div class="box">
25 <div class="title">
25 <div class="title">
26 <ul class="button-links">
26 <ul class="button-links">
27 <li class="btn ${('active' if c.active=='open' else '')}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0})}">${_('Opened')}</a></li>
27 <li class="btn ${h.is_active('open', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0})}">${_('Opened')}</a></li>
28 <li class="btn ${('active' if c.active=='my' else '')}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'my':1})}">${_('Opened by me')}</a></li>
28 <li class="btn ${h.is_active('my', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'my':1})}">${_('Opened by me')}</a></li>
29 <li class="btn ${('active' if c.active=='awaiting' else '')}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_review':1})}">${_('Awaiting review')}</a></li>
29 <li class="btn ${h.is_active('awaiting', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_review':1})}">${_('Awaiting review')}</a></li>
30 <li class="btn ${('active' if c.active=='awaiting_my' else '')}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_my_review':1})}">${_('Awaiting my review')}</a></li>
30 <li class="btn ${h.is_active('awaiting_my', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'awaiting_my_review':1})}">${_('Awaiting my review')}</a></li>
31 <li class="btn ${('active' if c.active=='closed' else '')}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'closed':1})}">${_('Closed')}</a></li>
31 <li class="btn ${h.is_active('closed', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':0,'closed':1})}">${_('Closed')}</a></li>
32 <li class="btn ${('active' if c.active=='source' else '')}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':1})}">${_('From this repo')}</a></li>
32 <li class="btn ${h.is_active('source', c.active)}"><a href="${h.route_path('pullrequest_show_all',repo_name=c.repo_name, _query={'source':1})}">${_('From this repo')}</a></li>
33 </ul>
33 </ul>
34
34
35 <ul class="links">
35 <ul class="links">
36 % if c.rhodecode_user.username != h.DEFAULT_USER:
36 % if c.rhodecode_user.username != h.DEFAULT_USER:
37 <li>
37 <li>
38 <span>
38 <span>
39 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">
39 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">
40 ${_('Open new Pull Request')}
40 ${_('Open new Pull Request')}
41 </a>
41 </a>
42 </span>
42 </span>
43 </li>
43 </li>
44 % endif
44 % endif
45
45
46 <li>
46 <li>
47 <div class="grid-quick-filter">
47 <div class="grid-quick-filter">
48 <ul class="grid-filter-box">
48 <ul class="grid-filter-box">
49 <li class="grid-filter-box-icon">
49 <li class="grid-filter-box-icon">
50 <i class="icon-search"></i>
50 <i class="icon-search"></i>
51 </li>
51 </li>
52 <li class="grid-filter-box-input">
52 <li class="grid-filter-box-input">
53 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
53 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
54 </li>
54 </li>
55 </ul>
55 </ul>
56 </div>
56 </div>
57 </li>
57 </li>
58
58
59 </ul>
59 </ul>
60
60
61 </div>
61 </div>
62
62
63 <div class="main-content-full-width">
63 <div class="main-content-full-width">
64 <table id="pull_request_list_table" class="display"></table>
64 <table id="pull_request_list_table" class="display"></table>
65 </div>
65 </div>
66
66
67 </div>
67 </div>
68
68
69 <script type="text/javascript">
69 <script type="text/javascript">
70 $(document).ready(function() {
70 $(document).ready(function() {
71 var $pullRequestListTable = $('#pull_request_list_table');
71 var $pullRequestListTable = $('#pull_request_list_table');
72
72
73 // object list
73 // object list
74 $pullRequestListTable.DataTable({
74 $pullRequestListTable.DataTable({
75 processing: true,
75 processing: true,
76 serverSide: true,
76 serverSide: true,
77 ajax: {
77 ajax: {
78 "url": "${h.route_path('pullrequest_show_all_data', repo_name=c.repo_name)}",
78 "url": "${h.route_path('pullrequest_show_all_data', repo_name=c.repo_name)}",
79 "data": function (d) {
79 "data": function (d) {
80 d.source = "${c.source}";
80 d.source = "${c.source}";
81 d.closed = "${c.closed}";
81 d.closed = "${c.closed}";
82 d.my = "${c.my}";
82 d.my = "${c.my}";
83 d.awaiting_review = "${c.awaiting_review}";
83 d.awaiting_review = "${c.awaiting_review}";
84 d.awaiting_my_review = "${c.awaiting_my_review}";
84 d.awaiting_my_review = "${c.awaiting_my_review}";
85 }
85 }
86 },
86 },
87 dom: 'rtp',
87 dom: 'rtp',
88 pageLength: ${c.visual.dashboard_items},
88 pageLength: ${c.visual.dashboard_items},
89 order: [[ 1, "desc" ]],
89 order: [[ 1, "desc" ]],
90 columns: [
90 columns: [
91 { data: {"_": "status",
91 { data: {"_": "status",
92 "sort": "status"}, title: "", className: "td-status", orderable: false},
92 "sort": "status"}, title: "", className: "td-status", orderable: false},
93 { data: {"_": "name",
93 { data: {"_": "name",
94 "sort": "name_raw"}, title: "${_('Id')}", className: "td-componentname", "type": "num" },
94 "sort": "name_raw"}, title: "${_('Id')}", className: "td-componentname", "type": "num" },
95 { data: {"_": "title",
95 { data: {"_": "title",
96 "sort": "title"}, title: "${_('Title')}", className: "td-description" },
96 "sort": "title"}, title: "${_('Title')}", className: "td-description" },
97 { data: {"_": "author",
97 { data: {"_": "author",
98 "sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
98 "sort": "author_raw"}, title: "${_('Author')}", className: "td-user", orderable: false },
99 { data: {"_": "comments",
99 { data: {"_": "comments",
100 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
100 "sort": "comments_raw"}, title: "", className: "td-comments", orderable: false},
101 { data: {"_": "updated_on",
101 { data: {"_": "updated_on",
102 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
102 "sort": "updated_on_raw"}, title: "${_('Last Update')}", className: "td-time" }
103 ],
103 ],
104 language: {
104 language: {
105 paginate: DEFAULT_GRID_PAGINATION,
105 paginate: DEFAULT_GRID_PAGINATION,
106 sProcessing: _gettext('loading...'),
106 sProcessing: _gettext('loading...'),
107 emptyTable: _gettext("No pull requests available yet.")
107 emptyTable: _gettext("No pull requests available yet.")
108 },
108 },
109 "drawCallback": function( settings, json ) {
109 "drawCallback": function( settings, json ) {
110 timeagoActivate();
110 timeagoActivate();
111 tooltipActivate();
111 tooltipActivate();
112 },
112 },
113 "createdRow": function ( row, data, index ) {
113 "createdRow": function ( row, data, index ) {
114 if (data['closed']) {
114 if (data['closed']) {
115 $(row).addClass('closed');
115 $(row).addClass('closed');
116 }
116 }
117 if (data['state'] !== 'created') {
117 if (data['state'] !== 'created') {
118 $(row).addClass('state-' + data['state']);
118 $(row).addClass('state-' + data['state']);
119 }
119 }
120 }
120 }
121 });
121 });
122
122
123 $pullRequestListTable.on('xhr.dt', function(e, settings, json, xhr){
123 $pullRequestListTable.on('xhr.dt', function(e, settings, json, xhr){
124 $pullRequestListTable.css('opacity', 1);
124 $pullRequestListTable.css('opacity', 1);
125 });
125 });
126
126
127 $pullRequestListTable.on('preXhr.dt', function(e, settings, data){
127 $pullRequestListTable.on('preXhr.dt', function(e, settings, data){
128 $pullRequestListTable.css('opacity', 0.3);
128 $pullRequestListTable.css('opacity', 0.3);
129 });
129 });
130
130
131 // filter
131 // filter
132 $('#q_filter').on('keyup',
132 $('#q_filter').on('keyup',
133 $.debounce(250, function() {
133 $.debounce(250, function() {
134 $pullRequestListTable.DataTable().search(
134 $pullRequestListTable.DataTable().search(
135 $('#q_filter').val()
135 $('#q_filter').val()
136 ).draw();
136 ).draw();
137 })
137 })
138 );
138 );
139
139
140 });
140 });
141
141
142 </script>
142 </script>
143 </%def>
143 </%def>
General Comments 0
You need to be logged in to leave comments. Login now