##// END OF EJS Templates
ux: #4037 use gravatar for commit email and add tooltips for name + email
lisaq -
r410:e0937131 default
parent child Browse files
Show More
@@ -1,1886 +1,1900 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 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 random
28 import random
29 import hashlib
29 import hashlib
30 import StringIO
30 import StringIO
31 import urllib
31 import urllib
32 import math
32 import math
33 import logging
33 import logging
34 import re
34 import re
35 import urlparse
35 import urlparse
36 import time
36 import time
37 import string
37 import string
38 import hashlib
38 import hashlib
39 import pygments
39 import pygments
40
40
41 from datetime import datetime
41 from datetime import datetime
42 from functools import partial
42 from functools import partial
43 from pygments.formatters.html import HtmlFormatter
43 from pygments.formatters.html import HtmlFormatter
44 from pygments import highlight as code_highlight
44 from pygments import highlight as code_highlight
45 from pygments.lexers import (
45 from pygments.lexers import (
46 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
46 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
47 from pylons import url
47 from pylons import url
48 from pylons.i18n.translation import _, ungettext
48 from pylons.i18n.translation import _, ungettext
49 from pyramid.threadlocal import get_current_request
49 from pyramid.threadlocal import get_current_request
50
50
51 from webhelpers.html import literal, HTML, escape
51 from webhelpers.html import literal, HTML, escape
52 from webhelpers.html.tools import *
52 from webhelpers.html.tools import *
53 from webhelpers.html.builder import make_tag
53 from webhelpers.html.builder import make_tag
54 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
54 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
55 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
55 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
56 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
56 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
57 submit, text, password, textarea, title, ul, xml_declaration, radio
57 submit, text, password, textarea, title, ul, xml_declaration, radio
58 from webhelpers.html.tools import auto_link, button_to, highlight, \
58 from webhelpers.html.tools import auto_link, button_to, highlight, \
59 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
59 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
60 from webhelpers.pylonslib import Flash as _Flash
60 from webhelpers.pylonslib import Flash as _Flash
61 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
61 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
62 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
62 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
63 replace_whitespace, urlify, truncate, wrap_paragraphs
63 replace_whitespace, urlify, truncate, wrap_paragraphs
64 from webhelpers.date import time_ago_in_words
64 from webhelpers.date import time_ago_in_words
65 from webhelpers.paginate import Page as _Page
65 from webhelpers.paginate import Page as _Page
66 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
66 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
67 convert_boolean_attrs, NotGiven, _make_safe_id_component
67 convert_boolean_attrs, NotGiven, _make_safe_id_component
68 from webhelpers2.number import format_byte_size
68 from webhelpers2.number import format_byte_size
69
69
70 from rhodecode.lib.annotate import annotate_highlight
70 from rhodecode.lib.annotate import annotate_highlight
71 from rhodecode.lib.action_parser import action_parser
71 from rhodecode.lib.action_parser import action_parser
72 from rhodecode.lib.ext_json import json
72 from rhodecode.lib.ext_json import json
73 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
73 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
74 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
74 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
75 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
75 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
76 AttributeDict, safe_int, md5, md5_safe
76 AttributeDict, safe_int, md5, md5_safe
77 from rhodecode.lib.markup_renderer import MarkupRenderer
77 from rhodecode.lib.markup_renderer import MarkupRenderer
78 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
78 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
79 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
79 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
80 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
80 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
81 from rhodecode.model.changeset_status import ChangesetStatusModel
81 from rhodecode.model.changeset_status import ChangesetStatusModel
82 from rhodecode.model.db import Permission, User, Repository
82 from rhodecode.model.db import Permission, User, Repository
83 from rhodecode.model.repo_group import RepoGroupModel
83 from rhodecode.model.repo_group import RepoGroupModel
84 from rhodecode.model.settings import IssueTrackerSettingsModel
84 from rhodecode.model.settings import IssueTrackerSettingsModel
85
85
86 log = logging.getLogger(__name__)
86 log = logging.getLogger(__name__)
87
87
88 DEFAULT_USER = User.DEFAULT_USER
88 DEFAULT_USER = User.DEFAULT_USER
89 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
89 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
90
90
91
91
92 def html_escape(text, html_escape_table=None):
92 def html_escape(text, html_escape_table=None):
93 """Produce entities within text."""
93 """Produce entities within text."""
94 if not html_escape_table:
94 if not html_escape_table:
95 html_escape_table = {
95 html_escape_table = {
96 "&": "&amp;",
96 "&": "&amp;",
97 '"': "&quot;",
97 '"': "&quot;",
98 "'": "&apos;",
98 "'": "&apos;",
99 ">": "&gt;",
99 ">": "&gt;",
100 "<": "&lt;",
100 "<": "&lt;",
101 }
101 }
102 return "".join(html_escape_table.get(c, c) for c in text)
102 return "".join(html_escape_table.get(c, c) for c in text)
103
103
104
104
105 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
105 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
106 """
106 """
107 Truncate string ``s`` at the first occurrence of ``sub``.
107 Truncate string ``s`` at the first occurrence of ``sub``.
108
108
109 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
109 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
110 """
110 """
111 suffix_if_chopped = suffix_if_chopped or ''
111 suffix_if_chopped = suffix_if_chopped or ''
112 pos = s.find(sub)
112 pos = s.find(sub)
113 if pos == -1:
113 if pos == -1:
114 return s
114 return s
115
115
116 if inclusive:
116 if inclusive:
117 pos += len(sub)
117 pos += len(sub)
118
118
119 chopped = s[:pos]
119 chopped = s[:pos]
120 left = s[pos:].strip()
120 left = s[pos:].strip()
121
121
122 if left and suffix_if_chopped:
122 if left and suffix_if_chopped:
123 chopped += suffix_if_chopped
123 chopped += suffix_if_chopped
124
124
125 return chopped
125 return chopped
126
126
127
127
128 def shorter(text, size=20):
128 def shorter(text, size=20):
129 postfix = '...'
129 postfix = '...'
130 if len(text) > size:
130 if len(text) > size:
131 return text[:size - len(postfix)] + postfix
131 return text[:size - len(postfix)] + postfix
132 return text
132 return text
133
133
134
134
135 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
135 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
136 """
136 """
137 Reset button
137 Reset button
138 """
138 """
139 _set_input_attrs(attrs, type, name, value)
139 _set_input_attrs(attrs, type, name, value)
140 _set_id_attr(attrs, id, name)
140 _set_id_attr(attrs, id, name)
141 convert_boolean_attrs(attrs, ["disabled"])
141 convert_boolean_attrs(attrs, ["disabled"])
142 return HTML.input(**attrs)
142 return HTML.input(**attrs)
143
143
144 reset = _reset
144 reset = _reset
145 safeid = _make_safe_id_component
145 safeid = _make_safe_id_component
146
146
147
147
148 def branding(name, length=40):
148 def branding(name, length=40):
149 return truncate(name, length, indicator="")
149 return truncate(name, length, indicator="")
150
150
151
151
152 def FID(raw_id, path):
152 def FID(raw_id, path):
153 """
153 """
154 Creates a unique ID for filenode based on it's hash of path and commit
154 Creates a unique ID for filenode based on it's hash of path and commit
155 it's safe to use in urls
155 it's safe to use in urls
156
156
157 :param raw_id:
157 :param raw_id:
158 :param path:
158 :param path:
159 """
159 """
160
160
161 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
161 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
162
162
163
163
164 class _GetError(object):
164 class _GetError(object):
165 """Get error from form_errors, and represent it as span wrapped error
165 """Get error from form_errors, and represent it as span wrapped error
166 message
166 message
167
167
168 :param field_name: field to fetch errors for
168 :param field_name: field to fetch errors for
169 :param form_errors: form errors dict
169 :param form_errors: form errors dict
170 """
170 """
171
171
172 def __call__(self, field_name, form_errors):
172 def __call__(self, field_name, form_errors):
173 tmpl = """<span class="error_msg">%s</span>"""
173 tmpl = """<span class="error_msg">%s</span>"""
174 if form_errors and field_name in form_errors:
174 if form_errors and field_name in form_errors:
175 return literal(tmpl % form_errors.get(field_name))
175 return literal(tmpl % form_errors.get(field_name))
176
176
177 get_error = _GetError()
177 get_error = _GetError()
178
178
179
179
180 class _ToolTip(object):
180 class _ToolTip(object):
181
181
182 def __call__(self, tooltip_title, trim_at=50):
182 def __call__(self, tooltip_title, trim_at=50):
183 """
183 """
184 Special function just to wrap our text into nice formatted
184 Special function just to wrap our text into nice formatted
185 autowrapped text
185 autowrapped text
186
186
187 :param tooltip_title:
187 :param tooltip_title:
188 """
188 """
189 tooltip_title = escape(tooltip_title)
189 tooltip_title = escape(tooltip_title)
190 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
190 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
191 return tooltip_title
191 return tooltip_title
192 tooltip = _ToolTip()
192 tooltip = _ToolTip()
193
193
194
194
195 def files_breadcrumbs(repo_name, commit_id, file_path):
195 def files_breadcrumbs(repo_name, commit_id, file_path):
196 if isinstance(file_path, str):
196 if isinstance(file_path, str):
197 file_path = safe_unicode(file_path)
197 file_path = safe_unicode(file_path)
198
198
199 # TODO: johbo: Is this always a url like path, or is this operating
199 # TODO: johbo: Is this always a url like path, or is this operating
200 # system dependent?
200 # system dependent?
201 path_segments = file_path.split('/')
201 path_segments = file_path.split('/')
202
202
203 repo_name_html = escape(repo_name)
203 repo_name_html = escape(repo_name)
204 if len(path_segments) == 1 and path_segments[0] == '':
204 if len(path_segments) == 1 and path_segments[0] == '':
205 url_segments = [repo_name_html]
205 url_segments = [repo_name_html]
206 else:
206 else:
207 url_segments = [
207 url_segments = [
208 link_to(
208 link_to(
209 repo_name_html,
209 repo_name_html,
210 url('files_home',
210 url('files_home',
211 repo_name=repo_name,
211 repo_name=repo_name,
212 revision=commit_id,
212 revision=commit_id,
213 f_path=''),
213 f_path=''),
214 class_='pjax-link')]
214 class_='pjax-link')]
215
215
216 last_cnt = len(path_segments) - 1
216 last_cnt = len(path_segments) - 1
217 for cnt, segment in enumerate(path_segments):
217 for cnt, segment in enumerate(path_segments):
218 if not segment:
218 if not segment:
219 continue
219 continue
220 segment_html = escape(segment)
220 segment_html = escape(segment)
221
221
222 if cnt != last_cnt:
222 if cnt != last_cnt:
223 url_segments.append(
223 url_segments.append(
224 link_to(
224 link_to(
225 segment_html,
225 segment_html,
226 url('files_home',
226 url('files_home',
227 repo_name=repo_name,
227 repo_name=repo_name,
228 revision=commit_id,
228 revision=commit_id,
229 f_path='/'.join(path_segments[:cnt + 1])),
229 f_path='/'.join(path_segments[:cnt + 1])),
230 class_='pjax-link'))
230 class_='pjax-link'))
231 else:
231 else:
232 url_segments.append(segment_html)
232 url_segments.append(segment_html)
233
233
234 return literal('/'.join(url_segments))
234 return literal('/'.join(url_segments))
235
235
236
236
237 class CodeHtmlFormatter(HtmlFormatter):
237 class CodeHtmlFormatter(HtmlFormatter):
238 """
238 """
239 My code Html Formatter for source codes
239 My code Html Formatter for source codes
240 """
240 """
241
241
242 def wrap(self, source, outfile):
242 def wrap(self, source, outfile):
243 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
243 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
244
244
245 def _wrap_code(self, source):
245 def _wrap_code(self, source):
246 for cnt, it in enumerate(source):
246 for cnt, it in enumerate(source):
247 i, t = it
247 i, t = it
248 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
248 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
249 yield i, t
249 yield i, t
250
250
251 def _wrap_tablelinenos(self, inner):
251 def _wrap_tablelinenos(self, inner):
252 dummyoutfile = StringIO.StringIO()
252 dummyoutfile = StringIO.StringIO()
253 lncount = 0
253 lncount = 0
254 for t, line in inner:
254 for t, line in inner:
255 if t:
255 if t:
256 lncount += 1
256 lncount += 1
257 dummyoutfile.write(line)
257 dummyoutfile.write(line)
258
258
259 fl = self.linenostart
259 fl = self.linenostart
260 mw = len(str(lncount + fl - 1))
260 mw = len(str(lncount + fl - 1))
261 sp = self.linenospecial
261 sp = self.linenospecial
262 st = self.linenostep
262 st = self.linenostep
263 la = self.lineanchors
263 la = self.lineanchors
264 aln = self.anchorlinenos
264 aln = self.anchorlinenos
265 nocls = self.noclasses
265 nocls = self.noclasses
266 if sp:
266 if sp:
267 lines = []
267 lines = []
268
268
269 for i in range(fl, fl + lncount):
269 for i in range(fl, fl + lncount):
270 if i % st == 0:
270 if i % st == 0:
271 if i % sp == 0:
271 if i % sp == 0:
272 if aln:
272 if aln:
273 lines.append('<a href="#%s%d" class="special">%*d</a>' %
273 lines.append('<a href="#%s%d" class="special">%*d</a>' %
274 (la, i, mw, i))
274 (la, i, mw, i))
275 else:
275 else:
276 lines.append('<span class="special">%*d</span>' % (mw, i))
276 lines.append('<span class="special">%*d</span>' % (mw, i))
277 else:
277 else:
278 if aln:
278 if aln:
279 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
279 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
280 else:
280 else:
281 lines.append('%*d' % (mw, i))
281 lines.append('%*d' % (mw, i))
282 else:
282 else:
283 lines.append('')
283 lines.append('')
284 ls = '\n'.join(lines)
284 ls = '\n'.join(lines)
285 else:
285 else:
286 lines = []
286 lines = []
287 for i in range(fl, fl + lncount):
287 for i in range(fl, fl + lncount):
288 if i % st == 0:
288 if i % st == 0:
289 if aln:
289 if aln:
290 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
290 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
291 else:
291 else:
292 lines.append('%*d' % (mw, i))
292 lines.append('%*d' % (mw, i))
293 else:
293 else:
294 lines.append('')
294 lines.append('')
295 ls = '\n'.join(lines)
295 ls = '\n'.join(lines)
296
296
297 # in case you wonder about the seemingly redundant <div> here: since the
297 # in case you wonder about the seemingly redundant <div> here: since the
298 # content in the other cell also is wrapped in a div, some browsers in
298 # content in the other cell also is wrapped in a div, some browsers in
299 # some configurations seem to mess up the formatting...
299 # some configurations seem to mess up the formatting...
300 if nocls:
300 if nocls:
301 yield 0, ('<table class="%stable">' % self.cssclass +
301 yield 0, ('<table class="%stable">' % self.cssclass +
302 '<tr><td><div class="linenodiv" '
302 '<tr><td><div class="linenodiv" '
303 'style="background-color: #f0f0f0; padding-right: 10px">'
303 'style="background-color: #f0f0f0; padding-right: 10px">'
304 '<pre style="line-height: 125%">' +
304 '<pre style="line-height: 125%">' +
305 ls + '</pre></div></td><td id="hlcode" class="code">')
305 ls + '</pre></div></td><td id="hlcode" class="code">')
306 else:
306 else:
307 yield 0, ('<table class="%stable">' % self.cssclass +
307 yield 0, ('<table class="%stable">' % self.cssclass +
308 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
308 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
309 ls + '</pre></div></td><td id="hlcode" class="code">')
309 ls + '</pre></div></td><td id="hlcode" class="code">')
310 yield 0, dummyoutfile.getvalue()
310 yield 0, dummyoutfile.getvalue()
311 yield 0, '</td></tr></table>'
311 yield 0, '</td></tr></table>'
312
312
313
313
314 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
314 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
315 def __init__(self, **kw):
315 def __init__(self, **kw):
316 # only show these line numbers if set
316 # only show these line numbers if set
317 self.only_lines = kw.pop('only_line_numbers', [])
317 self.only_lines = kw.pop('only_line_numbers', [])
318 self.query_terms = kw.pop('query_terms', [])
318 self.query_terms = kw.pop('query_terms', [])
319 self.max_lines = kw.pop('max_lines', 5)
319 self.max_lines = kw.pop('max_lines', 5)
320 self.line_context = kw.pop('line_context', 3)
320 self.line_context = kw.pop('line_context', 3)
321 self.url = kw.pop('url', None)
321 self.url = kw.pop('url', None)
322
322
323 super(CodeHtmlFormatter, self).__init__(**kw)
323 super(CodeHtmlFormatter, self).__init__(**kw)
324
324
325 def _wrap_code(self, source):
325 def _wrap_code(self, source):
326 for cnt, it in enumerate(source):
326 for cnt, it in enumerate(source):
327 i, t = it
327 i, t = it
328 t = '<pre>%s</pre>' % t
328 t = '<pre>%s</pre>' % t
329 yield i, t
329 yield i, t
330
330
331 def _wrap_tablelinenos(self, inner):
331 def _wrap_tablelinenos(self, inner):
332 yield 0, '<table class="code-highlight %stable">' % self.cssclass
332 yield 0, '<table class="code-highlight %stable">' % self.cssclass
333
333
334 last_shown_line_number = 0
334 last_shown_line_number = 0
335 current_line_number = 1
335 current_line_number = 1
336
336
337 for t, line in inner:
337 for t, line in inner:
338 if not t:
338 if not t:
339 yield t, line
339 yield t, line
340 continue
340 continue
341
341
342 if current_line_number in self.only_lines:
342 if current_line_number in self.only_lines:
343 if last_shown_line_number + 1 != current_line_number:
343 if last_shown_line_number + 1 != current_line_number:
344 yield 0, '<tr>'
344 yield 0, '<tr>'
345 yield 0, '<td class="line">...</td>'
345 yield 0, '<td class="line">...</td>'
346 yield 0, '<td id="hlcode" class="code"></td>'
346 yield 0, '<td id="hlcode" class="code"></td>'
347 yield 0, '</tr>'
347 yield 0, '</tr>'
348
348
349 yield 0, '<tr>'
349 yield 0, '<tr>'
350 if self.url:
350 if self.url:
351 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
351 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
352 self.url, current_line_number, current_line_number)
352 self.url, current_line_number, current_line_number)
353 else:
353 else:
354 yield 0, '<td class="line"><a href="">%i</a></td>' % (
354 yield 0, '<td class="line"><a href="">%i</a></td>' % (
355 current_line_number)
355 current_line_number)
356 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
356 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
357 yield 0, '</tr>'
357 yield 0, '</tr>'
358
358
359 last_shown_line_number = current_line_number
359 last_shown_line_number = current_line_number
360
360
361 current_line_number += 1
361 current_line_number += 1
362
362
363
363
364 yield 0, '</table>'
364 yield 0, '</table>'
365
365
366
366
367 def extract_phrases(text_query):
367 def extract_phrases(text_query):
368 """
368 """
369 Extracts phrases from search term string making sure phrases
369 Extracts phrases from search term string making sure phrases
370 contained in double quotes are kept together - and discarding empty values
370 contained in double quotes are kept together - and discarding empty values
371 or fully whitespace values eg.
371 or fully whitespace values eg.
372
372
373 'some text "a phrase" more' => ['some', 'text', 'a phrase', 'more']
373 'some text "a phrase" more' => ['some', 'text', 'a phrase', 'more']
374
374
375 """
375 """
376
376
377 in_phrase = False
377 in_phrase = False
378 buf = ''
378 buf = ''
379 phrases = []
379 phrases = []
380 for char in text_query:
380 for char in text_query:
381 if in_phrase:
381 if in_phrase:
382 if char == '"': # end phrase
382 if char == '"': # end phrase
383 phrases.append(buf)
383 phrases.append(buf)
384 buf = ''
384 buf = ''
385 in_phrase = False
385 in_phrase = False
386 continue
386 continue
387 else:
387 else:
388 buf += char
388 buf += char
389 continue
389 continue
390 else:
390 else:
391 if char == '"': # start phrase
391 if char == '"': # start phrase
392 in_phrase = True
392 in_phrase = True
393 phrases.append(buf)
393 phrases.append(buf)
394 buf = ''
394 buf = ''
395 continue
395 continue
396 elif char == ' ':
396 elif char == ' ':
397 phrases.append(buf)
397 phrases.append(buf)
398 buf = ''
398 buf = ''
399 continue
399 continue
400 else:
400 else:
401 buf += char
401 buf += char
402
402
403 phrases.append(buf)
403 phrases.append(buf)
404 phrases = [phrase.strip() for phrase in phrases if phrase.strip()]
404 phrases = [phrase.strip() for phrase in phrases if phrase.strip()]
405 return phrases
405 return phrases
406
406
407
407
408 def get_matching_offsets(text, phrases):
408 def get_matching_offsets(text, phrases):
409 """
409 """
410 Returns a list of string offsets in `text` that the list of `terms` match
410 Returns a list of string offsets in `text` that the list of `terms` match
411
411
412 >>> get_matching_offsets('some text here', ['some', 'here'])
412 >>> get_matching_offsets('some text here', ['some', 'here'])
413 [(0, 4), (10, 14)]
413 [(0, 4), (10, 14)]
414
414
415 """
415 """
416 offsets = []
416 offsets = []
417 for phrase in phrases:
417 for phrase in phrases:
418 for match in re.finditer(phrase, text):
418 for match in re.finditer(phrase, text):
419 offsets.append((match.start(), match.end()))
419 offsets.append((match.start(), match.end()))
420
420
421 return offsets
421 return offsets
422
422
423
423
424 def normalize_text_for_matching(x):
424 def normalize_text_for_matching(x):
425 """
425 """
426 Replaces all non alnum characters to spaces and lower cases the string,
426 Replaces all non alnum characters to spaces and lower cases the string,
427 useful for comparing two text strings without punctuation
427 useful for comparing two text strings without punctuation
428 """
428 """
429 return re.sub(r'[^\w]', ' ', x.lower())
429 return re.sub(r'[^\w]', ' ', x.lower())
430
430
431
431
432 def get_matching_line_offsets(lines, terms):
432 def get_matching_line_offsets(lines, terms):
433 """ Return a set of `lines` indices (starting from 1) matching a
433 """ Return a set of `lines` indices (starting from 1) matching a
434 text search query, along with `context` lines above/below matching lines
434 text search query, along with `context` lines above/below matching lines
435
435
436 :param lines: list of strings representing lines
436 :param lines: list of strings representing lines
437 :param terms: search term string to match in lines eg. 'some text'
437 :param terms: search term string to match in lines eg. 'some text'
438 :param context: number of lines above/below a matching line to add to result
438 :param context: number of lines above/below a matching line to add to result
439 :param max_lines: cut off for lines of interest
439 :param max_lines: cut off for lines of interest
440 eg.
440 eg.
441
441
442 text = '''
442 text = '''
443 words words words
443 words words words
444 words words words
444 words words words
445 some text some
445 some text some
446 words words words
446 words words words
447 words words words
447 words words words
448 text here what
448 text here what
449 '''
449 '''
450 get_matching_line_offsets(text, 'text', context=1)
450 get_matching_line_offsets(text, 'text', context=1)
451 {3: [(5, 9)], 6: [(0, 4)]]
451 {3: [(5, 9)], 6: [(0, 4)]]
452
452
453 """
453 """
454 matching_lines = {}
454 matching_lines = {}
455 phrases = [normalize_text_for_matching(phrase)
455 phrases = [normalize_text_for_matching(phrase)
456 for phrase in extract_phrases(terms)]
456 for phrase in extract_phrases(terms)]
457
457
458 for line_index, line in enumerate(lines, start=1):
458 for line_index, line in enumerate(lines, start=1):
459 match_offsets = get_matching_offsets(
459 match_offsets = get_matching_offsets(
460 normalize_text_for_matching(line), phrases)
460 normalize_text_for_matching(line), phrases)
461 if match_offsets:
461 if match_offsets:
462 matching_lines[line_index] = match_offsets
462 matching_lines[line_index] = match_offsets
463
463
464 return matching_lines
464 return matching_lines
465
465
466
466
467 def get_lexer_safe(mimetype=None, filepath=None):
467 def get_lexer_safe(mimetype=None, filepath=None):
468 """
468 """
469 Tries to return a relevant pygments lexer using mimetype/filepath name,
469 Tries to return a relevant pygments lexer using mimetype/filepath name,
470 defaulting to plain text if none could be found
470 defaulting to plain text if none could be found
471 """
471 """
472 lexer = None
472 lexer = None
473 try:
473 try:
474 if mimetype:
474 if mimetype:
475 lexer = get_lexer_for_mimetype(mimetype)
475 lexer = get_lexer_for_mimetype(mimetype)
476 if not lexer:
476 if not lexer:
477 lexer = get_lexer_for_filename(filepath)
477 lexer = get_lexer_for_filename(filepath)
478 except pygments.util.ClassNotFound:
478 except pygments.util.ClassNotFound:
479 pass
479 pass
480
480
481 if not lexer:
481 if not lexer:
482 lexer = get_lexer_by_name('text')
482 lexer = get_lexer_by_name('text')
483
483
484 return lexer
484 return lexer
485
485
486
486
487 def pygmentize(filenode, **kwargs):
487 def pygmentize(filenode, **kwargs):
488 """
488 """
489 pygmentize function using pygments
489 pygmentize function using pygments
490
490
491 :param filenode:
491 :param filenode:
492 """
492 """
493 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
493 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
494 return literal(code_highlight(filenode.content, lexer,
494 return literal(code_highlight(filenode.content, lexer,
495 CodeHtmlFormatter(**kwargs)))
495 CodeHtmlFormatter(**kwargs)))
496
496
497
497
498 def pygmentize_annotation(repo_name, filenode, **kwargs):
498 def pygmentize_annotation(repo_name, filenode, **kwargs):
499 """
499 """
500 pygmentize function for annotation
500 pygmentize function for annotation
501
501
502 :param filenode:
502 :param filenode:
503 """
503 """
504
504
505 color_dict = {}
505 color_dict = {}
506
506
507 def gen_color(n=10000):
507 def gen_color(n=10000):
508 """generator for getting n of evenly distributed colors using
508 """generator for getting n of evenly distributed colors using
509 hsv color and golden ratio. It always return same order of colors
509 hsv color and golden ratio. It always return same order of colors
510
510
511 :returns: RGB tuple
511 :returns: RGB tuple
512 """
512 """
513
513
514 def hsv_to_rgb(h, s, v):
514 def hsv_to_rgb(h, s, v):
515 if s == 0.0:
515 if s == 0.0:
516 return v, v, v
516 return v, v, v
517 i = int(h * 6.0) # XXX assume int() truncates!
517 i = int(h * 6.0) # XXX assume int() truncates!
518 f = (h * 6.0) - i
518 f = (h * 6.0) - i
519 p = v * (1.0 - s)
519 p = v * (1.0 - s)
520 q = v * (1.0 - s * f)
520 q = v * (1.0 - s * f)
521 t = v * (1.0 - s * (1.0 - f))
521 t = v * (1.0 - s * (1.0 - f))
522 i = i % 6
522 i = i % 6
523 if i == 0:
523 if i == 0:
524 return v, t, p
524 return v, t, p
525 if i == 1:
525 if i == 1:
526 return q, v, p
526 return q, v, p
527 if i == 2:
527 if i == 2:
528 return p, v, t
528 return p, v, t
529 if i == 3:
529 if i == 3:
530 return p, q, v
530 return p, q, v
531 if i == 4:
531 if i == 4:
532 return t, p, v
532 return t, p, v
533 if i == 5:
533 if i == 5:
534 return v, p, q
534 return v, p, q
535
535
536 golden_ratio = 0.618033988749895
536 golden_ratio = 0.618033988749895
537 h = 0.22717784590367374
537 h = 0.22717784590367374
538
538
539 for _ in xrange(n):
539 for _ in xrange(n):
540 h += golden_ratio
540 h += golden_ratio
541 h %= 1
541 h %= 1
542 HSV_tuple = [h, 0.95, 0.95]
542 HSV_tuple = [h, 0.95, 0.95]
543 RGB_tuple = hsv_to_rgb(*HSV_tuple)
543 RGB_tuple = hsv_to_rgb(*HSV_tuple)
544 yield map(lambda x: str(int(x * 256)), RGB_tuple)
544 yield map(lambda x: str(int(x * 256)), RGB_tuple)
545
545
546 cgenerator = gen_color()
546 cgenerator = gen_color()
547
547
548 def get_color_string(commit_id):
548 def get_color_string(commit_id):
549 if commit_id in color_dict:
549 if commit_id in color_dict:
550 col = color_dict[commit_id]
550 col = color_dict[commit_id]
551 else:
551 else:
552 col = color_dict[commit_id] = cgenerator.next()
552 col = color_dict[commit_id] = cgenerator.next()
553 return "color: rgb(%s)! important;" % (', '.join(col))
553 return "color: rgb(%s)! important;" % (', '.join(col))
554
554
555 def url_func(repo_name):
555 def url_func(repo_name):
556
556
557 def _url_func(commit):
557 def _url_func(commit):
558 author = commit.author
558 author = commit.author
559 date = commit.date
559 date = commit.date
560 message = tooltip(commit.message)
560 message = tooltip(commit.message)
561
561
562 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
562 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
563 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
563 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
564 "</b> %s<br/></div>")
564 "</b> %s<br/></div>")
565
565
566 tooltip_html = tooltip_html % (author, date, message)
566 tooltip_html = tooltip_html % (author, date, message)
567 lnk_format = '%5s:%s' % ('r%s' % commit.idx, commit.short_id)
567 lnk_format = '%5s:%s' % ('r%s' % commit.idx, commit.short_id)
568 uri = link_to(
568 uri = link_to(
569 lnk_format,
569 lnk_format,
570 url('changeset_home', repo_name=repo_name,
570 url('changeset_home', repo_name=repo_name,
571 revision=commit.raw_id),
571 revision=commit.raw_id),
572 style=get_color_string(commit.raw_id),
572 style=get_color_string(commit.raw_id),
573 class_='tooltip',
573 class_='tooltip',
574 title=tooltip_html
574 title=tooltip_html
575 )
575 )
576
576
577 uri += '\n'
577 uri += '\n'
578 return uri
578 return uri
579 return _url_func
579 return _url_func
580
580
581 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
581 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
582
582
583
583
584 def is_following_repo(repo_name, user_id):
584 def is_following_repo(repo_name, user_id):
585 from rhodecode.model.scm import ScmModel
585 from rhodecode.model.scm import ScmModel
586 return ScmModel().is_following_repo(repo_name, user_id)
586 return ScmModel().is_following_repo(repo_name, user_id)
587
587
588
588
589 class _Message(object):
589 class _Message(object):
590 """A message returned by ``Flash.pop_messages()``.
590 """A message returned by ``Flash.pop_messages()``.
591
591
592 Converting the message to a string returns the message text. Instances
592 Converting the message to a string returns the message text. Instances
593 also have the following attributes:
593 also have the following attributes:
594
594
595 * ``message``: the message text.
595 * ``message``: the message text.
596 * ``category``: the category specified when the message was created.
596 * ``category``: the category specified when the message was created.
597 """
597 """
598
598
599 def __init__(self, category, message):
599 def __init__(self, category, message):
600 self.category = category
600 self.category = category
601 self.message = message
601 self.message = message
602
602
603 def __str__(self):
603 def __str__(self):
604 return self.message
604 return self.message
605
605
606 __unicode__ = __str__
606 __unicode__ = __str__
607
607
608 def __html__(self):
608 def __html__(self):
609 return escape(safe_unicode(self.message))
609 return escape(safe_unicode(self.message))
610
610
611
611
612 class Flash(_Flash):
612 class Flash(_Flash):
613
613
614 def pop_messages(self):
614 def pop_messages(self):
615 """Return all accumulated messages and delete them from the session.
615 """Return all accumulated messages and delete them from the session.
616
616
617 The return value is a list of ``Message`` objects.
617 The return value is a list of ``Message`` objects.
618 """
618 """
619 from pylons import session
619 from pylons import session
620
620
621 messages = []
621 messages = []
622
622
623 # Pop the 'old' pylons flash messages. They are tuples of the form
623 # Pop the 'old' pylons flash messages. They are tuples of the form
624 # (category, message)
624 # (category, message)
625 for cat, msg in session.pop(self.session_key, []):
625 for cat, msg in session.pop(self.session_key, []):
626 messages.append(_Message(cat, msg))
626 messages.append(_Message(cat, msg))
627
627
628 # Pop the 'new' pyramid flash messages for each category as list
628 # Pop the 'new' pyramid flash messages for each category as list
629 # of strings.
629 # of strings.
630 for cat in self.categories:
630 for cat in self.categories:
631 for msg in session.pop_flash(queue=cat):
631 for msg in session.pop_flash(queue=cat):
632 messages.append(_Message(cat, msg))
632 messages.append(_Message(cat, msg))
633 # Map messages from the default queue to the 'notice' category.
633 # Map messages from the default queue to the 'notice' category.
634 for msg in session.pop_flash():
634 for msg in session.pop_flash():
635 messages.append(_Message('notice', msg))
635 messages.append(_Message('notice', msg))
636
636
637 session.save()
637 session.save()
638 return messages
638 return messages
639
639
640 flash = Flash()
640 flash = Flash()
641
641
642 #==============================================================================
642 #==============================================================================
643 # SCM FILTERS available via h.
643 # SCM FILTERS available via h.
644 #==============================================================================
644 #==============================================================================
645 from rhodecode.lib.vcs.utils import author_name, author_email
645 from rhodecode.lib.vcs.utils import author_name, author_email
646 from rhodecode.lib.utils2 import credentials_filter, age as _age
646 from rhodecode.lib.utils2 import credentials_filter, age as _age
647 from rhodecode.model.db import User, ChangesetStatus
647 from rhodecode.model.db import User, ChangesetStatus
648
648
649 age = _age
649 age = _age
650 capitalize = lambda x: x.capitalize()
650 capitalize = lambda x: x.capitalize()
651 email = author_email
651 email = author_email
652 short_id = lambda x: x[:12]
652 short_id = lambda x: x[:12]
653 hide_credentials = lambda x: ''.join(credentials_filter(x))
653 hide_credentials = lambda x: ''.join(credentials_filter(x))
654
654
655
655
656 def age_component(datetime_iso, value=None, time_is_local=False):
656 def age_component(datetime_iso, value=None, time_is_local=False):
657 title = value or format_date(datetime_iso)
657 title = value or format_date(datetime_iso)
658
658
659 # detect if we have a timezone info, otherwise, add it
659 # detect if we have a timezone info, otherwise, add it
660 if isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
660 if isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
661 tzinfo = '+00:00'
661 tzinfo = '+00:00'
662
662
663 if time_is_local:
663 if time_is_local:
664 tzinfo = time.strftime("+%H:%M",
664 tzinfo = time.strftime("+%H:%M",
665 time.gmtime(
665 time.gmtime(
666 (datetime.now() - datetime.utcnow()).seconds + 1
666 (datetime.now() - datetime.utcnow()).seconds + 1
667 )
667 )
668 )
668 )
669
669
670 return literal(
670 return literal(
671 '<time class="timeago tooltip" '
671 '<time class="timeago tooltip" '
672 'title="{1}" datetime="{0}{2}">{1}</time>'.format(
672 'title="{1}" datetime="{0}{2}">{1}</time>'.format(
673 datetime_iso, title, tzinfo))
673 datetime_iso, title, tzinfo))
674
674
675
675
676 def _shorten_commit_id(commit_id):
676 def _shorten_commit_id(commit_id):
677 from rhodecode import CONFIG
677 from rhodecode import CONFIG
678 def_len = safe_int(CONFIG.get('rhodecode_show_sha_length', 12))
678 def_len = safe_int(CONFIG.get('rhodecode_show_sha_length', 12))
679 return commit_id[:def_len]
679 return commit_id[:def_len]
680
680
681
681
682 def show_id(commit):
682 def show_id(commit):
683 """
683 """
684 Configurable function that shows ID
684 Configurable function that shows ID
685 by default it's r123:fffeeefffeee
685 by default it's r123:fffeeefffeee
686
686
687 :param commit: commit instance
687 :param commit: commit instance
688 """
688 """
689 from rhodecode import CONFIG
689 from rhodecode import CONFIG
690 show_idx = str2bool(CONFIG.get('rhodecode_show_revision_number', True))
690 show_idx = str2bool(CONFIG.get('rhodecode_show_revision_number', True))
691
691
692 raw_id = _shorten_commit_id(commit.raw_id)
692 raw_id = _shorten_commit_id(commit.raw_id)
693 if show_idx:
693 if show_idx:
694 return 'r%s:%s' % (commit.idx, raw_id)
694 return 'r%s:%s' % (commit.idx, raw_id)
695 else:
695 else:
696 return '%s' % (raw_id, )
696 return '%s' % (raw_id, )
697
697
698
698
699 def format_date(date):
699 def format_date(date):
700 """
700 """
701 use a standardized formatting for dates used in RhodeCode
701 use a standardized formatting for dates used in RhodeCode
702
702
703 :param date: date/datetime object
703 :param date: date/datetime object
704 :return: formatted date
704 :return: formatted date
705 """
705 """
706
706
707 if date:
707 if date:
708 _fmt = "%a, %d %b %Y %H:%M:%S"
708 _fmt = "%a, %d %b %Y %H:%M:%S"
709 return safe_unicode(date.strftime(_fmt))
709 return safe_unicode(date.strftime(_fmt))
710
710
711 return u""
711 return u""
712
712
713
713
714 class _RepoChecker(object):
714 class _RepoChecker(object):
715
715
716 def __init__(self, backend_alias):
716 def __init__(self, backend_alias):
717 self._backend_alias = backend_alias
717 self._backend_alias = backend_alias
718
718
719 def __call__(self, repository):
719 def __call__(self, repository):
720 if hasattr(repository, 'alias'):
720 if hasattr(repository, 'alias'):
721 _type = repository.alias
721 _type = repository.alias
722 elif hasattr(repository, 'repo_type'):
722 elif hasattr(repository, 'repo_type'):
723 _type = repository.repo_type
723 _type = repository.repo_type
724 else:
724 else:
725 _type = repository
725 _type = repository
726 return _type == self._backend_alias
726 return _type == self._backend_alias
727
727
728 is_git = _RepoChecker('git')
728 is_git = _RepoChecker('git')
729 is_hg = _RepoChecker('hg')
729 is_hg = _RepoChecker('hg')
730 is_svn = _RepoChecker('svn')
730 is_svn = _RepoChecker('svn')
731
731
732
732
733 def get_repo_type_by_name(repo_name):
733 def get_repo_type_by_name(repo_name):
734 repo = Repository.get_by_repo_name(repo_name)
734 repo = Repository.get_by_repo_name(repo_name)
735 return repo.repo_type
735 return repo.repo_type
736
736
737
737
738 def is_svn_without_proxy(repository):
738 def is_svn_without_proxy(repository):
739 from rhodecode import CONFIG
739 from rhodecode import CONFIG
740 if is_svn(repository):
740 if is_svn(repository):
741 if not CONFIG.get('rhodecode_proxy_subversion_http_requests', False):
741 if not CONFIG.get('rhodecode_proxy_subversion_http_requests', False):
742 return True
742 return True
743 return False
743 return False
744
744
745
745
746 def discover_user(author):
746 def discover_user(author):
747 """
747 """
748 Tries to discover RhodeCode User based on the autho string. Author string
748 Tries to discover RhodeCode User based on the autho string. Author string
749 is typically `FirstName LastName <email@address.com>`
749 is typically `FirstName LastName <email@address.com>`
750 """
750 """
751
751
752 # if author is already an instance use it for extraction
752 # if author is already an instance use it for extraction
753 if isinstance(author, User):
753 if isinstance(author, User):
754 return author
754 return author
755
755
756 # Valid email in the attribute passed, see if they're in the system
756 # Valid email in the attribute passed, see if they're in the system
757 _email = author_email(author)
757 _email = author_email(author)
758 if _email != '':
758 if _email != '':
759 user = User.get_by_email(_email, case_insensitive=True, cache=True)
759 user = User.get_by_email(_email, case_insensitive=True, cache=True)
760 if user is not None:
760 if user is not None:
761 return user
761 return user
762
762
763 # Maybe it's a username, we try to extract it and fetch by username ?
763 # Maybe it's a username, we try to extract it and fetch by username ?
764 _author = author_name(author)
764 _author = author_name(author)
765 user = User.get_by_username(_author, case_insensitive=True, cache=True)
765 user = User.get_by_username(_author, case_insensitive=True, cache=True)
766 if user is not None:
766 if user is not None:
767 return user
767 return user
768
768
769 return None
769 return None
770
770
771
771
772 def email_or_none(author):
772 def email_or_none(author):
773 # extract email from the commit string
773 # extract email from the commit string
774 _email = author_email(author)
774 _email = author_email(author)
775
775
776 # If we have an email, use it, otherwise
776 # If we have an email, use it, otherwise
777 # see if it contains a username we can get an email from
777 # see if it contains a username we can get an email from
778 if _email != '':
778 if _email != '':
779 return _email
779 return _email
780 else:
780 else:
781 user = User.get_by_username(author_name(author), case_insensitive=True,
781 user = User.get_by_username(author_name(author), case_insensitive=True,
782 cache=True)
782 cache=True)
783
783
784 if user is not None:
784 if user is not None:
785 return user.email
785 return user.email
786
786
787 # No valid email, not a valid user in the system, none!
787 # No valid email, not a valid user in the system, none!
788 return None
788 return None
789
789
790
790
791 def link_to_user(author, length=0, **kwargs):
791 def link_to_user(author, length=0, **kwargs):
792 user = discover_user(author)
792 user = discover_user(author)
793 # user can be None, but if we have it already it means we can re-use it
793 # user can be None, but if we have it already it means we can re-use it
794 # in the person() function, so we save 1 intensive-query
794 # in the person() function, so we save 1 intensive-query
795 if user:
795 if user:
796 author = user
796 author = user
797
797
798 display_person = person(author, 'username_or_name_or_email')
798 display_person = person(author, 'username_or_name_or_email')
799 if length:
799 if length:
800 display_person = shorter(display_person, length)
800 display_person = shorter(display_person, length)
801
801
802 if user:
802 if user:
803 return link_to(
803 return link_to(
804 escape(display_person),
804 escape(display_person),
805 url('user_profile', username=user.username),
805 url('user_profile', username=user.username),
806 **kwargs)
806 **kwargs)
807 else:
807 else:
808 return escape(display_person)
808 return escape(display_person)
809
809
810
810
811 def person(author, show_attr="username_and_name"):
811 def person(author, show_attr="username_and_name"):
812 user = discover_user(author)
812 user = discover_user(author)
813 if user:
813 if user:
814 return getattr(user, show_attr)
814 return getattr(user, show_attr)
815 else:
815 else:
816 _author = author_name(author)
816 _author = author_name(author)
817 _email = email(author)
817 _email = email(author)
818 return _author or _email
818 return _author or _email
819
819
820
820
821 def author_string(email):
822 if email:
823 user = User.get_by_email(email, case_insensitive=True, cache=True)
824 if user:
825 if user.firstname or user.lastname:
826 return '%s %s &lt;%s&gt;' % (user.firstname, user.lastname, email)
827 else:
828 return email
829 else:
830 return email
831 else:
832 return None
833
834
821 def person_by_id(id_, show_attr="username_and_name"):
835 def person_by_id(id_, show_attr="username_and_name"):
822 # attr to return from fetched user
836 # attr to return from fetched user
823 person_getter = lambda usr: getattr(usr, show_attr)
837 person_getter = lambda usr: getattr(usr, show_attr)
824
838
825 #maybe it's an ID ?
839 #maybe it's an ID ?
826 if str(id_).isdigit() or isinstance(id_, int):
840 if str(id_).isdigit() or isinstance(id_, int):
827 id_ = int(id_)
841 id_ = int(id_)
828 user = User.get(id_)
842 user = User.get(id_)
829 if user is not None:
843 if user is not None:
830 return person_getter(user)
844 return person_getter(user)
831 return id_
845 return id_
832
846
833
847
834 def gravatar_with_user(author, show_disabled=False):
848 def gravatar_with_user(author, show_disabled=False):
835 from rhodecode.lib.utils import PartialRenderer
849 from rhodecode.lib.utils import PartialRenderer
836 _render = PartialRenderer('base/base.html')
850 _render = PartialRenderer('base/base.html')
837 return _render('gravatar_with_user', author, show_disabled=show_disabled)
851 return _render('gravatar_with_user', author, show_disabled=show_disabled)
838
852
839
853
840 def desc_stylize(value):
854 def desc_stylize(value):
841 """
855 """
842 converts tags from value into html equivalent
856 converts tags from value into html equivalent
843
857
844 :param value:
858 :param value:
845 """
859 """
846 if not value:
860 if not value:
847 return ''
861 return ''
848
862
849 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
863 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
850 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
864 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
851 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
865 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
852 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
866 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
853 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
867 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
854 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
868 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
855 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
869 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
856 '<div class="metatag" tag="lang">\\2</div>', value)
870 '<div class="metatag" tag="lang">\\2</div>', value)
857 value = re.sub(r'\[([a-z]+)\]',
871 value = re.sub(r'\[([a-z]+)\]',
858 '<div class="metatag" tag="\\1">\\1</div>', value)
872 '<div class="metatag" tag="\\1">\\1</div>', value)
859
873
860 return value
874 return value
861
875
862
876
863 def escaped_stylize(value):
877 def escaped_stylize(value):
864 """
878 """
865 converts tags from value into html equivalent, but escaping its value first
879 converts tags from value into html equivalent, but escaping its value first
866 """
880 """
867 if not value:
881 if not value:
868 return ''
882 return ''
869
883
870 # Using default webhelper escape method, but has to force it as a
884 # Using default webhelper escape method, but has to force it as a
871 # plain unicode instead of a markup tag to be used in regex expressions
885 # plain unicode instead of a markup tag to be used in regex expressions
872 value = unicode(escape(safe_unicode(value)))
886 value = unicode(escape(safe_unicode(value)))
873
887
874 value = re.sub(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
888 value = re.sub(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
875 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
889 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
876 value = re.sub(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
890 value = re.sub(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
877 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
891 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
878 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]',
892 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]',
879 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
893 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
880 value = re.sub(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+]*)\]',
894 value = re.sub(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+]*)\]',
881 '<div class="metatag" tag="lang">\\2</div>', value)
895 '<div class="metatag" tag="lang">\\2</div>', value)
882 value = re.sub(r'\[([a-z]+)\]',
896 value = re.sub(r'\[([a-z]+)\]',
883 '<div class="metatag" tag="\\1">\\1</div>', value)
897 '<div class="metatag" tag="\\1">\\1</div>', value)
884
898
885 return value
899 return value
886
900
887
901
888 def bool2icon(value):
902 def bool2icon(value):
889 """
903 """
890 Returns boolean value of a given value, represented as html element with
904 Returns boolean value of a given value, represented as html element with
891 classes that will represent icons
905 classes that will represent icons
892
906
893 :param value: given value to convert to html node
907 :param value: given value to convert to html node
894 """
908 """
895
909
896 if value: # does bool conversion
910 if value: # does bool conversion
897 return HTML.tag('i', class_="icon-true")
911 return HTML.tag('i', class_="icon-true")
898 else: # not true as bool
912 else: # not true as bool
899 return HTML.tag('i', class_="icon-false")
913 return HTML.tag('i', class_="icon-false")
900
914
901
915
902 #==============================================================================
916 #==============================================================================
903 # PERMS
917 # PERMS
904 #==============================================================================
918 #==============================================================================
905 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
919 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
906 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
920 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
907 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token
921 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token
908
922
909
923
910 #==============================================================================
924 #==============================================================================
911 # GRAVATAR URL
925 # GRAVATAR URL
912 #==============================================================================
926 #==============================================================================
913 class InitialsGravatar(object):
927 class InitialsGravatar(object):
914 def __init__(self, email_address, first_name, last_name, size=30,
928 def __init__(self, email_address, first_name, last_name, size=30,
915 background=None, text_color='#fff'):
929 background=None, text_color='#fff'):
916 self.size = size
930 self.size = size
917 self.first_name = first_name
931 self.first_name = first_name
918 self.last_name = last_name
932 self.last_name = last_name
919 self.email_address = email_address
933 self.email_address = email_address
920 self.background = background or self.str2color(email_address)
934 self.background = background or self.str2color(email_address)
921 self.text_color = text_color
935 self.text_color = text_color
922
936
923 def get_color_bank(self):
937 def get_color_bank(self):
924 """
938 """
925 returns a predefined list of colors that gravatars can use.
939 returns a predefined list of colors that gravatars can use.
926 Those are randomized distinct colors that guarantee readability and
940 Those are randomized distinct colors that guarantee readability and
927 uniqueness.
941 uniqueness.
928
942
929 generated with: http://phrogz.net/css/distinct-colors.html
943 generated with: http://phrogz.net/css/distinct-colors.html
930 """
944 """
931 return [
945 return [
932 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
946 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
933 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
947 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
934 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
948 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
935 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
949 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
936 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
950 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
937 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
951 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
938 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
952 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
939 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
953 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
940 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
954 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
941 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
955 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
942 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
956 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
943 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
957 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
944 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
958 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
945 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
959 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
946 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
960 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
947 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
961 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
948 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
962 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
949 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
963 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
950 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
964 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
951 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
965 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
952 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
966 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
953 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
967 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
954 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
968 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
955 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
969 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
956 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
970 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
957 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
971 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
958 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
972 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
959 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
973 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
960 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
974 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
961 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
975 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
962 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
976 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
963 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
977 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
964 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
978 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
965 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
979 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
966 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
980 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
967 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
981 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
968 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
982 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
969 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
983 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
970 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
984 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
971 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
985 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
972 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
986 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
973 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
987 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
974 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
988 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
975 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
989 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
976 '#4f8c46', '#368dd9', '#5c0073'
990 '#4f8c46', '#368dd9', '#5c0073'
977 ]
991 ]
978
992
979 def rgb_to_hex_color(self, rgb_tuple):
993 def rgb_to_hex_color(self, rgb_tuple):
980 """
994 """
981 Converts an rgb_tuple passed to an hex color.
995 Converts an rgb_tuple passed to an hex color.
982
996
983 :param rgb_tuple: tuple with 3 ints represents rgb color space
997 :param rgb_tuple: tuple with 3 ints represents rgb color space
984 """
998 """
985 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
999 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
986
1000
987 def email_to_int_list(self, email_str):
1001 def email_to_int_list(self, email_str):
988 """
1002 """
989 Get every byte of the hex digest value of email and turn it to integer.
1003 Get every byte of the hex digest value of email and turn it to integer.
990 It's going to be always between 0-255
1004 It's going to be always between 0-255
991 """
1005 """
992 digest = md5_safe(email_str.lower())
1006 digest = md5_safe(email_str.lower())
993 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
1007 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
994
1008
995 def pick_color_bank_index(self, email_str, color_bank):
1009 def pick_color_bank_index(self, email_str, color_bank):
996 return self.email_to_int_list(email_str)[0] % len(color_bank)
1010 return self.email_to_int_list(email_str)[0] % len(color_bank)
997
1011
998 def str2color(self, email_str):
1012 def str2color(self, email_str):
999 """
1013 """
1000 Tries to map in a stable algorithm an email to color
1014 Tries to map in a stable algorithm an email to color
1001
1015
1002 :param email_str:
1016 :param email_str:
1003 """
1017 """
1004 color_bank = self.get_color_bank()
1018 color_bank = self.get_color_bank()
1005 # pick position (module it's length so we always find it in the
1019 # pick position (module it's length so we always find it in the
1006 # bank even if it's smaller than 256 values
1020 # bank even if it's smaller than 256 values
1007 pos = self.pick_color_bank_index(email_str, color_bank)
1021 pos = self.pick_color_bank_index(email_str, color_bank)
1008 return color_bank[pos]
1022 return color_bank[pos]
1009
1023
1010 def normalize_email(self, email_address):
1024 def normalize_email(self, email_address):
1011 import unicodedata
1025 import unicodedata
1012 # default host used to fill in the fake/missing email
1026 # default host used to fill in the fake/missing email
1013 default_host = u'localhost'
1027 default_host = u'localhost'
1014
1028
1015 if not email_address:
1029 if not email_address:
1016 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1030 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1017
1031
1018 email_address = safe_unicode(email_address)
1032 email_address = safe_unicode(email_address)
1019
1033
1020 if u'@' not in email_address:
1034 if u'@' not in email_address:
1021 email_address = u'%s@%s' % (email_address, default_host)
1035 email_address = u'%s@%s' % (email_address, default_host)
1022
1036
1023 if email_address.endswith(u'@'):
1037 if email_address.endswith(u'@'):
1024 email_address = u'%s%s' % (email_address, default_host)
1038 email_address = u'%s%s' % (email_address, default_host)
1025
1039
1026 email_address = unicodedata.normalize('NFKD', email_address)\
1040 email_address = unicodedata.normalize('NFKD', email_address)\
1027 .encode('ascii', 'ignore')
1041 .encode('ascii', 'ignore')
1028 return email_address
1042 return email_address
1029
1043
1030 def get_initials(self):
1044 def get_initials(self):
1031 """
1045 """
1032 Returns 2 letter initials calculated based on the input.
1046 Returns 2 letter initials calculated based on the input.
1033 The algorithm picks first given email address, and takes first letter
1047 The algorithm picks first given email address, and takes first letter
1034 of part before @, and then the first letter of server name. In case
1048 of part before @, and then the first letter of server name. In case
1035 the part before @ is in a format of `somestring.somestring2` it replaces
1049 the part before @ is in a format of `somestring.somestring2` it replaces
1036 the server letter with first letter of somestring2
1050 the server letter with first letter of somestring2
1037
1051
1038 In case function was initialized with both first and lastname, this
1052 In case function was initialized with both first and lastname, this
1039 overrides the extraction from email by first letter of the first and
1053 overrides the extraction from email by first letter of the first and
1040 last name. We add special logic to that functionality, In case Full name
1054 last name. We add special logic to that functionality, In case Full name
1041 is compound, like Guido Von Rossum, we use last part of the last name
1055 is compound, like Guido Von Rossum, we use last part of the last name
1042 (Von Rossum) picking `R`.
1056 (Von Rossum) picking `R`.
1043
1057
1044 Function also normalizes the non-ascii characters to they ascii
1058 Function also normalizes the non-ascii characters to they ascii
1045 representation, eg Ą => A
1059 representation, eg Ą => A
1046 """
1060 """
1047 import unicodedata
1061 import unicodedata
1048 # replace non-ascii to ascii
1062 # replace non-ascii to ascii
1049 first_name = unicodedata.normalize(
1063 first_name = unicodedata.normalize(
1050 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1064 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1051 last_name = unicodedata.normalize(
1065 last_name = unicodedata.normalize(
1052 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1066 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1053
1067
1054 # do NFKD encoding, and also make sure email has proper format
1068 # do NFKD encoding, and also make sure email has proper format
1055 email_address = self.normalize_email(self.email_address)
1069 email_address = self.normalize_email(self.email_address)
1056
1070
1057 # first push the email initials
1071 # first push the email initials
1058 prefix, server = email_address.split('@', 1)
1072 prefix, server = email_address.split('@', 1)
1059
1073
1060 # check if prefix is maybe a 'firstname.lastname' syntax
1074 # check if prefix is maybe a 'firstname.lastname' syntax
1061 _dot_split = prefix.rsplit('.', 1)
1075 _dot_split = prefix.rsplit('.', 1)
1062 if len(_dot_split) == 2:
1076 if len(_dot_split) == 2:
1063 initials = [_dot_split[0][0], _dot_split[1][0]]
1077 initials = [_dot_split[0][0], _dot_split[1][0]]
1064 else:
1078 else:
1065 initials = [prefix[0], server[0]]
1079 initials = [prefix[0], server[0]]
1066
1080
1067 # then try to replace either firtname or lastname
1081 # then try to replace either firtname or lastname
1068 fn_letter = (first_name or " ")[0].strip()
1082 fn_letter = (first_name or " ")[0].strip()
1069 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1083 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1070
1084
1071 if fn_letter:
1085 if fn_letter:
1072 initials[0] = fn_letter
1086 initials[0] = fn_letter
1073
1087
1074 if ln_letter:
1088 if ln_letter:
1075 initials[1] = ln_letter
1089 initials[1] = ln_letter
1076
1090
1077 return ''.join(initials).upper()
1091 return ''.join(initials).upper()
1078
1092
1079 def get_img_data_by_type(self, font_family, img_type):
1093 def get_img_data_by_type(self, font_family, img_type):
1080 default_user = """
1094 default_user = """
1081 <svg xmlns="http://www.w3.org/2000/svg"
1095 <svg xmlns="http://www.w3.org/2000/svg"
1082 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1096 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1083 viewBox="-15 -10 439.165 429.164"
1097 viewBox="-15 -10 439.165 429.164"
1084
1098
1085 xml:space="preserve"
1099 xml:space="preserve"
1086 style="background:{background};" >
1100 style="background:{background};" >
1087
1101
1088 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1102 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1089 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1103 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1090 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1104 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1091 168.596,153.916,216.671,
1105 168.596,153.916,216.671,
1092 204.583,216.671z" fill="{text_color}"/>
1106 204.583,216.671z" fill="{text_color}"/>
1093 <path d="M407.164,374.717L360.88,
1107 <path d="M407.164,374.717L360.88,
1094 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1108 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1095 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1109 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1096 15.366-44.203,23.488-69.076,23.488c-24.877,
1110 15.366-44.203,23.488-69.076,23.488c-24.877,
1097 0-48.762-8.122-69.078-23.488
1111 0-48.762-8.122-69.078-23.488
1098 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1112 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1099 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1113 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1100 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1114 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1101 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1115 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1102 19.402-10.527 C409.699,390.129,
1116 19.402-10.527 C409.699,390.129,
1103 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1117 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1104 </svg>""".format(
1118 </svg>""".format(
1105 size=self.size,
1119 size=self.size,
1106 background='#979797', # @grey4
1120 background='#979797', # @grey4
1107 text_color=self.text_color,
1121 text_color=self.text_color,
1108 font_family=font_family)
1122 font_family=font_family)
1109
1123
1110 return {
1124 return {
1111 "default_user": default_user
1125 "default_user": default_user
1112 }[img_type]
1126 }[img_type]
1113
1127
1114 def get_img_data(self, svg_type=None):
1128 def get_img_data(self, svg_type=None):
1115 """
1129 """
1116 generates the svg metadata for image
1130 generates the svg metadata for image
1117 """
1131 """
1118
1132
1119 font_family = ','.join([
1133 font_family = ','.join([
1120 'proximanovaregular',
1134 'proximanovaregular',
1121 'Proxima Nova Regular',
1135 'Proxima Nova Regular',
1122 'Proxima Nova',
1136 'Proxima Nova',
1123 'Arial',
1137 'Arial',
1124 'Lucida Grande',
1138 'Lucida Grande',
1125 'sans-serif'
1139 'sans-serif'
1126 ])
1140 ])
1127 if svg_type:
1141 if svg_type:
1128 return self.get_img_data_by_type(font_family, svg_type)
1142 return self.get_img_data_by_type(font_family, svg_type)
1129
1143
1130 initials = self.get_initials()
1144 initials = self.get_initials()
1131 img_data = """
1145 img_data = """
1132 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1146 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1133 width="{size}" height="{size}"
1147 width="{size}" height="{size}"
1134 style="width: 100%; height: 100%; background-color: {background}"
1148 style="width: 100%; height: 100%; background-color: {background}"
1135 viewBox="0 0 {size} {size}">
1149 viewBox="0 0 {size} {size}">
1136 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1150 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1137 pointer-events="auto" fill="{text_color}"
1151 pointer-events="auto" fill="{text_color}"
1138 font-family="{font_family}"
1152 font-family="{font_family}"
1139 style="font-weight: 400; font-size: {f_size}px;">{text}
1153 style="font-weight: 400; font-size: {f_size}px;">{text}
1140 </text>
1154 </text>
1141 </svg>""".format(
1155 </svg>""".format(
1142 size=self.size,
1156 size=self.size,
1143 f_size=self.size/1.85, # scale the text inside the box nicely
1157 f_size=self.size/1.85, # scale the text inside the box nicely
1144 background=self.background,
1158 background=self.background,
1145 text_color=self.text_color,
1159 text_color=self.text_color,
1146 text=initials.upper(),
1160 text=initials.upper(),
1147 font_family=font_family)
1161 font_family=font_family)
1148
1162
1149 return img_data
1163 return img_data
1150
1164
1151 def generate_svg(self, svg_type=None):
1165 def generate_svg(self, svg_type=None):
1152 img_data = self.get_img_data(svg_type)
1166 img_data = self.get_img_data(svg_type)
1153 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1167 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1154
1168
1155
1169
1156 def initials_gravatar(email_address, first_name, last_name, size=30):
1170 def initials_gravatar(email_address, first_name, last_name, size=30):
1157 svg_type = None
1171 svg_type = None
1158 if email_address == User.DEFAULT_USER_EMAIL:
1172 if email_address == User.DEFAULT_USER_EMAIL:
1159 svg_type = 'default_user'
1173 svg_type = 'default_user'
1160 klass = InitialsGravatar(email_address, first_name, last_name, size)
1174 klass = InitialsGravatar(email_address, first_name, last_name, size)
1161 return klass.generate_svg(svg_type=svg_type)
1175 return klass.generate_svg(svg_type=svg_type)
1162
1176
1163
1177
1164 def gravatar_url(email_address, size=30):
1178 def gravatar_url(email_address, size=30):
1165 # doh, we need to re-import those to mock it later
1179 # doh, we need to re-import those to mock it later
1166 from pylons import tmpl_context as c
1180 from pylons import tmpl_context as c
1167
1181
1168 _use_gravatar = c.visual.use_gravatar
1182 _use_gravatar = c.visual.use_gravatar
1169 _gravatar_url = c.visual.gravatar_url or User.DEFAULT_GRAVATAR_URL
1183 _gravatar_url = c.visual.gravatar_url or User.DEFAULT_GRAVATAR_URL
1170
1184
1171 email_address = email_address or User.DEFAULT_USER_EMAIL
1185 email_address = email_address or User.DEFAULT_USER_EMAIL
1172 if isinstance(email_address, unicode):
1186 if isinstance(email_address, unicode):
1173 # hashlib crashes on unicode items
1187 # hashlib crashes on unicode items
1174 email_address = safe_str(email_address)
1188 email_address = safe_str(email_address)
1175
1189
1176 # empty email or default user
1190 # empty email or default user
1177 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1191 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1178 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1192 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1179
1193
1180 if _use_gravatar:
1194 if _use_gravatar:
1181 # TODO: Disuse pyramid thread locals. Think about another solution to
1195 # TODO: Disuse pyramid thread locals. Think about another solution to
1182 # get the host and schema here.
1196 # get the host and schema here.
1183 request = get_current_request()
1197 request = get_current_request()
1184 tmpl = safe_str(_gravatar_url)
1198 tmpl = safe_str(_gravatar_url)
1185 tmpl = tmpl.replace('{email}', email_address)\
1199 tmpl = tmpl.replace('{email}', email_address)\
1186 .replace('{md5email}', md5_safe(email_address.lower())) \
1200 .replace('{md5email}', md5_safe(email_address.lower())) \
1187 .replace('{netloc}', request.host)\
1201 .replace('{netloc}', request.host)\
1188 .replace('{scheme}', request.scheme)\
1202 .replace('{scheme}', request.scheme)\
1189 .replace('{size}', safe_str(size))
1203 .replace('{size}', safe_str(size))
1190 return tmpl
1204 return tmpl
1191 else:
1205 else:
1192 return initials_gravatar(email_address, '', '', size=size)
1206 return initials_gravatar(email_address, '', '', size=size)
1193
1207
1194
1208
1195 class Page(_Page):
1209 class Page(_Page):
1196 """
1210 """
1197 Custom pager to match rendering style with paginator
1211 Custom pager to match rendering style with paginator
1198 """
1212 """
1199
1213
1200 def _get_pos(self, cur_page, max_page, items):
1214 def _get_pos(self, cur_page, max_page, items):
1201 edge = (items / 2) + 1
1215 edge = (items / 2) + 1
1202 if (cur_page <= edge):
1216 if (cur_page <= edge):
1203 radius = max(items / 2, items - cur_page)
1217 radius = max(items / 2, items - cur_page)
1204 elif (max_page - cur_page) < edge:
1218 elif (max_page - cur_page) < edge:
1205 radius = (items - 1) - (max_page - cur_page)
1219 radius = (items - 1) - (max_page - cur_page)
1206 else:
1220 else:
1207 radius = items / 2
1221 radius = items / 2
1208
1222
1209 left = max(1, (cur_page - (radius)))
1223 left = max(1, (cur_page - (radius)))
1210 right = min(max_page, cur_page + (radius))
1224 right = min(max_page, cur_page + (radius))
1211 return left, cur_page, right
1225 return left, cur_page, right
1212
1226
1213 def _range(self, regexp_match):
1227 def _range(self, regexp_match):
1214 """
1228 """
1215 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1229 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1216
1230
1217 Arguments:
1231 Arguments:
1218
1232
1219 regexp_match
1233 regexp_match
1220 A "re" (regular expressions) match object containing the
1234 A "re" (regular expressions) match object containing the
1221 radius of linked pages around the current page in
1235 radius of linked pages around the current page in
1222 regexp_match.group(1) as a string
1236 regexp_match.group(1) as a string
1223
1237
1224 This function is supposed to be called as a callable in
1238 This function is supposed to be called as a callable in
1225 re.sub.
1239 re.sub.
1226
1240
1227 """
1241 """
1228 radius = int(regexp_match.group(1))
1242 radius = int(regexp_match.group(1))
1229
1243
1230 # Compute the first and last page number within the radius
1244 # Compute the first and last page number within the radius
1231 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1245 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1232 # -> leftmost_page = 5
1246 # -> leftmost_page = 5
1233 # -> rightmost_page = 9
1247 # -> rightmost_page = 9
1234 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1248 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1235 self.last_page,
1249 self.last_page,
1236 (radius * 2) + 1)
1250 (radius * 2) + 1)
1237 nav_items = []
1251 nav_items = []
1238
1252
1239 # Create a link to the first page (unless we are on the first page
1253 # Create a link to the first page (unless we are on the first page
1240 # or there would be no need to insert '..' spacers)
1254 # or there would be no need to insert '..' spacers)
1241 if self.page != self.first_page and self.first_page < leftmost_page:
1255 if self.page != self.first_page and self.first_page < leftmost_page:
1242 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1256 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1243
1257
1244 # Insert dots if there are pages between the first page
1258 # Insert dots if there are pages between the first page
1245 # and the currently displayed page range
1259 # and the currently displayed page range
1246 if leftmost_page - self.first_page > 1:
1260 if leftmost_page - self.first_page > 1:
1247 # Wrap in a SPAN tag if nolink_attr is set
1261 # Wrap in a SPAN tag if nolink_attr is set
1248 text = '..'
1262 text = '..'
1249 if self.dotdot_attr:
1263 if self.dotdot_attr:
1250 text = HTML.span(c=text, **self.dotdot_attr)
1264 text = HTML.span(c=text, **self.dotdot_attr)
1251 nav_items.append(text)
1265 nav_items.append(text)
1252
1266
1253 for thispage in xrange(leftmost_page, rightmost_page + 1):
1267 for thispage in xrange(leftmost_page, rightmost_page + 1):
1254 # Hilight the current page number and do not use a link
1268 # Hilight the current page number and do not use a link
1255 if thispage == self.page:
1269 if thispage == self.page:
1256 text = '%s' % (thispage,)
1270 text = '%s' % (thispage,)
1257 # Wrap in a SPAN tag if nolink_attr is set
1271 # Wrap in a SPAN tag if nolink_attr is set
1258 if self.curpage_attr:
1272 if self.curpage_attr:
1259 text = HTML.span(c=text, **self.curpage_attr)
1273 text = HTML.span(c=text, **self.curpage_attr)
1260 nav_items.append(text)
1274 nav_items.append(text)
1261 # Otherwise create just a link to that page
1275 # Otherwise create just a link to that page
1262 else:
1276 else:
1263 text = '%s' % (thispage,)
1277 text = '%s' % (thispage,)
1264 nav_items.append(self._pagerlink(thispage, text))
1278 nav_items.append(self._pagerlink(thispage, text))
1265
1279
1266 # Insert dots if there are pages between the displayed
1280 # Insert dots if there are pages between the displayed
1267 # page numbers and the end of the page range
1281 # page numbers and the end of the page range
1268 if self.last_page - rightmost_page > 1:
1282 if self.last_page - rightmost_page > 1:
1269 text = '..'
1283 text = '..'
1270 # Wrap in a SPAN tag if nolink_attr is set
1284 # Wrap in a SPAN tag if nolink_attr is set
1271 if self.dotdot_attr:
1285 if self.dotdot_attr:
1272 text = HTML.span(c=text, **self.dotdot_attr)
1286 text = HTML.span(c=text, **self.dotdot_attr)
1273 nav_items.append(text)
1287 nav_items.append(text)
1274
1288
1275 # Create a link to the very last page (unless we are on the last
1289 # Create a link to the very last page (unless we are on the last
1276 # page or there would be no need to insert '..' spacers)
1290 # page or there would be no need to insert '..' spacers)
1277 if self.page != self.last_page and rightmost_page < self.last_page:
1291 if self.page != self.last_page and rightmost_page < self.last_page:
1278 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1292 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1279
1293
1280 ## prerender links
1294 ## prerender links
1281 #_page_link = url.current()
1295 #_page_link = url.current()
1282 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1296 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1283 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1297 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1284 return self.separator.join(nav_items)
1298 return self.separator.join(nav_items)
1285
1299
1286 def pager(self, format='~2~', page_param='page', partial_param='partial',
1300 def pager(self, format='~2~', page_param='page', partial_param='partial',
1287 show_if_single_page=False, separator=' ', onclick=None,
1301 show_if_single_page=False, separator=' ', onclick=None,
1288 symbol_first='<<', symbol_last='>>',
1302 symbol_first='<<', symbol_last='>>',
1289 symbol_previous='<', symbol_next='>',
1303 symbol_previous='<', symbol_next='>',
1290 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1304 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1291 curpage_attr={'class': 'pager_curpage'},
1305 curpage_attr={'class': 'pager_curpage'},
1292 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1306 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1293
1307
1294 self.curpage_attr = curpage_attr
1308 self.curpage_attr = curpage_attr
1295 self.separator = separator
1309 self.separator = separator
1296 self.pager_kwargs = kwargs
1310 self.pager_kwargs = kwargs
1297 self.page_param = page_param
1311 self.page_param = page_param
1298 self.partial_param = partial_param
1312 self.partial_param = partial_param
1299 self.onclick = onclick
1313 self.onclick = onclick
1300 self.link_attr = link_attr
1314 self.link_attr = link_attr
1301 self.dotdot_attr = dotdot_attr
1315 self.dotdot_attr = dotdot_attr
1302
1316
1303 # Don't show navigator if there is no more than one page
1317 # Don't show navigator if there is no more than one page
1304 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1318 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1305 return ''
1319 return ''
1306
1320
1307 from string import Template
1321 from string import Template
1308 # Replace ~...~ in token format by range of pages
1322 # Replace ~...~ in token format by range of pages
1309 result = re.sub(r'~(\d+)~', self._range, format)
1323 result = re.sub(r'~(\d+)~', self._range, format)
1310
1324
1311 # Interpolate '%' variables
1325 # Interpolate '%' variables
1312 result = Template(result).safe_substitute({
1326 result = Template(result).safe_substitute({
1313 'first_page': self.first_page,
1327 'first_page': self.first_page,
1314 'last_page': self.last_page,
1328 'last_page': self.last_page,
1315 'page': self.page,
1329 'page': self.page,
1316 'page_count': self.page_count,
1330 'page_count': self.page_count,
1317 'items_per_page': self.items_per_page,
1331 'items_per_page': self.items_per_page,
1318 'first_item': self.first_item,
1332 'first_item': self.first_item,
1319 'last_item': self.last_item,
1333 'last_item': self.last_item,
1320 'item_count': self.item_count,
1334 'item_count': self.item_count,
1321 'link_first': self.page > self.first_page and \
1335 'link_first': self.page > self.first_page and \
1322 self._pagerlink(self.first_page, symbol_first) or '',
1336 self._pagerlink(self.first_page, symbol_first) or '',
1323 'link_last': self.page < self.last_page and \
1337 'link_last': self.page < self.last_page and \
1324 self._pagerlink(self.last_page, symbol_last) or '',
1338 self._pagerlink(self.last_page, symbol_last) or '',
1325 'link_previous': self.previous_page and \
1339 'link_previous': self.previous_page and \
1326 self._pagerlink(self.previous_page, symbol_previous) \
1340 self._pagerlink(self.previous_page, symbol_previous) \
1327 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1341 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1328 'link_next': self.next_page and \
1342 'link_next': self.next_page and \
1329 self._pagerlink(self.next_page, symbol_next) \
1343 self._pagerlink(self.next_page, symbol_next) \
1330 or HTML.span(symbol_next, class_="pg-next disabled")
1344 or HTML.span(symbol_next, class_="pg-next disabled")
1331 })
1345 })
1332
1346
1333 return literal(result)
1347 return literal(result)
1334
1348
1335
1349
1336 #==============================================================================
1350 #==============================================================================
1337 # REPO PAGER, PAGER FOR REPOSITORY
1351 # REPO PAGER, PAGER FOR REPOSITORY
1338 #==============================================================================
1352 #==============================================================================
1339 class RepoPage(Page):
1353 class RepoPage(Page):
1340
1354
1341 def __init__(self, collection, page=1, items_per_page=20,
1355 def __init__(self, collection, page=1, items_per_page=20,
1342 item_count=None, url=None, **kwargs):
1356 item_count=None, url=None, **kwargs):
1343
1357
1344 """Create a "RepoPage" instance. special pager for paging
1358 """Create a "RepoPage" instance. special pager for paging
1345 repository
1359 repository
1346 """
1360 """
1347 self._url_generator = url
1361 self._url_generator = url
1348
1362
1349 # Safe the kwargs class-wide so they can be used in the pager() method
1363 # Safe the kwargs class-wide so they can be used in the pager() method
1350 self.kwargs = kwargs
1364 self.kwargs = kwargs
1351
1365
1352 # Save a reference to the collection
1366 # Save a reference to the collection
1353 self.original_collection = collection
1367 self.original_collection = collection
1354
1368
1355 self.collection = collection
1369 self.collection = collection
1356
1370
1357 # The self.page is the number of the current page.
1371 # The self.page is the number of the current page.
1358 # The first page has the number 1!
1372 # The first page has the number 1!
1359 try:
1373 try:
1360 self.page = int(page) # make it int() if we get it as a string
1374 self.page = int(page) # make it int() if we get it as a string
1361 except (ValueError, TypeError):
1375 except (ValueError, TypeError):
1362 self.page = 1
1376 self.page = 1
1363
1377
1364 self.items_per_page = items_per_page
1378 self.items_per_page = items_per_page
1365
1379
1366 # Unless the user tells us how many items the collections has
1380 # Unless the user tells us how many items the collections has
1367 # we calculate that ourselves.
1381 # we calculate that ourselves.
1368 if item_count is not None:
1382 if item_count is not None:
1369 self.item_count = item_count
1383 self.item_count = item_count
1370 else:
1384 else:
1371 self.item_count = len(self.collection)
1385 self.item_count = len(self.collection)
1372
1386
1373 # Compute the number of the first and last available page
1387 # Compute the number of the first and last available page
1374 if self.item_count > 0:
1388 if self.item_count > 0:
1375 self.first_page = 1
1389 self.first_page = 1
1376 self.page_count = int(math.ceil(float(self.item_count) /
1390 self.page_count = int(math.ceil(float(self.item_count) /
1377 self.items_per_page))
1391 self.items_per_page))
1378 self.last_page = self.first_page + self.page_count - 1
1392 self.last_page = self.first_page + self.page_count - 1
1379
1393
1380 # Make sure that the requested page number is the range of
1394 # Make sure that the requested page number is the range of
1381 # valid pages
1395 # valid pages
1382 if self.page > self.last_page:
1396 if self.page > self.last_page:
1383 self.page = self.last_page
1397 self.page = self.last_page
1384 elif self.page < self.first_page:
1398 elif self.page < self.first_page:
1385 self.page = self.first_page
1399 self.page = self.first_page
1386
1400
1387 # Note: the number of items on this page can be less than
1401 # Note: the number of items on this page can be less than
1388 # items_per_page if the last page is not full
1402 # items_per_page if the last page is not full
1389 self.first_item = max(0, (self.item_count) - (self.page *
1403 self.first_item = max(0, (self.item_count) - (self.page *
1390 items_per_page))
1404 items_per_page))
1391 self.last_item = ((self.item_count - 1) - items_per_page *
1405 self.last_item = ((self.item_count - 1) - items_per_page *
1392 (self.page - 1))
1406 (self.page - 1))
1393
1407
1394 self.items = list(self.collection[self.first_item:self.last_item + 1])
1408 self.items = list(self.collection[self.first_item:self.last_item + 1])
1395
1409
1396 # Links to previous and next page
1410 # Links to previous and next page
1397 if self.page > self.first_page:
1411 if self.page > self.first_page:
1398 self.previous_page = self.page - 1
1412 self.previous_page = self.page - 1
1399 else:
1413 else:
1400 self.previous_page = None
1414 self.previous_page = None
1401
1415
1402 if self.page < self.last_page:
1416 if self.page < self.last_page:
1403 self.next_page = self.page + 1
1417 self.next_page = self.page + 1
1404 else:
1418 else:
1405 self.next_page = None
1419 self.next_page = None
1406
1420
1407 # No items available
1421 # No items available
1408 else:
1422 else:
1409 self.first_page = None
1423 self.first_page = None
1410 self.page_count = 0
1424 self.page_count = 0
1411 self.last_page = None
1425 self.last_page = None
1412 self.first_item = None
1426 self.first_item = None
1413 self.last_item = None
1427 self.last_item = None
1414 self.previous_page = None
1428 self.previous_page = None
1415 self.next_page = None
1429 self.next_page = None
1416 self.items = []
1430 self.items = []
1417
1431
1418 # This is a subclass of the 'list' type. Initialise the list now.
1432 # This is a subclass of the 'list' type. Initialise the list now.
1419 list.__init__(self, reversed(self.items))
1433 list.__init__(self, reversed(self.items))
1420
1434
1421
1435
1422 def changed_tooltip(nodes):
1436 def changed_tooltip(nodes):
1423 """
1437 """
1424 Generates a html string for changed nodes in commit page.
1438 Generates a html string for changed nodes in commit page.
1425 It limits the output to 30 entries
1439 It limits the output to 30 entries
1426
1440
1427 :param nodes: LazyNodesGenerator
1441 :param nodes: LazyNodesGenerator
1428 """
1442 """
1429 if nodes:
1443 if nodes:
1430 pref = ': <br/> '
1444 pref = ': <br/> '
1431 suf = ''
1445 suf = ''
1432 if len(nodes) > 30:
1446 if len(nodes) > 30:
1433 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
1447 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
1434 return literal(pref + '<br/> '.join([safe_unicode(x.path)
1448 return literal(pref + '<br/> '.join([safe_unicode(x.path)
1435 for x in nodes[:30]]) + suf)
1449 for x in nodes[:30]]) + suf)
1436 else:
1450 else:
1437 return ': ' + _('No Files')
1451 return ': ' + _('No Files')
1438
1452
1439
1453
1440 def breadcrumb_repo_link(repo):
1454 def breadcrumb_repo_link(repo):
1441 """
1455 """
1442 Makes a breadcrumbs path link to repo
1456 Makes a breadcrumbs path link to repo
1443
1457
1444 ex::
1458 ex::
1445 group >> subgroup >> repo
1459 group >> subgroup >> repo
1446
1460
1447 :param repo: a Repository instance
1461 :param repo: a Repository instance
1448 """
1462 """
1449
1463
1450 path = [
1464 path = [
1451 link_to(group.name, url('repo_group_home', group_name=group.group_name))
1465 link_to(group.name, url('repo_group_home', group_name=group.group_name))
1452 for group in repo.groups_with_parents
1466 for group in repo.groups_with_parents
1453 ] + [
1467 ] + [
1454 link_to(repo.just_name, url('summary_home', repo_name=repo.repo_name))
1468 link_to(repo.just_name, url('summary_home', repo_name=repo.repo_name))
1455 ]
1469 ]
1456
1470
1457 return literal(' &raquo; '.join(path))
1471 return literal(' &raquo; '.join(path))
1458
1472
1459
1473
1460 def format_byte_size_binary(file_size):
1474 def format_byte_size_binary(file_size):
1461 """
1475 """
1462 Formats file/folder sizes to standard.
1476 Formats file/folder sizes to standard.
1463 """
1477 """
1464 formatted_size = format_byte_size(file_size, binary=True)
1478 formatted_size = format_byte_size(file_size, binary=True)
1465 return formatted_size
1479 return formatted_size
1466
1480
1467
1481
1468 def fancy_file_stats(stats):
1482 def fancy_file_stats(stats):
1469 """
1483 """
1470 Displays a fancy two colored bar for number of added/deleted
1484 Displays a fancy two colored bar for number of added/deleted
1471 lines of code on file
1485 lines of code on file
1472
1486
1473 :param stats: two element list of added/deleted lines of code
1487 :param stats: two element list of added/deleted lines of code
1474 """
1488 """
1475 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
1489 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
1476 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
1490 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
1477
1491
1478 def cgen(l_type, a_v, d_v):
1492 def cgen(l_type, a_v, d_v):
1479 mapping = {'tr': 'top-right-rounded-corner-mid',
1493 mapping = {'tr': 'top-right-rounded-corner-mid',
1480 'tl': 'top-left-rounded-corner-mid',
1494 'tl': 'top-left-rounded-corner-mid',
1481 'br': 'bottom-right-rounded-corner-mid',
1495 'br': 'bottom-right-rounded-corner-mid',
1482 'bl': 'bottom-left-rounded-corner-mid'}
1496 'bl': 'bottom-left-rounded-corner-mid'}
1483 map_getter = lambda x: mapping[x]
1497 map_getter = lambda x: mapping[x]
1484
1498
1485 if l_type == 'a' and d_v:
1499 if l_type == 'a' and d_v:
1486 #case when added and deleted are present
1500 #case when added and deleted are present
1487 return ' '.join(map(map_getter, ['tl', 'bl']))
1501 return ' '.join(map(map_getter, ['tl', 'bl']))
1488
1502
1489 if l_type == 'a' and not d_v:
1503 if l_type == 'a' and not d_v:
1490 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1504 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1491
1505
1492 if l_type == 'd' and a_v:
1506 if l_type == 'd' and a_v:
1493 return ' '.join(map(map_getter, ['tr', 'br']))
1507 return ' '.join(map(map_getter, ['tr', 'br']))
1494
1508
1495 if l_type == 'd' and not a_v:
1509 if l_type == 'd' and not a_v:
1496 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1510 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1497
1511
1498 a, d = stats['added'], stats['deleted']
1512 a, d = stats['added'], stats['deleted']
1499 width = 100
1513 width = 100
1500
1514
1501 if stats['binary']: # binary operations like chmod/rename etc
1515 if stats['binary']: # binary operations like chmod/rename etc
1502 lbl = []
1516 lbl = []
1503 bin_op = 0 # undefined
1517 bin_op = 0 # undefined
1504
1518
1505 # prefix with bin for binary files
1519 # prefix with bin for binary files
1506 if BIN_FILENODE in stats['ops']:
1520 if BIN_FILENODE in stats['ops']:
1507 lbl += ['bin']
1521 lbl += ['bin']
1508
1522
1509 if NEW_FILENODE in stats['ops']:
1523 if NEW_FILENODE in stats['ops']:
1510 lbl += [_('new file')]
1524 lbl += [_('new file')]
1511 bin_op = NEW_FILENODE
1525 bin_op = NEW_FILENODE
1512 elif MOD_FILENODE in stats['ops']:
1526 elif MOD_FILENODE in stats['ops']:
1513 lbl += [_('mod')]
1527 lbl += [_('mod')]
1514 bin_op = MOD_FILENODE
1528 bin_op = MOD_FILENODE
1515 elif DEL_FILENODE in stats['ops']:
1529 elif DEL_FILENODE in stats['ops']:
1516 lbl += [_('del')]
1530 lbl += [_('del')]
1517 bin_op = DEL_FILENODE
1531 bin_op = DEL_FILENODE
1518 elif RENAMED_FILENODE in stats['ops']:
1532 elif RENAMED_FILENODE in stats['ops']:
1519 lbl += [_('rename')]
1533 lbl += [_('rename')]
1520 bin_op = RENAMED_FILENODE
1534 bin_op = RENAMED_FILENODE
1521
1535
1522 # chmod can go with other operations, so we add a + to lbl if needed
1536 # chmod can go with other operations, so we add a + to lbl if needed
1523 if CHMOD_FILENODE in stats['ops']:
1537 if CHMOD_FILENODE in stats['ops']:
1524 lbl += [_('chmod')]
1538 lbl += [_('chmod')]
1525 if bin_op == 0:
1539 if bin_op == 0:
1526 bin_op = CHMOD_FILENODE
1540 bin_op = CHMOD_FILENODE
1527
1541
1528 lbl = '+'.join(lbl)
1542 lbl = '+'.join(lbl)
1529 b_a = '<div class="bin bin%s %s" style="width:100%%">%s</div>' \
1543 b_a = '<div class="bin bin%s %s" style="width:100%%">%s</div>' \
1530 % (bin_op, cgen('a', a_v='', d_v=0), lbl)
1544 % (bin_op, cgen('a', a_v='', d_v=0), lbl)
1531 b_d = '<div class="bin bin1" style="width:0%%"></div>'
1545 b_d = '<div class="bin bin1" style="width:0%%"></div>'
1532 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
1546 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
1533
1547
1534 t = stats['added'] + stats['deleted']
1548 t = stats['added'] + stats['deleted']
1535 unit = float(width) / (t or 1)
1549 unit = float(width) / (t or 1)
1536
1550
1537 # needs > 9% of width to be visible or 0 to be hidden
1551 # needs > 9% of width to be visible or 0 to be hidden
1538 a_p = max(9, unit * a) if a > 0 else 0
1552 a_p = max(9, unit * a) if a > 0 else 0
1539 d_p = max(9, unit * d) if d > 0 else 0
1553 d_p = max(9, unit * d) if d > 0 else 0
1540 p_sum = a_p + d_p
1554 p_sum = a_p + d_p
1541
1555
1542 if p_sum > width:
1556 if p_sum > width:
1543 #adjust the percentage to be == 100% since we adjusted to 9
1557 #adjust the percentage to be == 100% since we adjusted to 9
1544 if a_p > d_p:
1558 if a_p > d_p:
1545 a_p = a_p - (p_sum - width)
1559 a_p = a_p - (p_sum - width)
1546 else:
1560 else:
1547 d_p = d_p - (p_sum - width)
1561 d_p = d_p - (p_sum - width)
1548
1562
1549 a_v = a if a > 0 else ''
1563 a_v = a if a > 0 else ''
1550 d_v = d if d > 0 else ''
1564 d_v = d if d > 0 else ''
1551
1565
1552 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
1566 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
1553 cgen('a', a_v, d_v), a_p, a_v
1567 cgen('a', a_v, d_v), a_p, a_v
1554 )
1568 )
1555 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
1569 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
1556 cgen('d', a_v, d_v), d_p, d_v
1570 cgen('d', a_v, d_v), d_p, d_v
1557 )
1571 )
1558 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
1572 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
1559
1573
1560
1574
1561 def urlify_text(text_, safe=True):
1575 def urlify_text(text_, safe=True):
1562 """
1576 """
1563 Extrac urls from text and make html links out of them
1577 Extrac urls from text and make html links out of them
1564
1578
1565 :param text_:
1579 :param text_:
1566 """
1580 """
1567
1581
1568 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1582 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1569 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1583 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1570
1584
1571 def url_func(match_obj):
1585 def url_func(match_obj):
1572 url_full = match_obj.groups()[0]
1586 url_full = match_obj.groups()[0]
1573 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1587 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1574 _newtext = url_pat.sub(url_func, text_)
1588 _newtext = url_pat.sub(url_func, text_)
1575 if safe:
1589 if safe:
1576 return literal(_newtext)
1590 return literal(_newtext)
1577 return _newtext
1591 return _newtext
1578
1592
1579
1593
1580 def urlify_commits(text_, repository):
1594 def urlify_commits(text_, repository):
1581 """
1595 """
1582 Extract commit ids from text and make link from them
1596 Extract commit ids from text and make link from them
1583
1597
1584 :param text_:
1598 :param text_:
1585 :param repository: repo name to build the URL with
1599 :param repository: repo name to build the URL with
1586 """
1600 """
1587 from pylons import url # doh, we need to re-import url to mock it later
1601 from pylons import url # doh, we need to re-import url to mock it later
1588 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1602 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1589
1603
1590 def url_func(match_obj):
1604 def url_func(match_obj):
1591 commit_id = match_obj.groups()[1]
1605 commit_id = match_obj.groups()[1]
1592 pref = match_obj.groups()[0]
1606 pref = match_obj.groups()[0]
1593 suf = match_obj.groups()[2]
1607 suf = match_obj.groups()[2]
1594
1608
1595 tmpl = (
1609 tmpl = (
1596 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1610 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1597 '%(commit_id)s</a>%(suf)s'
1611 '%(commit_id)s</a>%(suf)s'
1598 )
1612 )
1599 return tmpl % {
1613 return tmpl % {
1600 'pref': pref,
1614 'pref': pref,
1601 'cls': 'revision-link',
1615 'cls': 'revision-link',
1602 'url': url('changeset_home', repo_name=repository,
1616 'url': url('changeset_home', repo_name=repository,
1603 revision=commit_id),
1617 revision=commit_id),
1604 'commit_id': commit_id,
1618 'commit_id': commit_id,
1605 'suf': suf
1619 'suf': suf
1606 }
1620 }
1607
1621
1608 newtext = URL_PAT.sub(url_func, text_)
1622 newtext = URL_PAT.sub(url_func, text_)
1609
1623
1610 return newtext
1624 return newtext
1611
1625
1612
1626
1613 def _process_url_func(match_obj, repo_name, uid, entry):
1627 def _process_url_func(match_obj, repo_name, uid, entry):
1614 pref = ''
1628 pref = ''
1615 if match_obj.group().startswith(' '):
1629 if match_obj.group().startswith(' '):
1616 pref = ' '
1630 pref = ' '
1617
1631
1618 issue_id = ''.join(match_obj.groups())
1632 issue_id = ''.join(match_obj.groups())
1619 tmpl = (
1633 tmpl = (
1620 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1634 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1621 '%(issue-prefix)s%(id-repr)s'
1635 '%(issue-prefix)s%(id-repr)s'
1622 '</a>')
1636 '</a>')
1623
1637
1624 (repo_name_cleaned,
1638 (repo_name_cleaned,
1625 parent_group_name) = RepoGroupModel().\
1639 parent_group_name) = RepoGroupModel().\
1626 _get_group_name_and_parent(repo_name)
1640 _get_group_name_and_parent(repo_name)
1627
1641
1628 # variables replacement
1642 # variables replacement
1629 named_vars = {
1643 named_vars = {
1630 'id': issue_id,
1644 'id': issue_id,
1631 'repo': repo_name,
1645 'repo': repo_name,
1632 'repo_name': repo_name_cleaned,
1646 'repo_name': repo_name_cleaned,
1633 'group_name': parent_group_name
1647 'group_name': parent_group_name
1634 }
1648 }
1635 # named regex variables
1649 # named regex variables
1636 named_vars.update(match_obj.groupdict())
1650 named_vars.update(match_obj.groupdict())
1637 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1651 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1638
1652
1639 return tmpl % {
1653 return tmpl % {
1640 'pref': pref,
1654 'pref': pref,
1641 'cls': 'issue-tracker-link',
1655 'cls': 'issue-tracker-link',
1642 'url': _url,
1656 'url': _url,
1643 'id-repr': issue_id,
1657 'id-repr': issue_id,
1644 'issue-prefix': entry['pref'],
1658 'issue-prefix': entry['pref'],
1645 'serv': entry['url'],
1659 'serv': entry['url'],
1646 }
1660 }
1647
1661
1648
1662
1649 def process_patterns(text_string, repo_name, config):
1663 def process_patterns(text_string, repo_name, config):
1650 repo = None
1664 repo = None
1651 if repo_name:
1665 if repo_name:
1652 # Retrieving repo_name to avoid invalid repo_name to explode on
1666 # Retrieving repo_name to avoid invalid repo_name to explode on
1653 # IssueTrackerSettingsModel but still passing invalid name further down
1667 # IssueTrackerSettingsModel but still passing invalid name further down
1654 repo = Repository.get_by_repo_name(repo_name, cache=True)
1668 repo = Repository.get_by_repo_name(repo_name, cache=True)
1655
1669
1656 settings_model = IssueTrackerSettingsModel(repo=repo)
1670 settings_model = IssueTrackerSettingsModel(repo=repo)
1657 active_entries = settings_model.get_settings(cache=True)
1671 active_entries = settings_model.get_settings(cache=True)
1658
1672
1659 newtext = text_string
1673 newtext = text_string
1660 for uid, entry in active_entries.items():
1674 for uid, entry in active_entries.items():
1661 url_func = partial(
1675 url_func = partial(
1662 _process_url_func, repo_name=repo_name, entry=entry, uid=uid)
1676 _process_url_func, repo_name=repo_name, entry=entry, uid=uid)
1663
1677
1664 log.debug('found issue tracker entry with uid %s' % (uid,))
1678 log.debug('found issue tracker entry with uid %s' % (uid,))
1665
1679
1666 if not (entry['pat'] and entry['url']):
1680 if not (entry['pat'] and entry['url']):
1667 log.debug('skipping due to missing data')
1681 log.debug('skipping due to missing data')
1668 continue
1682 continue
1669
1683
1670 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s'
1684 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s'
1671 % (uid, entry['pat'], entry['url'], entry['pref']))
1685 % (uid, entry['pat'], entry['url'], entry['pref']))
1672
1686
1673 try:
1687 try:
1674 pattern = re.compile(r'%s' % entry['pat'])
1688 pattern = re.compile(r'%s' % entry['pat'])
1675 except re.error:
1689 except re.error:
1676 log.exception(
1690 log.exception(
1677 'issue tracker pattern: `%s` failed to compile',
1691 'issue tracker pattern: `%s` failed to compile',
1678 entry['pat'])
1692 entry['pat'])
1679 continue
1693 continue
1680
1694
1681 newtext = pattern.sub(url_func, newtext)
1695 newtext = pattern.sub(url_func, newtext)
1682 log.debug('processed prefix:uid `%s`' % (uid,))
1696 log.debug('processed prefix:uid `%s`' % (uid,))
1683
1697
1684 return newtext
1698 return newtext
1685
1699
1686
1700
1687 def urlify_commit_message(commit_text, repository=None):
1701 def urlify_commit_message(commit_text, repository=None):
1688 """
1702 """
1689 Parses given text message and makes proper links.
1703 Parses given text message and makes proper links.
1690 issues are linked to given issue-server, and rest is a commit link
1704 issues are linked to given issue-server, and rest is a commit link
1691
1705
1692 :param commit_text:
1706 :param commit_text:
1693 :param repository:
1707 :param repository:
1694 """
1708 """
1695 from pylons import url # doh, we need to re-import url to mock it later
1709 from pylons import url # doh, we need to re-import url to mock it later
1696 from rhodecode import CONFIG
1710 from rhodecode import CONFIG
1697
1711
1698 def escaper(string):
1712 def escaper(string):
1699 return string.replace('<', '&lt;').replace('>', '&gt;')
1713 return string.replace('<', '&lt;').replace('>', '&gt;')
1700
1714
1701 newtext = escaper(commit_text)
1715 newtext = escaper(commit_text)
1702 # urlify commits - extract commit ids and make link out of them, if we have
1716 # urlify commits - extract commit ids and make link out of them, if we have
1703 # the scope of repository present.
1717 # the scope of repository present.
1704 if repository:
1718 if repository:
1705 newtext = urlify_commits(newtext, repository)
1719 newtext = urlify_commits(newtext, repository)
1706
1720
1707 # extract http/https links and make them real urls
1721 # extract http/https links and make them real urls
1708 newtext = urlify_text(newtext, safe=False)
1722 newtext = urlify_text(newtext, safe=False)
1709
1723
1710 # process issue tracker patterns
1724 # process issue tracker patterns
1711 newtext = process_patterns(newtext, repository or '', CONFIG)
1725 newtext = process_patterns(newtext, repository or '', CONFIG)
1712
1726
1713 return literal(newtext)
1727 return literal(newtext)
1714
1728
1715
1729
1716 def rst(source, mentions=False):
1730 def rst(source, mentions=False):
1717 return literal('<div class="rst-block">%s</div>' %
1731 return literal('<div class="rst-block">%s</div>' %
1718 MarkupRenderer.rst(source, mentions=mentions))
1732 MarkupRenderer.rst(source, mentions=mentions))
1719
1733
1720
1734
1721 def markdown(source, mentions=False):
1735 def markdown(source, mentions=False):
1722 return literal('<div class="markdown-block">%s</div>' %
1736 return literal('<div class="markdown-block">%s</div>' %
1723 MarkupRenderer.markdown(source, flavored=True,
1737 MarkupRenderer.markdown(source, flavored=True,
1724 mentions=mentions))
1738 mentions=mentions))
1725
1739
1726 def renderer_from_filename(filename, exclude=None):
1740 def renderer_from_filename(filename, exclude=None):
1727 return MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1741 return MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1728
1742
1729
1743
1730 def render(source, renderer='rst', mentions=False):
1744 def render(source, renderer='rst', mentions=False):
1731 if renderer == 'rst':
1745 if renderer == 'rst':
1732 return rst(source, mentions=mentions)
1746 return rst(source, mentions=mentions)
1733 if renderer == 'markdown':
1747 if renderer == 'markdown':
1734 return markdown(source, mentions=mentions)
1748 return markdown(source, mentions=mentions)
1735
1749
1736
1750
1737 def commit_status(repo, commit_id):
1751 def commit_status(repo, commit_id):
1738 return ChangesetStatusModel().get_status(repo, commit_id)
1752 return ChangesetStatusModel().get_status(repo, commit_id)
1739
1753
1740
1754
1741 def commit_status_lbl(commit_status):
1755 def commit_status_lbl(commit_status):
1742 return dict(ChangesetStatus.STATUSES).get(commit_status)
1756 return dict(ChangesetStatus.STATUSES).get(commit_status)
1743
1757
1744
1758
1745 def commit_time(repo_name, commit_id):
1759 def commit_time(repo_name, commit_id):
1746 repo = Repository.get_by_repo_name(repo_name)
1760 repo = Repository.get_by_repo_name(repo_name)
1747 commit = repo.get_commit(commit_id=commit_id)
1761 commit = repo.get_commit(commit_id=commit_id)
1748 return commit.date
1762 return commit.date
1749
1763
1750
1764
1751 def get_permission_name(key):
1765 def get_permission_name(key):
1752 return dict(Permission.PERMS).get(key)
1766 return dict(Permission.PERMS).get(key)
1753
1767
1754
1768
1755 def journal_filter_help():
1769 def journal_filter_help():
1756 return _(
1770 return _(
1757 'Example filter terms:\n' +
1771 'Example filter terms:\n' +
1758 ' repository:vcs\n' +
1772 ' repository:vcs\n' +
1759 ' username:marcin\n' +
1773 ' username:marcin\n' +
1760 ' action:*push*\n' +
1774 ' action:*push*\n' +
1761 ' ip:127.0.0.1\n' +
1775 ' ip:127.0.0.1\n' +
1762 ' date:20120101\n' +
1776 ' date:20120101\n' +
1763 ' date:[20120101100000 TO 20120102]\n' +
1777 ' date:[20120101100000 TO 20120102]\n' +
1764 '\n' +
1778 '\n' +
1765 'Generate wildcards using \'*\' character:\n' +
1779 'Generate wildcards using \'*\' character:\n' +
1766 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1780 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1767 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1781 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1768 '\n' +
1782 '\n' +
1769 'Optional AND / OR operators in queries\n' +
1783 'Optional AND / OR operators in queries\n' +
1770 ' "repository:vcs OR repository:test"\n' +
1784 ' "repository:vcs OR repository:test"\n' +
1771 ' "username:test AND repository:test*"\n'
1785 ' "username:test AND repository:test*"\n'
1772 )
1786 )
1773
1787
1774
1788
1775 def not_mapped_error(repo_name):
1789 def not_mapped_error(repo_name):
1776 flash(_('%s repository is not mapped to db perhaps'
1790 flash(_('%s repository is not mapped to db perhaps'
1777 ' it was created or renamed from the filesystem'
1791 ' it was created or renamed from the filesystem'
1778 ' please run the application again'
1792 ' please run the application again'
1779 ' in order to rescan repositories') % repo_name, category='error')
1793 ' in order to rescan repositories') % repo_name, category='error')
1780
1794
1781
1795
1782 def ip_range(ip_addr):
1796 def ip_range(ip_addr):
1783 from rhodecode.model.db import UserIpMap
1797 from rhodecode.model.db import UserIpMap
1784 s, e = UserIpMap._get_ip_range(ip_addr)
1798 s, e = UserIpMap._get_ip_range(ip_addr)
1785 return '%s - %s' % (s, e)
1799 return '%s - %s' % (s, e)
1786
1800
1787
1801
1788 def form(url, method='post', needs_csrf_token=True, **attrs):
1802 def form(url, method='post', needs_csrf_token=True, **attrs):
1789 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1803 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1790 if method.lower() != 'get' and needs_csrf_token:
1804 if method.lower() != 'get' and needs_csrf_token:
1791 raise Exception(
1805 raise Exception(
1792 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1806 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1793 'CSRF token. If the endpoint does not require such token you can ' +
1807 'CSRF token. If the endpoint does not require such token you can ' +
1794 'explicitly set the parameter needs_csrf_token to false.')
1808 'explicitly set the parameter needs_csrf_token to false.')
1795
1809
1796 return wh_form(url, method=method, **attrs)
1810 return wh_form(url, method=method, **attrs)
1797
1811
1798
1812
1799 def secure_form(url, method="POST", multipart=False, **attrs):
1813 def secure_form(url, method="POST", multipart=False, **attrs):
1800 """Start a form tag that points the action to an url. This
1814 """Start a form tag that points the action to an url. This
1801 form tag will also include the hidden field containing
1815 form tag will also include the hidden field containing
1802 the auth token.
1816 the auth token.
1803
1817
1804 The url options should be given either as a string, or as a
1818 The url options should be given either as a string, or as a
1805 ``url()`` function. The method for the form defaults to POST.
1819 ``url()`` function. The method for the form defaults to POST.
1806
1820
1807 Options:
1821 Options:
1808
1822
1809 ``multipart``
1823 ``multipart``
1810 If set to True, the enctype is set to "multipart/form-data".
1824 If set to True, the enctype is set to "multipart/form-data".
1811 ``method``
1825 ``method``
1812 The method to use when submitting the form, usually either
1826 The method to use when submitting the form, usually either
1813 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1827 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1814 hidden input with name _method is added to simulate the verb
1828 hidden input with name _method is added to simulate the verb
1815 over POST.
1829 over POST.
1816
1830
1817 """
1831 """
1818 from webhelpers.pylonslib.secure_form import insecure_form
1832 from webhelpers.pylonslib.secure_form import insecure_form
1819 from rhodecode.lib.auth import get_csrf_token, csrf_token_key
1833 from rhodecode.lib.auth import get_csrf_token, csrf_token_key
1820 form = insecure_form(url, method, multipart, **attrs)
1834 form = insecure_form(url, method, multipart, **attrs)
1821 token = HTML.div(hidden(csrf_token_key, get_csrf_token()), style="display: none;")
1835 token = HTML.div(hidden(csrf_token_key, get_csrf_token()), style="display: none;")
1822 return literal("%s\n%s" % (form, token))
1836 return literal("%s\n%s" % (form, token))
1823
1837
1824 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1838 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1825 select_html = select(name, selected, options, **attrs)
1839 select_html = select(name, selected, options, **attrs)
1826 select2 = """
1840 select2 = """
1827 <script>
1841 <script>
1828 $(document).ready(function() {
1842 $(document).ready(function() {
1829 $('#%s').select2({
1843 $('#%s').select2({
1830 containerCssClass: 'drop-menu',
1844 containerCssClass: 'drop-menu',
1831 dropdownCssClass: 'drop-menu-dropdown',
1845 dropdownCssClass: 'drop-menu-dropdown',
1832 dropdownAutoWidth: true%s
1846 dropdownAutoWidth: true%s
1833 });
1847 });
1834 });
1848 });
1835 </script>
1849 </script>
1836 """
1850 """
1837 filter_option = """,
1851 filter_option = """,
1838 minimumResultsForSearch: -1
1852 minimumResultsForSearch: -1
1839 """
1853 """
1840 input_id = attrs.get('id') or name
1854 input_id = attrs.get('id') or name
1841 filter_enabled = "" if enable_filter else filter_option
1855 filter_enabled = "" if enable_filter else filter_option
1842 select_script = literal(select2 % (input_id, filter_enabled))
1856 select_script = literal(select2 % (input_id, filter_enabled))
1843
1857
1844 return literal(select_html+select_script)
1858 return literal(select_html+select_script)
1845
1859
1846
1860
1847 def get_visual_attr(tmpl_context_var, attr_name):
1861 def get_visual_attr(tmpl_context_var, attr_name):
1848 """
1862 """
1849 A safe way to get a variable from visual variable of template context
1863 A safe way to get a variable from visual variable of template context
1850
1864
1851 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
1865 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
1852 :param attr_name: name of the attribute we fetch from the c.visual
1866 :param attr_name: name of the attribute we fetch from the c.visual
1853 """
1867 """
1854 visual = getattr(tmpl_context_var, 'visual', None)
1868 visual = getattr(tmpl_context_var, 'visual', None)
1855 if not visual:
1869 if not visual:
1856 return
1870 return
1857 else:
1871 else:
1858 return getattr(visual, attr_name, None)
1872 return getattr(visual, attr_name, None)
1859
1873
1860
1874
1861 def get_last_path_part(file_node):
1875 def get_last_path_part(file_node):
1862 if not file_node.path:
1876 if not file_node.path:
1863 return u''
1877 return u''
1864
1878
1865 path = safe_unicode(file_node.path.split('/')[-1])
1879 path = safe_unicode(file_node.path.split('/')[-1])
1866 return u'../' + path
1880 return u'../' + path
1867
1881
1868
1882
1869 def route_path(*args, **kwds):
1883 def route_path(*args, **kwds):
1870 """
1884 """
1871 Wrapper around pyramids `route_path` function. It is used to generate
1885 Wrapper around pyramids `route_path` function. It is used to generate
1872 URLs from within pylons views or templates. This will be removed when
1886 URLs from within pylons views or templates. This will be removed when
1873 pyramid migration if finished.
1887 pyramid migration if finished.
1874 """
1888 """
1875 req = get_current_request()
1889 req = get_current_request()
1876 return req.route_path(*args, **kwds)
1890 return req.route_path(*args, **kwds)
1877
1891
1878
1892
1879 def resource_path(*args, **kwds):
1893 def resource_path(*args, **kwds):
1880 """
1894 """
1881 Wrapper around pyramids `route_path` function. It is used to generate
1895 Wrapper around pyramids `route_path` function. It is used to generate
1882 URLs from within pylons views or templates. This will be removed when
1896 URLs from within pylons views or templates. This will be removed when
1883 pyramid migration if finished.
1897 pyramid migration if finished.
1884 """
1898 """
1885 req = get_current_request()
1899 req = get_current_request()
1886 return req.resource_path(*args, **kwds)
1900 return req.resource_path(*args, **kwds)
@@ -1,2095 +1,2093 b''
1 //Primary CSS
1 //Primary CSS
2
2
3 //--- IMPORTS ------------------//
3 //--- IMPORTS ------------------//
4
4
5 @import 'helpers';
5 @import 'helpers';
6 @import 'mixins';
6 @import 'mixins';
7 @import 'rcicons';
7 @import 'rcicons';
8 @import 'fonts';
8 @import 'fonts';
9 @import 'variables';
9 @import 'variables';
10 @import 'bootstrap-variables';
10 @import 'bootstrap-variables';
11 @import 'form-bootstrap';
11 @import 'form-bootstrap';
12 @import 'codemirror';
12 @import 'codemirror';
13 @import 'legacy_code_styles';
13 @import 'legacy_code_styles';
14 @import 'progress-bar';
14 @import 'progress-bar';
15
15
16 @import 'type';
16 @import 'type';
17 @import 'alerts';
17 @import 'alerts';
18 @import 'buttons';
18 @import 'buttons';
19 @import 'tags';
19 @import 'tags';
20 @import 'code-block';
20 @import 'code-block';
21 @import 'examples';
21 @import 'examples';
22 @import 'login';
22 @import 'login';
23 @import 'main-content';
23 @import 'main-content';
24 @import 'select2';
24 @import 'select2';
25 @import 'comments';
25 @import 'comments';
26 @import 'panels-bootstrap';
26 @import 'panels-bootstrap';
27 @import 'panels';
27 @import 'panels';
28
28
29
29
30 //--- BASE ------------------//
30 //--- BASE ------------------//
31 .noscript-error {
31 .noscript-error {
32 top: 0;
32 top: 0;
33 left: 0;
33 left: 0;
34 width: 100%;
34 width: 100%;
35 z-index: 101;
35 z-index: 101;
36 text-align: center;
36 text-align: center;
37 font-family: @text-semibold;
37 font-family: @text-semibold;
38 font-size: 120%;
38 font-size: 120%;
39 color: white;
39 color: white;
40 background-color: @alert2;
40 background-color: @alert2;
41 padding: 5px 0 5px 0;
41 padding: 5px 0 5px 0;
42 }
42 }
43
43
44 html {
44 html {
45 display: table;
45 display: table;
46 height: 100%;
46 height: 100%;
47 width: 100%;
47 width: 100%;
48 }
48 }
49
49
50 body {
50 body {
51 display: table-cell;
51 display: table-cell;
52 width: 100%;
52 width: 100%;
53 }
53 }
54
54
55 //--- LAYOUT ------------------//
55 //--- LAYOUT ------------------//
56
56
57 .hidden{
57 .hidden{
58 display: none !important;
58 display: none !important;
59 }
59 }
60
60
61 .box{
61 .box{
62 float: left;
62 float: left;
63 width: 100%;
63 width: 100%;
64 }
64 }
65
65
66 .browser-header {
66 .browser-header {
67 clear: both;
67 clear: both;
68 }
68 }
69 .main {
69 .main {
70 clear: both;
70 clear: both;
71 padding:0 0 @pagepadding;
71 padding:0 0 @pagepadding;
72 height: auto;
72 height: auto;
73
73
74 &:after { //clearfix
74 &:after { //clearfix
75 content:"";
75 content:"";
76 clear:both;
76 clear:both;
77 width:100%;
77 width:100%;
78 display:block;
78 display:block;
79 }
79 }
80 }
80 }
81
81
82 .action-link{
82 .action-link{
83 margin-left: @padding;
83 margin-left: @padding;
84 padding-left: @padding;
84 padding-left: @padding;
85 border-left: @border-thickness solid @border-default-color;
85 border-left: @border-thickness solid @border-default-color;
86 }
86 }
87
87
88 input + .action-link, .action-link.first{
88 input + .action-link, .action-link.first{
89 border-left: none;
89 border-left: none;
90 }
90 }
91
91
92 .action-link.last{
92 .action-link.last{
93 margin-right: @padding;
93 margin-right: @padding;
94 padding-right: @padding;
94 padding-right: @padding;
95 }
95 }
96
96
97 .action-link.active,
97 .action-link.active,
98 .action-link.active a{
98 .action-link.active a{
99 color: @grey4;
99 color: @grey4;
100 }
100 }
101
101
102 ul.simple-list{
102 ul.simple-list{
103 list-style: none;
103 list-style: none;
104 margin: 0;
104 margin: 0;
105 padding: 0;
105 padding: 0;
106 }
106 }
107
107
108 .main-content {
108 .main-content {
109 padding-bottom: @pagepadding;
109 padding-bottom: @pagepadding;
110 }
110 }
111
111
112 .wrapper {
112 .wrapper {
113 position: relative;
113 position: relative;
114 max-width: @wrapper-maxwidth;
114 max-width: @wrapper-maxwidth;
115 margin: 0 auto;
115 margin: 0 auto;
116 }
116 }
117
117
118 #content {
118 #content {
119 clear: both;
119 clear: both;
120 padding: 0 @contentpadding;
120 padding: 0 @contentpadding;
121 }
121 }
122
122
123 .advanced-settings-fields{
123 .advanced-settings-fields{
124 input{
124 input{
125 margin-left: @textmargin;
125 margin-left: @textmargin;
126 margin-right: @padding/2;
126 margin-right: @padding/2;
127 }
127 }
128 }
128 }
129
129
130 .cs_files_title {
130 .cs_files_title {
131 margin: @pagepadding 0 0;
131 margin: @pagepadding 0 0;
132 }
132 }
133
133
134 input.inline[type="file"] {
134 input.inline[type="file"] {
135 display: inline;
135 display: inline;
136 }
136 }
137
137
138 .error_page {
138 .error_page {
139 margin: 10% auto;
139 margin: 10% auto;
140
140
141 h1 {
141 h1 {
142 color: @grey2;
142 color: @grey2;
143 }
143 }
144
144
145 .error-branding {
145 .error-branding {
146 font-family: @text-semibold;
146 font-family: @text-semibold;
147 color: @grey4;
147 color: @grey4;
148 }
148 }
149
149
150 .error_message {
150 .error_message {
151 font-family: @text-regular;
151 font-family: @text-regular;
152 }
152 }
153
153
154 .sidebar {
154 .sidebar {
155 min-height: 275px;
155 min-height: 275px;
156 margin: 0;
156 margin: 0;
157 padding: 0 0 @sidebarpadding @sidebarpadding;
157 padding: 0 0 @sidebarpadding @sidebarpadding;
158 border: none;
158 border: none;
159 }
159 }
160
160
161 .main-content {
161 .main-content {
162 position: relative;
162 position: relative;
163 margin: 0 @sidebarpadding @sidebarpadding;
163 margin: 0 @sidebarpadding @sidebarpadding;
164 padding: 0 0 0 @sidebarpadding;
164 padding: 0 0 0 @sidebarpadding;
165 border-left: @border-thickness solid @grey5;
165 border-left: @border-thickness solid @grey5;
166
166
167 @media (max-width:767px) {
167 @media (max-width:767px) {
168 clear: both;
168 clear: both;
169 width: 100%;
169 width: 100%;
170 margin: 0;
170 margin: 0;
171 border: none;
171 border: none;
172 }
172 }
173 }
173 }
174
174
175 .inner-column {
175 .inner-column {
176 float: left;
176 float: left;
177 width: 29.75%;
177 width: 29.75%;
178 min-height: 150px;
178 min-height: 150px;
179 margin: @sidebarpadding 2% 0 0;
179 margin: @sidebarpadding 2% 0 0;
180 padding: 0 2% 0 0;
180 padding: 0 2% 0 0;
181 border-right: @border-thickness solid @grey5;
181 border-right: @border-thickness solid @grey5;
182
182
183 @media (max-width:767px) {
183 @media (max-width:767px) {
184 clear: both;
184 clear: both;
185 width: 100%;
185 width: 100%;
186 border: none;
186 border: none;
187 }
187 }
188
188
189 ul {
189 ul {
190 padding-left: 1.25em;
190 padding-left: 1.25em;
191 }
191 }
192
192
193 &:last-child {
193 &:last-child {
194 margin: @sidebarpadding 0 0;
194 margin: @sidebarpadding 0 0;
195 border: none;
195 border: none;
196 }
196 }
197
197
198 h4 {
198 h4 {
199 margin: 0 0 @padding;
199 margin: 0 0 @padding;
200 font-family: @text-semibold;
200 font-family: @text-semibold;
201 }
201 }
202 }
202 }
203 }
203 }
204 .error-page-logo {
204 .error-page-logo {
205 width: 130px;
205 width: 130px;
206 height: 160px;
206 height: 160px;
207 }
207 }
208
208
209 // HEADER
209 // HEADER
210 .header {
210 .header {
211
211
212 // TODO: johbo: Fix login pages, so that they work without a min-height
212 // TODO: johbo: Fix login pages, so that they work without a min-height
213 // for the header and then remove the min-height. I chose a smaller value
213 // for the header and then remove the min-height. I chose a smaller value
214 // intentionally here to avoid rendering issues in the main navigation.
214 // intentionally here to avoid rendering issues in the main navigation.
215 min-height: 49px;
215 min-height: 49px;
216
216
217 position: relative;
217 position: relative;
218 vertical-align: bottom;
218 vertical-align: bottom;
219 padding: 0 @header-padding;
219 padding: 0 @header-padding;
220 background-color: @grey2;
220 background-color: @grey2;
221 color: @grey5;
221 color: @grey5;
222
222
223 .title {
223 .title {
224 overflow: visible;
224 overflow: visible;
225 }
225 }
226
226
227 &:before,
227 &:before,
228 &:after {
228 &:after {
229 content: "";
229 content: "";
230 clear: both;
230 clear: both;
231 width: 100%;
231 width: 100%;
232 }
232 }
233
233
234 // TODO: johbo: Avoids breaking "Repositories" chooser
234 // TODO: johbo: Avoids breaking "Repositories" chooser
235 .select2-container .select2-choice .select2-arrow {
235 .select2-container .select2-choice .select2-arrow {
236 display: none;
236 display: none;
237 }
237 }
238 }
238 }
239
239
240 #header-inner {
240 #header-inner {
241 &.title {
241 &.title {
242 margin: 0;
242 margin: 0;
243 }
243 }
244 &:before,
244 &:before,
245 &:after {
245 &:after {
246 content: "";
246 content: "";
247 clear: both;
247 clear: both;
248 }
248 }
249 }
249 }
250
250
251 // Gists
251 // Gists
252 #files_data {
252 #files_data {
253 clear: both; //for firefox
253 clear: both; //for firefox
254 }
254 }
255 #gistid {
255 #gistid {
256 margin-right: @padding;
256 margin-right: @padding;
257 }
257 }
258
258
259 // Global Settings Editor
259 // Global Settings Editor
260 .textarea.editor {
260 .textarea.editor {
261 float: left;
261 float: left;
262 position: relative;
262 position: relative;
263 max-width: @texteditor-width;
263 max-width: @texteditor-width;
264
264
265 select {
265 select {
266 position: absolute;
266 position: absolute;
267 top:10px;
267 top:10px;
268 right:0;
268 right:0;
269 }
269 }
270
270
271 .CodeMirror {
271 .CodeMirror {
272 margin: 0;
272 margin: 0;
273 }
273 }
274
274
275 .help-block {
275 .help-block {
276 margin: 0 0 @padding;
276 margin: 0 0 @padding;
277 padding:.5em;
277 padding:.5em;
278 background-color: @grey6;
278 background-color: @grey6;
279 }
279 }
280 }
280 }
281
281
282 ul.auth_plugins {
282 ul.auth_plugins {
283 margin: @padding 0 @padding @legend-width;
283 margin: @padding 0 @padding @legend-width;
284 padding: 0;
284 padding: 0;
285
285
286 li {
286 li {
287 margin-bottom: @padding;
287 margin-bottom: @padding;
288 line-height: 1em;
288 line-height: 1em;
289 list-style-type: none;
289 list-style-type: none;
290
290
291 .auth_buttons .btn {
291 .auth_buttons .btn {
292 margin-right: @padding;
292 margin-right: @padding;
293 }
293 }
294
294
295 &:before { content: none; }
295 &:before { content: none; }
296 }
296 }
297 }
297 }
298
298
299
299
300 // My Account PR list
300 // My Account PR list
301
301
302 #show_closed {
302 #show_closed {
303 margin: 0 1em 0 0;
303 margin: 0 1em 0 0;
304 }
304 }
305
305
306 .pullrequestlist {
306 .pullrequestlist {
307 .closed {
307 .closed {
308 background-color: @grey6;
308 background-color: @grey6;
309 }
309 }
310 .td-status {
310 .td-status {
311 padding-left: .5em;
311 padding-left: .5em;
312 }
312 }
313 .truncate {
313 .truncate {
314 height: 2.75em;
314 height: 2.75em;
315 white-space: pre-line;
315 white-space: pre-line;
316 }
316 }
317 table.rctable .user {
317 table.rctable .user {
318 padding-left: 0;
318 padding-left: 0;
319 }
319 }
320 }
320 }
321
321
322 // Pull Requests
322 // Pull Requests
323
323
324 .pullrequests_section_head {
324 .pullrequests_section_head {
325 display: block;
325 display: block;
326 clear: both;
326 clear: both;
327 margin: @padding 0;
327 margin: @padding 0;
328 font-family: @text-bold;
328 font-family: @text-bold;
329 }
329 }
330
330
331 .pr-origininfo, .pr-targetinfo {
331 .pr-origininfo, .pr-targetinfo {
332 position: relative;
332 position: relative;
333
333
334 .tag {
334 .tag {
335 display: inline-block;
335 display: inline-block;
336 margin: 0 1em .5em 0;
336 margin: 0 1em .5em 0;
337 }
337 }
338
338
339 .clone-url {
339 .clone-url {
340 display: inline-block;
340 display: inline-block;
341 margin: 0 0 .5em 0;
341 margin: 0 0 .5em 0;
342 padding: 0;
342 padding: 0;
343 line-height: 1.2em;
343 line-height: 1.2em;
344 }
344 }
345 }
345 }
346
346
347 .pr-pullinfo {
347 .pr-pullinfo {
348 clear: both;
348 clear: both;
349 margin: .5em 0;
349 margin: .5em 0;
350 }
350 }
351
351
352 #pr-title-input {
352 #pr-title-input {
353 width: 72%;
353 width: 72%;
354 font-size: 1em;
354 font-size: 1em;
355 font-family: @text-bold;
355 font-family: @text-bold;
356 margin: 0;
356 margin: 0;
357 padding: 0 0 0 @padding/4;
357 padding: 0 0 0 @padding/4;
358 line-height: 1.7em;
358 line-height: 1.7em;
359 color: @text-color;
359 color: @text-color;
360 letter-spacing: .02em;
360 letter-spacing: .02em;
361 }
361 }
362
362
363 #pullrequest_title {
363 #pullrequest_title {
364 width: 100%;
364 width: 100%;
365 box-sizing: border-box;
365 box-sizing: border-box;
366 }
366 }
367
367
368 #pr_open_message {
368 #pr_open_message {
369 border: @border-thickness solid #fff;
369 border: @border-thickness solid #fff;
370 border-radius: @border-radius;
370 border-radius: @border-radius;
371 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
371 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
372 text-align: right;
372 text-align: right;
373 overflow: hidden;
373 overflow: hidden;
374 }
374 }
375
375
376 .pr-submit-button {
376 .pr-submit-button {
377 float: right;
377 float: right;
378 margin: 0 0 0 5px;
378 margin: 0 0 0 5px;
379 }
379 }
380
380
381 .pr-spacing-container {
381 .pr-spacing-container {
382 padding: 20px;
382 padding: 20px;
383 clear: both
383 clear: both
384 }
384 }
385
385
386 #pr-description-input {
386 #pr-description-input {
387 margin-bottom: 0;
387 margin-bottom: 0;
388 }
388 }
389
389
390 .pr-description-label {
390 .pr-description-label {
391 vertical-align: top;
391 vertical-align: top;
392 }
392 }
393
393
394 .perms_section_head {
394 .perms_section_head {
395 min-width: 625px;
395 min-width: 625px;
396
396
397 h2 {
397 h2 {
398 margin-bottom: 0;
398 margin-bottom: 0;
399 }
399 }
400
400
401 .label-checkbox {
401 .label-checkbox {
402 float: left;
402 float: left;
403 }
403 }
404
404
405 &.field {
405 &.field {
406 margin: @space 0 @padding;
406 margin: @space 0 @padding;
407 }
407 }
408
408
409 &:first-child.field {
409 &:first-child.field {
410 margin-top: 0;
410 margin-top: 0;
411
411
412 .label {
412 .label {
413 margin-top: 0;
413 margin-top: 0;
414 padding-top: 0;
414 padding-top: 0;
415 }
415 }
416
416
417 .radios {
417 .radios {
418 padding-top: 0;
418 padding-top: 0;
419 }
419 }
420 }
420 }
421
421
422 .radios {
422 .radios {
423 float: right;
423 float: right;
424 position: relative;
424 position: relative;
425 width: 405px;
425 width: 405px;
426 }
426 }
427 }
427 }
428
428
429 //--- MODULES ------------------//
429 //--- MODULES ------------------//
430
430
431
431
432 // Fixed Sidebar Column
432 // Fixed Sidebar Column
433 .sidebar-col-wrapper {
433 .sidebar-col-wrapper {
434 padding-left: @sidebar-all-width;
434 padding-left: @sidebar-all-width;
435
435
436 .sidebar {
436 .sidebar {
437 width: @sidebar-width;
437 width: @sidebar-width;
438 margin-left: -@sidebar-all-width;
438 margin-left: -@sidebar-all-width;
439 }
439 }
440 }
440 }
441
441
442 .sidebar-col-wrapper.scw-small {
442 .sidebar-col-wrapper.scw-small {
443 padding-left: @sidebar-small-all-width;
443 padding-left: @sidebar-small-all-width;
444
444
445 .sidebar {
445 .sidebar {
446 width: @sidebar-small-width;
446 width: @sidebar-small-width;
447 margin-left: -@sidebar-small-all-width;
447 margin-left: -@sidebar-small-all-width;
448 }
448 }
449 }
449 }
450
450
451
451
452 // FOOTER
452 // FOOTER
453 #footer {
453 #footer {
454 padding: 0;
454 padding: 0;
455 text-align: center;
455 text-align: center;
456 vertical-align: middle;
456 vertical-align: middle;
457 color: @grey2;
457 color: @grey2;
458 background-color: @grey6;
458 background-color: @grey6;
459
459
460 p {
460 p {
461 margin: 0;
461 margin: 0;
462 padding: 1em;
462 padding: 1em;
463 line-height: 1em;
463 line-height: 1em;
464 }
464 }
465
465
466 .server-instance { //server instance
466 .server-instance { //server instance
467 display: none;
467 display: none;
468 }
468 }
469
469
470 .title {
470 .title {
471 float: none;
471 float: none;
472 margin: 0 auto;
472 margin: 0 auto;
473 }
473 }
474 }
474 }
475
475
476 button.close {
476 button.close {
477 padding: 0;
477 padding: 0;
478 cursor: pointer;
478 cursor: pointer;
479 background: transparent;
479 background: transparent;
480 border: 0;
480 border: 0;
481 .box-shadow(none);
481 .box-shadow(none);
482 -webkit-appearance: none;
482 -webkit-appearance: none;
483 }
483 }
484
484
485 .close {
485 .close {
486 float: right;
486 float: right;
487 font-size: 21px;
487 font-size: 21px;
488 font-family: @text-bootstrap;
488 font-family: @text-bootstrap;
489 line-height: 1em;
489 line-height: 1em;
490 font-weight: bold;
490 font-weight: bold;
491 color: @grey2;
491 color: @grey2;
492
492
493 &:hover,
493 &:hover,
494 &:focus {
494 &:focus {
495 color: @grey1;
495 color: @grey1;
496 text-decoration: none;
496 text-decoration: none;
497 cursor: pointer;
497 cursor: pointer;
498 }
498 }
499 }
499 }
500
500
501 // GRID
501 // GRID
502 .sorting,
502 .sorting,
503 .sorting_desc,
503 .sorting_desc,
504 .sorting_asc {
504 .sorting_asc {
505 cursor: pointer;
505 cursor: pointer;
506 }
506 }
507 .sorting_desc:after {
507 .sorting_desc:after {
508 content: "\00A0\25B2";
508 content: "\00A0\25B2";
509 font-size: .75em;
509 font-size: .75em;
510 }
510 }
511 .sorting_asc:after {
511 .sorting_asc:after {
512 content: "\00A0\25BC";
512 content: "\00A0\25BC";
513 font-size: .68em;
513 font-size: .68em;
514 }
514 }
515
515
516
516
517 .user_auth_tokens {
517 .user_auth_tokens {
518
518
519 &.truncate {
519 &.truncate {
520 white-space: nowrap;
520 white-space: nowrap;
521 overflow: hidden;
521 overflow: hidden;
522 text-overflow: ellipsis;
522 text-overflow: ellipsis;
523 }
523 }
524
524
525 .fields .field .input {
525 .fields .field .input {
526 margin: 0;
526 margin: 0;
527 }
527 }
528
528
529 input#description {
529 input#description {
530 width: 100px;
530 width: 100px;
531 margin: 0;
531 margin: 0;
532 }
532 }
533
533
534 .drop-menu {
534 .drop-menu {
535 // TODO: johbo: Remove this, should work out of the box when
535 // TODO: johbo: Remove this, should work out of the box when
536 // having multiple inputs inline
536 // having multiple inputs inline
537 margin: 0 0 0 5px;
537 margin: 0 0 0 5px;
538 }
538 }
539 }
539 }
540 #user_list_table {
540 #user_list_table {
541 .closed {
541 .closed {
542 background-color: @grey6;
542 background-color: @grey6;
543 }
543 }
544 }
544 }
545
545
546
546
547 input {
547 input {
548 &.disabled {
548 &.disabled {
549 opacity: .5;
549 opacity: .5;
550 }
550 }
551 }
551 }
552
552
553 // remove extra padding in firefox
553 // remove extra padding in firefox
554 input::-moz-focus-inner { border:0; padding:0 }
554 input::-moz-focus-inner { border:0; padding:0 }
555
555
556 .adjacent input {
556 .adjacent input {
557 margin-bottom: @padding;
557 margin-bottom: @padding;
558 }
558 }
559
559
560 .permissions_boxes {
560 .permissions_boxes {
561 display: block;
561 display: block;
562 }
562 }
563
563
564 //TODO: lisa: this should be in tables
564 //TODO: lisa: this should be in tables
565 .show_more_col {
565 .show_more_col {
566 width: 20px;
566 width: 20px;
567 }
567 }
568
568
569 //FORMS
569 //FORMS
570
570
571 .medium-inline,
571 .medium-inline,
572 input#description.medium-inline {
572 input#description.medium-inline {
573 display: inline;
573 display: inline;
574 width: @medium-inline-input-width;
574 width: @medium-inline-input-width;
575 min-width: 100px;
575 min-width: 100px;
576 }
576 }
577
577
578 select {
578 select {
579 //reset
579 //reset
580 -webkit-appearance: none;
580 -webkit-appearance: none;
581 -moz-appearance: none;
581 -moz-appearance: none;
582
582
583 display: inline-block;
583 display: inline-block;
584 height: 28px;
584 height: 28px;
585 width: auto;
585 width: auto;
586 margin: 0 @padding @padding 0;
586 margin: 0 @padding @padding 0;
587 padding: 0 18px 0 8px;
587 padding: 0 18px 0 8px;
588 line-height:1em;
588 line-height:1em;
589 font-size: @basefontsize;
589 font-size: @basefontsize;
590 border: @border-thickness solid @rcblue;
590 border: @border-thickness solid @rcblue;
591 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
591 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
592 color: @rcblue;
592 color: @rcblue;
593
593
594 &:after {
594 &:after {
595 content: "\00A0\25BE";
595 content: "\00A0\25BE";
596 }
596 }
597
597
598 &:focus {
598 &:focus {
599 outline: none;
599 outline: none;
600 }
600 }
601 }
601 }
602
602
603 option {
603 option {
604 &:focus {
604 &:focus {
605 outline: none;
605 outline: none;
606 }
606 }
607 }
607 }
608
608
609 input,
609 input,
610 textarea {
610 textarea {
611 padding: @input-padding;
611 padding: @input-padding;
612 border: @input-border-thickness solid @border-highlight-color;
612 border: @input-border-thickness solid @border-highlight-color;
613 .border-radius (@border-radius);
613 .border-radius (@border-radius);
614 font-family: @text-light;
614 font-family: @text-light;
615 font-size: @basefontsize;
615 font-size: @basefontsize;
616
616
617 &.input-sm {
617 &.input-sm {
618 padding: 5px;
618 padding: 5px;
619 }
619 }
620
620
621 &#description {
621 &#description {
622 min-width: @input-description-minwidth;
622 min-width: @input-description-minwidth;
623 min-height: 1em;
623 min-height: 1em;
624 padding: 10px;
624 padding: 10px;
625 }
625 }
626 }
626 }
627
627
628 .field-sm {
628 .field-sm {
629 input,
629 input,
630 textarea {
630 textarea {
631 padding: 5px;
631 padding: 5px;
632 }
632 }
633 }
633 }
634
634
635 textarea {
635 textarea {
636 display: block;
636 display: block;
637 clear: both;
637 clear: both;
638 width: 100%;
638 width: 100%;
639 min-height: 100px;
639 min-height: 100px;
640 margin-bottom: @padding;
640 margin-bottom: @padding;
641 .box-sizing(border-box);
641 .box-sizing(border-box);
642 overflow: auto;
642 overflow: auto;
643 }
643 }
644
644
645 label {
645 label {
646 font-family: @text-light;
646 font-family: @text-light;
647 }
647 }
648
648
649 // GRAVATARS
649 // GRAVATARS
650 // centers gravatar on username to the right
650 // centers gravatar on username to the right
651
651
652 .gravatar {
652 .gravatar {
653 display: inline;
653 display: inline;
654 min-width: 16px;
654 min-width: 16px;
655 min-height: 16px;
655 min-height: 16px;
656 margin: -5px 0;
656 margin: -5px 0;
657 padding: 0;
657 padding: 0;
658 line-height: 1em;
658 line-height: 1em;
659 border: 1px solid @grey4;
659 border: 1px solid @grey4;
660
660
661 &.gravatar-large {
661 &.gravatar-large {
662 margin: -0.5em .25em -0.5em 0;
662 margin: -0.5em .25em -0.5em 0;
663 }
663 }
664
664
665 & + .user {
665 & + .user {
666 display: inline;
666 display: inline;
667 margin: 0;
667 margin: 0;
668 padding: 0 0 0 .17em;
668 padding: 0 0 0 .17em;
669 line-height: 1em;
669 line-height: 1em;
670 }
670 }
671 }
671 }
672
672
673 .user-inline-data {
674 display: inline-block;
675 float: left;
676 padding-left: .5em;
677 line-height: 1.3em;
678 }
679
673 .rc-user { // gravatar + user wrapper
680 .rc-user { // gravatar + user wrapper
674 float: left;
681 float: left;
675 position: relative;
682 position: relative;
676 min-width: 100px;
683 min-width: 100px;
677 max-width: 200px;
684 max-width: 200px;
678 min-height: (@gravatar-size + @border-thickness * 2); // account for border
685 min-height: (@gravatar-size + @border-thickness * 2); // account for border
679 display: block;
686 display: block;
680 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
687 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
681
688
682
689
683 .gravatar {
690 .gravatar {
684 display: block;
691 display: block;
685 position: absolute;
692 position: absolute;
686 top: 0;
693 top: 0;
687 left: 0;
694 left: 0;
688 min-width: @gravatar-size;
695 min-width: @gravatar-size;
689 min-height: @gravatar-size;
696 min-height: @gravatar-size;
690 margin: 0;
697 margin: 0;
691 }
698 }
692
699
693 .user {
700 .user {
694 display: block;
701 display: block;
695 max-width: 175px;
702 max-width: 175px;
696 padding-top: 2px;
703 padding-top: 2px;
697 overflow: hidden;
704 overflow: hidden;
698 text-overflow: ellipsis;
705 text-overflow: ellipsis;
699 }
706 }
700 }
707 }
701
708
702 .gist-gravatar,
709 .gist-gravatar,
703 .journal_container {
710 .journal_container {
704 .gravatar-large {
711 .gravatar-large {
705 margin: 0 .5em -10px 0;
712 margin: 0 .5em -10px 0;
706 }
713 }
707 }
714 }
708
715
709
716
710 // ADMIN SETTINGS
717 // ADMIN SETTINGS
711
718
712 // Tag Patterns
719 // Tag Patterns
713 .tag_patterns {
720 .tag_patterns {
714 .tag_input {
721 .tag_input {
715 margin-bottom: @padding;
722 margin-bottom: @padding;
716 }
723 }
717 }
724 }
718
725
719 .locked_input {
726 .locked_input {
720 position: relative;
727 position: relative;
721
728
722 input {
729 input {
723 display: inline;
730 display: inline;
724 margin-top: 3px;
731 margin-top: 3px;
725 }
732 }
726
733
727 br {
734 br {
728 display: none;
735 display: none;
729 }
736 }
730
737
731 .error-message {
738 .error-message {
732 float: left;
739 float: left;
733 width: 100%;
740 width: 100%;
734 }
741 }
735
742
736 .lock_input_button {
743 .lock_input_button {
737 display: inline;
744 display: inline;
738 }
745 }
739
746
740 .help-block {
747 .help-block {
741 clear: both;
748 clear: both;
742 }
749 }
743 }
750 }
744
751
745 // Notifications
752 // Notifications
746
753
747 .notifications_buttons {
754 .notifications_buttons {
748 margin: 0 0 @space 0;
755 margin: 0 0 @space 0;
749 padding: 0;
756 padding: 0;
750
757
751 .btn {
758 .btn {
752 display: inline-block;
759 display: inline-block;
753 }
760 }
754 }
761 }
755
762
756 .notification-list {
763 .notification-list {
757
764
758 div {
765 div {
759 display: inline-block;
766 display: inline-block;
760 vertical-align: middle;
767 vertical-align: middle;
761 }
768 }
762
769
763 .container {
770 .container {
764 display: block;
771 display: block;
765 margin: 0 0 @padding 0;
772 margin: 0 0 @padding 0;
766 }
773 }
767
774
768 .delete-notifications {
775 .delete-notifications {
769 margin-left: @padding;
776 margin-left: @padding;
770 text-align: right;
777 text-align: right;
771 cursor: pointer;
778 cursor: pointer;
772 }
779 }
773
780
774 .read-notifications {
781 .read-notifications {
775 margin-left: @padding/2;
782 margin-left: @padding/2;
776 text-align: right;
783 text-align: right;
777 width: 35px;
784 width: 35px;
778 cursor: pointer;
785 cursor: pointer;
779 }
786 }
780
787
781 .icon-minus-sign {
788 .icon-minus-sign {
782 color: @alert2;
789 color: @alert2;
783 }
790 }
784
791
785 .icon-ok-sign {
792 .icon-ok-sign {
786 color: @alert1;
793 color: @alert1;
787 }
794 }
788 }
795 }
789
796
790 .user_settings {
797 .user_settings {
791 float: left;
798 float: left;
792 clear: both;
799 clear: both;
793 display: block;
800 display: block;
794 width: 100%;
801 width: 100%;
795
802
796 .gravatar_box {
803 .gravatar_box {
797 margin-bottom: @padding;
804 margin-bottom: @padding;
798
805
799 &:after {
806 &:after {
800 content: " ";
807 content: " ";
801 clear: both;
808 clear: both;
802 width: 100%;
809 width: 100%;
803 }
810 }
804 }
811 }
805
812
806 .fields .field {
813 .fields .field {
807 clear: both;
814 clear: both;
808 }
815 }
809 }
816 }
810
817
811 .advanced_settings {
818 .advanced_settings {
812 margin-bottom: @space;
819 margin-bottom: @space;
813
820
814 .help-block {
821 .help-block {
815 margin-left: 0;
822 margin-left: 0;
816 }
823 }
817
824
818 button + .help-block {
825 button + .help-block {
819 margin-top: @padding;
826 margin-top: @padding;
820 }
827 }
821 }
828 }
822
829
823 // admin settings radio buttons and labels
830 // admin settings radio buttons and labels
824 .label-2 {
831 .label-2 {
825 float: left;
832 float: left;
826 width: @label2-width;
833 width: @label2-width;
827
834
828 label {
835 label {
829 color: @grey1;
836 color: @grey1;
830 }
837 }
831 }
838 }
832 .checkboxes {
839 .checkboxes {
833 float: left;
840 float: left;
834 width: @checkboxes-width;
841 width: @checkboxes-width;
835 margin-bottom: @padding;
842 margin-bottom: @padding;
836
843
837 .checkbox {
844 .checkbox {
838 width: 100%;
845 width: 100%;
839
846
840 label {
847 label {
841 margin: 0;
848 margin: 0;
842 padding: 0;
849 padding: 0;
843 }
850 }
844 }
851 }
845
852
846 .checkbox + .checkbox {
853 .checkbox + .checkbox {
847 display: inline-block;
854 display: inline-block;
848 }
855 }
849
856
850 label {
857 label {
851 margin-right: 1em;
858 margin-right: 1em;
852 }
859 }
853 }
860 }
854
861
855 // CHANGELOG
862 // CHANGELOG
856 .container_header {
863 .container_header {
857 float: left;
864 float: left;
858 display: block;
865 display: block;
859 width: 100%;
866 width: 100%;
860 margin: @padding 0 @padding;
867 margin: @padding 0 @padding;
861
868
862 #filter_changelog {
869 #filter_changelog {
863 float: left;
870 float: left;
864 margin-right: @padding;
871 margin-right: @padding;
865 }
872 }
866
873
867 .breadcrumbs_light {
874 .breadcrumbs_light {
868 display: inline-block;
875 display: inline-block;
869 }
876 }
870 }
877 }
871
878
872 .info_box {
879 .info_box {
873 float: right;
880 float: right;
874 }
881 }
875
882
876
883
877 #graph_nodes {
884 #graph_nodes {
878 padding-top: 43px;
885 padding-top: 43px;
879 }
886 }
880
887
881 #graph_content{
888 #graph_content{
882
889
883 // adjust for table headers so that graph renders properly
890 // adjust for table headers so that graph renders properly
884 // #graph_nodes padding - table cell padding
891 // #graph_nodes padding - table cell padding
885 padding-top: (@space - (@basefontsize * 2.4));
892 padding-top: (@space - (@basefontsize * 2.4));
886
893
887 &.graph_full_width {
894 &.graph_full_width {
888 width: 100%;
895 width: 100%;
889 max-width: 100%;
896 max-width: 100%;
890 }
897 }
891 }
898 }
892
899
893 #graph {
900 #graph {
894 .flag_status {
901 .flag_status {
895 margin: 0;
902 margin: 0;
896 }
903 }
897
904
898 .pagination-left {
905 .pagination-left {
899 float: left;
906 float: left;
900 clear: both;
907 clear: both;
901 }
908 }
902
909
903 .log-container {
910 .log-container {
904 max-width: 345px;
911 max-width: 345px;
905
912
906 .message{
913 .message{
907 max-width: 340px;
914 max-width: 340px;
908 }
915 }
909 }
916 }
910
917
911 .graph-col-wrapper {
918 .graph-col-wrapper {
912 padding-left: 110px;
919 padding-left: 110px;
913
920
914 #graph_nodes {
921 #graph_nodes {
915 width: 100px;
922 width: 100px;
916 margin-left: -110px;
923 margin-left: -110px;
917 float: left;
924 float: left;
918 clear: left;
925 clear: left;
919 }
926 }
920 }
927 }
921 }
928 }
922
929
923 #filter_changelog {
930 #filter_changelog {
924 float: left;
931 float: left;
925 }
932 }
926
933
927
934
928 //--- THEME ------------------//
935 //--- THEME ------------------//
929
936
930 #logo {
937 #logo {
931 float: left;
938 float: left;
932 margin: 9px 0 0 0;
939 margin: 9px 0 0 0;
933
940
934 .header {
941 .header {
935 background-color: transparent;
942 background-color: transparent;
936 }
943 }
937
944
938 a {
945 a {
939 display: inline-block;
946 display: inline-block;
940 }
947 }
941
948
942 img {
949 img {
943 height:30px;
950 height:30px;
944 }
951 }
945 }
952 }
946
953
947 .logo-wrapper {
954 .logo-wrapper {
948 float:left;
955 float:left;
949 }
956 }
950
957
951 .branding{
958 .branding{
952 float: left;
959 float: left;
953 padding: 9px 2px;
960 padding: 9px 2px;
954 line-height: 1em;
961 line-height: 1em;
955 font-size: @navigation-fontsize;
962 font-size: @navigation-fontsize;
956 }
963 }
957
964
958 img {
965 img {
959 border: none;
966 border: none;
960 outline: none;
967 outline: none;
961 }
968 }
962 user-profile-header
969 user-profile-header
963 label {
970 label {
964
971
965 input[type="checkbox"] {
972 input[type="checkbox"] {
966 margin-right: 1em;
973 margin-right: 1em;
967 }
974 }
968 input[type="radio"] {
975 input[type="radio"] {
969 margin-right: 1em;
976 margin-right: 1em;
970 }
977 }
971 }
978 }
972
979
973 .flag_status {
980 .flag_status {
974 margin: 2px 8px 6px 2px;
981 margin: 2px 8px 6px 2px;
975 &.under_review {
982 &.under_review {
976 .circle(5px, @alert3);
983 .circle(5px, @alert3);
977 }
984 }
978 &.approved {
985 &.approved {
979 .circle(5px, @alert1);
986 .circle(5px, @alert1);
980 }
987 }
981 &.rejected,
988 &.rejected,
982 &.forced_closed{
989 &.forced_closed{
983 .circle(5px, @alert2);
990 .circle(5px, @alert2);
984 }
991 }
985 &.not_reviewed {
992 &.not_reviewed {
986 .circle(5px, @grey5);
993 .circle(5px, @grey5);
987 }
994 }
988 }
995 }
989
996
990 .flag_status_comment_box {
997 .flag_status_comment_box {
991 margin: 5px 6px 0px 2px;
998 margin: 5px 6px 0px 2px;
992 }
999 }
993 .test_pattern_preview {
1000 .test_pattern_preview {
994 margin: @space 0;
1001 margin: @space 0;
995
1002
996 p {
1003 p {
997 margin-bottom: 0;
1004 margin-bottom: 0;
998 border-bottom: @border-thickness solid @border-default-color;
1005 border-bottom: @border-thickness solid @border-default-color;
999 color: @grey3;
1006 color: @grey3;
1000 }
1007 }
1001
1008
1002 .btn {
1009 .btn {
1003 margin-bottom: @padding;
1010 margin-bottom: @padding;
1004 }
1011 }
1005 }
1012 }
1006 #test_pattern_result {
1013 #test_pattern_result {
1007 display: none;
1014 display: none;
1008 &:extend(pre);
1015 &:extend(pre);
1009 padding: .9em;
1016 padding: .9em;
1010 color: @grey3;
1017 color: @grey3;
1011 background-color: @grey7;
1018 background-color: @grey7;
1012 border-right: @border-thickness solid @border-default-color;
1019 border-right: @border-thickness solid @border-default-color;
1013 border-bottom: @border-thickness solid @border-default-color;
1020 border-bottom: @border-thickness solid @border-default-color;
1014 border-left: @border-thickness solid @border-default-color;
1021 border-left: @border-thickness solid @border-default-color;
1015 }
1022 }
1016
1023
1017 #repo_vcs_settings {
1024 #repo_vcs_settings {
1018 #inherit_overlay_vcs_default {
1025 #inherit_overlay_vcs_default {
1019 display: none;
1026 display: none;
1020 }
1027 }
1021 #inherit_overlay_vcs_custom {
1028 #inherit_overlay_vcs_custom {
1022 display: custom;
1029 display: custom;
1023 }
1030 }
1024 &.inherited {
1031 &.inherited {
1025 #inherit_overlay_vcs_default {
1032 #inherit_overlay_vcs_default {
1026 display: block;
1033 display: block;
1027 }
1034 }
1028 #inherit_overlay_vcs_custom {
1035 #inherit_overlay_vcs_custom {
1029 display: none;
1036 display: none;
1030 }
1037 }
1031 }
1038 }
1032 }
1039 }
1033
1040
1034 .issue-tracker-link {
1041 .issue-tracker-link {
1035 color: @rcblue;
1042 color: @rcblue;
1036 }
1043 }
1037
1044
1038 // Issue Tracker Table Show/Hide
1045 // Issue Tracker Table Show/Hide
1039 #repo_issue_tracker {
1046 #repo_issue_tracker {
1040 #inherit_overlay {
1047 #inherit_overlay {
1041 display: none;
1048 display: none;
1042 }
1049 }
1043 #custom_overlay {
1050 #custom_overlay {
1044 display: custom;
1051 display: custom;
1045 }
1052 }
1046 &.inherited {
1053 &.inherited {
1047 #inherit_overlay {
1054 #inherit_overlay {
1048 display: block;
1055 display: block;
1049 }
1056 }
1050 #custom_overlay {
1057 #custom_overlay {
1051 display: none;
1058 display: none;
1052 }
1059 }
1053 }
1060 }
1054 }
1061 }
1055 table.issuetracker {
1062 table.issuetracker {
1056 &.readonly {
1063 &.readonly {
1057 tr, td {
1064 tr, td {
1058 color: @grey3;
1065 color: @grey3;
1059 }
1066 }
1060 }
1067 }
1061 .edit {
1068 .edit {
1062 display: none;
1069 display: none;
1063 }
1070 }
1064 .editopen {
1071 .editopen {
1065 .edit {
1072 .edit {
1066 display: inline;
1073 display: inline;
1067 }
1074 }
1068 .entry {
1075 .entry {
1069 display: none;
1076 display: none;
1070 }
1077 }
1071 }
1078 }
1072 tr td.td-action {
1079 tr td.td-action {
1073 min-width: 117px;
1080 min-width: 117px;
1074 }
1081 }
1075 td input {
1082 td input {
1076 max-width: none;
1083 max-width: none;
1077 min-width: 30px;
1084 min-width: 30px;
1078 width: 80%;
1085 width: 80%;
1079 }
1086 }
1080 .issuetracker_pref input {
1087 .issuetracker_pref input {
1081 width: 40%;
1088 width: 40%;
1082 }
1089 }
1083 input.edit_issuetracker_update {
1090 input.edit_issuetracker_update {
1084 margin-right: 0;
1091 margin-right: 0;
1085 width: auto;
1092 width: auto;
1086 }
1093 }
1087 }
1094 }
1088
1095
1089
1096
1090 //Permissions Settings
1097 //Permissions Settings
1091 #add_perm {
1098 #add_perm {
1092 margin: 0 0 @padding;
1099 margin: 0 0 @padding;
1093 cursor: pointer;
1100 cursor: pointer;
1094 }
1101 }
1095
1102
1096 .perm_ac {
1103 .perm_ac {
1097 input {
1104 input {
1098 width: 95%;
1105 width: 95%;
1099 }
1106 }
1100 }
1107 }
1101
1108
1102 .autocomplete-suggestions {
1109 .autocomplete-suggestions {
1103 width: auto !important; // overrides autocomplete.js
1110 width: auto !important; // overrides autocomplete.js
1104 margin: 0;
1111 margin: 0;
1105 border: @border-thickness solid @rcblue;
1112 border: @border-thickness solid @rcblue;
1106 border-radius: @border-radius;
1113 border-radius: @border-radius;
1107 color: @rcblue;
1114 color: @rcblue;
1108 background-color: white;
1115 background-color: white;
1109 }
1116 }
1110 .autocomplete-selected {
1117 .autocomplete-selected {
1111 background: #F0F0F0;
1118 background: #F0F0F0;
1112 }
1119 }
1113 .ac-container-wrap {
1120 .ac-container-wrap {
1114 margin: 0;
1121 margin: 0;
1115 padding: 8px;
1122 padding: 8px;
1116 border-bottom: @border-thickness solid @rclightblue;
1123 border-bottom: @border-thickness solid @rclightblue;
1117 list-style-type: none;
1124 list-style-type: none;
1118 cursor: pointer;
1125 cursor: pointer;
1119
1126
1120 &:hover {
1127 &:hover {
1121 background-color: @rclightblue;
1128 background-color: @rclightblue;
1122 }
1129 }
1123
1130
1124 img {
1131 img {
1125 margin-right: 1em;
1132 margin-right: 1em;
1126 }
1133 }
1127
1134
1128 strong {
1135 strong {
1129 font-weight: normal;
1136 font-weight: normal;
1130 }
1137 }
1131 }
1138 }
1132
1139
1133 // Settings Dropdown
1140 // Settings Dropdown
1134 .user-menu .container {
1141 .user-menu .container {
1135 padding: 0 4px;
1142 padding: 0 4px;
1136 margin: 0;
1143 margin: 0;
1137 }
1144 }
1138
1145
1139 .user-menu .gravatar {
1146 .user-menu .gravatar {
1140 cursor: pointer;
1147 cursor: pointer;
1141 }
1148 }
1142
1149
1143 .codeblock {
1150 .codeblock {
1144 margin-bottom: @padding;
1151 margin-bottom: @padding;
1145 clear: both;
1152 clear: both;
1146
1153
1147 .stats{
1154 .stats{
1148 overflow: hidden;
1155 overflow: hidden;
1149 }
1156 }
1150
1157
1151 .message{
1158 .message{
1152 textarea{
1159 textarea{
1153 margin: 0;
1160 margin: 0;
1154 }
1161 }
1155 }
1162 }
1156
1163
1157 .code-header {
1164 .code-header {
1158 .stats {
1165 .stats {
1159 line-height: 2em;
1166 line-height: 2em;
1160
1167
1161 .revision_id {
1168 .revision_id {
1162 margin-left: 0;
1169 margin-left: 0;
1163 }
1170 }
1164 .buttons {
1171 .buttons {
1165 padding-right: 0;
1172 padding-right: 0;
1166 }
1173 }
1167 }
1174 }
1168
1175
1169 .item{
1176 .item{
1170 margin-right: 0.5em;
1177 margin-right: 0.5em;
1171 }
1178 }
1172 }
1179 }
1173
1180
1174 #editor_container{
1181 #editor_container{
1175 position: relative;
1182 position: relative;
1176 margin: @padding;
1183 margin: @padding;
1177 }
1184 }
1178 }
1185 }
1179
1186
1180 #file_history_container {
1187 #file_history_container {
1181 display: none;
1188 display: none;
1182 }
1189 }
1183
1190
1184 .file-history-inner {
1191 .file-history-inner {
1185 margin-bottom: 10px;
1192 margin-bottom: 10px;
1186 }
1193 }
1187
1194
1188 // Pull Requests
1195 // Pull Requests
1189 .summary-details {
1196 .summary-details {
1190 width: 72%;
1197 width: 72%;
1191 }
1198 }
1192 .pr-summary {
1199 .pr-summary {
1193 border-bottom: @border-thickness solid @grey5;
1200 border-bottom: @border-thickness solid @grey5;
1194 margin-bottom: @space;
1201 margin-bottom: @space;
1195 }
1202 }
1196 .reviewers-title {
1203 .reviewers-title {
1197 width: 25%;
1204 width: 25%;
1198 min-width: 200px;
1205 min-width: 200px;
1199 }
1206 }
1200 .reviewers {
1207 .reviewers {
1201 width: 25%;
1208 width: 25%;
1202 min-width: 200px;
1209 min-width: 200px;
1203 }
1210 }
1204 .reviewers ul li {
1211 .reviewers ul li {
1205 position: relative;
1212 position: relative;
1206 width: 100%;
1213 width: 100%;
1207 margin-bottom: 8px;
1214 margin-bottom: 8px;
1208 }
1215 }
1209 .reviewers_member {
1216 .reviewers_member {
1210 width: 100%;
1217 width: 100%;
1211 overflow: auto;
1218 overflow: auto;
1212 }
1219 }
1213 .reviewer_status {
1220 .reviewer_status {
1214 display: inline-block;
1221 display: inline-block;
1215 vertical-align: top;
1222 vertical-align: top;
1216 width: 7%;
1223 width: 7%;
1217 min-width: 20px;
1224 min-width: 20px;
1218 height: 1.2em;
1225 height: 1.2em;
1219 margin-top: 3px;
1226 margin-top: 3px;
1220 line-height: 1em;
1227 line-height: 1em;
1221 }
1228 }
1222
1229
1223 .reviewer_name {
1230 .reviewer_name {
1224 display: inline-block;
1231 display: inline-block;
1225 max-width: 83%;
1232 max-width: 83%;
1226 padding-right: 20px;
1233 padding-right: 20px;
1227 vertical-align: middle;
1234 vertical-align: middle;
1228 line-height: 1;
1235 line-height: 1;
1229
1236
1230 .rc-user {
1237 .rc-user {
1231 min-width: 0;
1238 min-width: 0;
1232 margin: -2px 1em 0 0;
1239 margin: -2px 1em 0 0;
1233 }
1240 }
1234
1241
1235 .reviewer {
1242 .reviewer {
1236 float: left;
1243 float: left;
1237 }
1244 }
1238
1245
1239 &.to-delete {
1246 &.to-delete {
1240 .user,
1247 .user,
1241 .reviewer {
1248 .reviewer {
1242 text-decoration: line-through;
1249 text-decoration: line-through;
1243 }
1250 }
1244 }
1251 }
1245 }
1252 }
1246
1253
1247 .reviewer_member_remove {
1254 .reviewer_member_remove {
1248 position: absolute;
1255 position: absolute;
1249 right: 0;
1256 right: 0;
1250 top: 0;
1257 top: 0;
1251 width: 16px;
1258 width: 16px;
1252 margin-bottom: 10px;
1259 margin-bottom: 10px;
1253 padding: 0;
1260 padding: 0;
1254 color: black;
1261 color: black;
1255 }
1262 }
1256 .reviewer_member_status {
1263 .reviewer_member_status {
1257 margin-top: 5px;
1264 margin-top: 5px;
1258 }
1265 }
1259 .pr-summary #summary{
1266 .pr-summary #summary{
1260 width: 100%;
1267 width: 100%;
1261 }
1268 }
1262 .pr-summary .action_button:hover {
1269 .pr-summary .action_button:hover {
1263 border: 0;
1270 border: 0;
1264 cursor: pointer;
1271 cursor: pointer;
1265 }
1272 }
1266 .pr-details-title {
1273 .pr-details-title {
1267 padding-bottom: 8px;
1274 padding-bottom: 8px;
1268 border-bottom: @border-thickness solid @grey5;
1275 border-bottom: @border-thickness solid @grey5;
1269 .action_button {
1276 .action_button {
1270 color: @rcblue;
1277 color: @rcblue;
1271 }
1278 }
1272 }
1279 }
1273 .pr-details-content {
1280 .pr-details-content {
1274 margin-top: @textmargin;
1281 margin-top: @textmargin;
1275 margin-bottom: @textmargin;
1282 margin-bottom: @textmargin;
1276 }
1283 }
1277 .pr-description {
1284 .pr-description {
1278 white-space:pre-wrap;
1285 white-space:pre-wrap;
1279 }
1286 }
1280 .group_members {
1287 .group_members {
1281 margin-top: 0;
1288 margin-top: 0;
1282 padding: 0;
1289 padding: 0;
1283 list-style: outside none none;
1290 list-style: outside none none;
1284 }
1291 }
1285 .reviewer_ac .ac-input {
1292 .reviewer_ac .ac-input {
1286 width: 92%;
1293 width: 92%;
1287 margin-bottom: 1em;
1294 margin-bottom: 1em;
1288 }
1295 }
1289 #update_commits {
1296 #update_commits {
1290 float: right;
1297 float: right;
1291 }
1298 }
1292 .compare_view_commits tr{
1299 .compare_view_commits tr{
1293 height: 20px;
1300 height: 20px;
1294 }
1301 }
1295 .compare_view_commits td {
1302 .compare_view_commits td {
1296 vertical-align: top;
1303 vertical-align: top;
1297 padding-top: 10px;
1304 padding-top: 10px;
1298 }
1305 }
1299 .compare_view_commits .author {
1306 .compare_view_commits .author {
1300 margin-left: 5px;
1307 margin-left: 5px;
1301 }
1308 }
1302
1309
1303 .compare_view_files {
1310 .compare_view_files {
1304 width: 100%;
1311 width: 100%;
1305
1312
1306 td {
1313 td {
1307 vertical-align: middle;
1314 vertical-align: middle;
1308 }
1315 }
1309 }
1316 }
1310
1317
1311 .compare_view_filepath {
1318 .compare_view_filepath {
1312 color: @grey1;
1319 color: @grey1;
1313 }
1320 }
1314
1321
1315 .show_more {
1322 .show_more {
1316 display: inline-block;
1323 display: inline-block;
1317 position: relative;
1324 position: relative;
1318 vertical-align: middle;
1325 vertical-align: middle;
1319 width: 4px;
1326 width: 4px;
1320 height: @basefontsize;
1327 height: @basefontsize;
1321
1328
1322 &:after {
1329 &:after {
1323 content: "\00A0\25BE";
1330 content: "\00A0\25BE";
1324 display: inline-block;
1331 display: inline-block;
1325 width:10px;
1332 width:10px;
1326 line-height: 5px;
1333 line-height: 5px;
1327 font-size: 12px;
1334 font-size: 12px;
1328 cursor: pointer;
1335 cursor: pointer;
1329 }
1336 }
1330 }
1337 }
1331
1338
1332 .journal_more .show_more {
1339 .journal_more .show_more {
1333 display: inline;
1340 display: inline;
1334
1341
1335 &:after {
1342 &:after {
1336 content: none;
1343 content: none;
1337 }
1344 }
1338 }
1345 }
1339
1346
1340 .open .show_more:after,
1347 .open .show_more:after,
1341 .select2-dropdown-open .show_more:after {
1348 .select2-dropdown-open .show_more:after {
1342 .rotate(180deg);
1349 .rotate(180deg);
1343 margin-left: 4px;
1350 margin-left: 4px;
1344 }
1351 }
1345
1352
1346
1353
1347 .compare_view_commits .collapse_commit:after {
1354 .compare_view_commits .collapse_commit:after {
1348 cursor: pointer;
1355 cursor: pointer;
1349 content: "\00A0\25B4";
1356 content: "\00A0\25B4";
1350 margin-left: -3px;
1357 margin-left: -3px;
1351 font-size: 17px;
1358 font-size: 17px;
1352 color: @grey4;
1359 color: @grey4;
1353 }
1360 }
1354
1361
1355 .diff_links {
1362 .diff_links {
1356 margin-left: 8px;
1363 margin-left: 8px;
1357 }
1364 }
1358
1365
1359 p.ancestor {
1366 p.ancestor {
1360 margin: @padding 0;
1367 margin: @padding 0;
1361 }
1368 }
1362
1369
1363 .cs_icon_td input[type="checkbox"] {
1370 .cs_icon_td input[type="checkbox"] {
1364 display: none;
1371 display: none;
1365 }
1372 }
1366
1373
1367 .cs_icon_td .expand_file_icon:after {
1374 .cs_icon_td .expand_file_icon:after {
1368 cursor: pointer;
1375 cursor: pointer;
1369 content: "\00A0\25B6";
1376 content: "\00A0\25B6";
1370 font-size: 12px;
1377 font-size: 12px;
1371 color: @grey4;
1378 color: @grey4;
1372 }
1379 }
1373
1380
1374 .cs_icon_td .collapse_file_icon:after {
1381 .cs_icon_td .collapse_file_icon:after {
1375 cursor: pointer;
1382 cursor: pointer;
1376 content: "\00A0\25BC";
1383 content: "\00A0\25BC";
1377 font-size: 12px;
1384 font-size: 12px;
1378 color: @grey4;
1385 color: @grey4;
1379 }
1386 }
1380
1387
1381 /*new binary
1388 /*new binary
1382 NEW_FILENODE = 1
1389 NEW_FILENODE = 1
1383 DEL_FILENODE = 2
1390 DEL_FILENODE = 2
1384 MOD_FILENODE = 3
1391 MOD_FILENODE = 3
1385 RENAMED_FILENODE = 4
1392 RENAMED_FILENODE = 4
1386 COPIED_FILENODE = 5
1393 COPIED_FILENODE = 5
1387 CHMOD_FILENODE = 6
1394 CHMOD_FILENODE = 6
1388 BIN_FILENODE = 7
1395 BIN_FILENODE = 7
1389 */
1396 */
1390 .cs_files_expand {
1397 .cs_files_expand {
1391 font-size: @basefontsize + 5px;
1398 font-size: @basefontsize + 5px;
1392 line-height: 1.8em;
1399 line-height: 1.8em;
1393 float: right;
1400 float: right;
1394 }
1401 }
1395
1402
1396 .cs_files_expand span{
1403 .cs_files_expand span{
1397 color: @rcblue;
1404 color: @rcblue;
1398 cursor: pointer;
1405 cursor: pointer;
1399 }
1406 }
1400 .cs_files {
1407 .cs_files {
1401 clear: both;
1408 clear: both;
1402 padding-bottom: @padding;
1409 padding-bottom: @padding;
1403
1410
1404 .cur_cs {
1411 .cur_cs {
1405 margin: 10px 2px;
1412 margin: 10px 2px;
1406 font-weight: bold;
1413 font-weight: bold;
1407 }
1414 }
1408
1415
1409 .node {
1416 .node {
1410 float: left;
1417 float: left;
1411 }
1418 }
1412
1419
1413 .changes {
1420 .changes {
1414 float: right;
1421 float: right;
1415 color: white;
1422 color: white;
1416 font-size: @basefontsize - 4px;
1423 font-size: @basefontsize - 4px;
1417 margin-top: 4px;
1424 margin-top: 4px;
1418 opacity: 0.6;
1425 opacity: 0.6;
1419 filter: Alpha(opacity=60); /* IE8 and earlier */
1426 filter: Alpha(opacity=60); /* IE8 and earlier */
1420
1427
1421 .added {
1428 .added {
1422 background-color: @alert1;
1429 background-color: @alert1;
1423 float: left;
1430 float: left;
1424 text-align: center;
1431 text-align: center;
1425 }
1432 }
1426
1433
1427 .deleted {
1434 .deleted {
1428 background-color: @alert2;
1435 background-color: @alert2;
1429 float: left;
1436 float: left;
1430 text-align: center;
1437 text-align: center;
1431 }
1438 }
1432
1439
1433 .bin {
1440 .bin {
1434 background-color: @alert1;
1441 background-color: @alert1;
1435 text-align: center;
1442 text-align: center;
1436 }
1443 }
1437
1444
1438 /*new binary*/
1445 /*new binary*/
1439 .bin.bin1 {
1446 .bin.bin1 {
1440 background-color: @alert1;
1447 background-color: @alert1;
1441 text-align: center;
1448 text-align: center;
1442 }
1449 }
1443
1450
1444 /*deleted binary*/
1451 /*deleted binary*/
1445 .bin.bin2 {
1452 .bin.bin2 {
1446 background-color: @alert2;
1453 background-color: @alert2;
1447 text-align: center;
1454 text-align: center;
1448 }
1455 }
1449
1456
1450 /*mod binary*/
1457 /*mod binary*/
1451 .bin.bin3 {
1458 .bin.bin3 {
1452 background-color: @grey2;
1459 background-color: @grey2;
1453 text-align: center;
1460 text-align: center;
1454 }
1461 }
1455
1462
1456 /*rename file*/
1463 /*rename file*/
1457 .bin.bin4 {
1464 .bin.bin4 {
1458 background-color: @alert4;
1465 background-color: @alert4;
1459 text-align: center;
1466 text-align: center;
1460 }
1467 }
1461
1468
1462 /*copied file*/
1469 /*copied file*/
1463 .bin.bin5 {
1470 .bin.bin5 {
1464 background-color: @alert4;
1471 background-color: @alert4;
1465 text-align: center;
1472 text-align: center;
1466 }
1473 }
1467
1474
1468 /*chmod file*/
1475 /*chmod file*/
1469 .bin.bin6 {
1476 .bin.bin6 {
1470 background-color: @grey2;
1477 background-color: @grey2;
1471 text-align: center;
1478 text-align: center;
1472 }
1479 }
1473 }
1480 }
1474 }
1481 }
1475
1482
1476 .cs_files .cs_added, .cs_files .cs_A,
1483 .cs_files .cs_added, .cs_files .cs_A,
1477 .cs_files .cs_added, .cs_files .cs_M,
1484 .cs_files .cs_added, .cs_files .cs_M,
1478 .cs_files .cs_added, .cs_files .cs_D {
1485 .cs_files .cs_added, .cs_files .cs_D {
1479 height: 16px;
1486 height: 16px;
1480 padding-right: 10px;
1487 padding-right: 10px;
1481 margin-top: 7px;
1488 margin-top: 7px;
1482 text-align: left;
1489 text-align: left;
1483 }
1490 }
1484
1491
1485 .cs_icon_td {
1492 .cs_icon_td {
1486 min-width: 16px;
1493 min-width: 16px;
1487 width: 16px;
1494 width: 16px;
1488 }
1495 }
1489
1496
1490 .pull-request-merge {
1497 .pull-request-merge {
1491 padding: 10px 0;
1498 padding: 10px 0;
1492 margin-top: 10px;
1499 margin-top: 10px;
1493 margin-bottom: 20px;
1500 margin-bottom: 20px;
1494 }
1501 }
1495
1502
1496 .pull-request-merge .pull-request-wrap {
1503 .pull-request-merge .pull-request-wrap {
1497 height: 25px;
1504 height: 25px;
1498 padding: 5px 0;
1505 padding: 5px 0;
1499 }
1506 }
1500
1507
1501 .pull-request-merge span {
1508 .pull-request-merge span {
1502 margin-right: 10px;
1509 margin-right: 10px;
1503 }
1510 }
1504 #close_pull_request {
1511 #close_pull_request {
1505 margin-right: 0px;
1512 margin-right: 0px;
1506 }
1513 }
1507
1514
1508 .empty_data {
1515 .empty_data {
1509 color: @grey4;
1516 color: @grey4;
1510 }
1517 }
1511
1518
1512 #changeset_compare_view_content {
1519 #changeset_compare_view_content {
1513 margin-bottom: @space;
1520 margin-bottom: @space;
1514 clear: both;
1521 clear: both;
1515 width: 100%;
1522 width: 100%;
1516 box-sizing: border-box;
1523 box-sizing: border-box;
1517 .border-radius(@border-radius);
1524 .border-radius(@border-radius);
1518
1525
1519 .help-block {
1526 .help-block {
1520 margin: @padding 0;
1527 margin: @padding 0;
1521 color: @text-color;
1528 color: @text-color;
1522 }
1529 }
1523
1530
1524 .empty_data {
1531 .empty_data {
1525 margin: @padding 0;
1532 margin: @padding 0;
1526 }
1533 }
1527
1534
1528 .alert {
1535 .alert {
1529 margin-bottom: @space;
1536 margin-bottom: @space;
1530 }
1537 }
1531 }
1538 }
1532
1539
1533 .table_disp {
1540 .table_disp {
1534 .status {
1541 .status {
1535 width: auto;
1542 width: auto;
1536
1543
1537 .flag_status {
1544 .flag_status {
1538 float: left;
1545 float: left;
1539 }
1546 }
1540 }
1547 }
1541 }
1548 }
1542
1549
1543 .status_box_menu {
1550 .status_box_menu {
1544 margin: 0;
1551 margin: 0;
1545 }
1552 }
1546
1553
1547 .notification-table{
1554 .notification-table{
1548 margin-bottom: @space;
1555 margin-bottom: @space;
1549 display: table;
1556 display: table;
1550 width: 100%;
1557 width: 100%;
1551
1558
1552 .container{
1559 .container{
1553 display: table-row;
1560 display: table-row;
1554
1561
1555 .notification-header{
1562 .notification-header{
1556 border-bottom: @border-thickness solid @border-default-color;
1563 border-bottom: @border-thickness solid @border-default-color;
1557 }
1564 }
1558
1565
1559 .notification-subject{
1566 .notification-subject{
1560 display: table-cell;
1567 display: table-cell;
1561 }
1568 }
1562 }
1569 }
1563 }
1570 }
1564
1571
1565 // Notifications
1572 // Notifications
1566 .notification-header{
1573 .notification-header{
1567 display: table;
1574 display: table;
1568 width: 100%;
1575 width: 100%;
1569 padding: floor(@basefontsize/2) 0;
1576 padding: floor(@basefontsize/2) 0;
1570 line-height: 1em;
1577 line-height: 1em;
1571
1578
1572 .desc, .delete-notifications, .read-notifications{
1579 .desc, .delete-notifications, .read-notifications{
1573 display: table-cell;
1580 display: table-cell;
1574 text-align: left;
1581 text-align: left;
1575 }
1582 }
1576
1583
1577 .desc{
1584 .desc{
1578 width: 1163px;
1585 width: 1163px;
1579 }
1586 }
1580
1587
1581 .delete-notifications, .read-notifications{
1588 .delete-notifications, .read-notifications{
1582 width: 35px;
1589 width: 35px;
1583 min-width: 35px; //fixes when only one button is displayed
1590 min-width: 35px; //fixes when only one button is displayed
1584 }
1591 }
1585 }
1592 }
1586
1593
1587 .notification-body {
1594 .notification-body {
1588 .markdown-block,
1595 .markdown-block,
1589 .rst-block {
1596 .rst-block {
1590 padding: @padding 0;
1597 padding: @padding 0;
1591 }
1598 }
1592
1599
1593 .notification-subject {
1600 .notification-subject {
1594 padding: @textmargin 0;
1601 padding: @textmargin 0;
1595 border-bottom: @border-thickness solid @border-default-color;
1602 border-bottom: @border-thickness solid @border-default-color;
1596 }
1603 }
1597 }
1604 }
1598
1605
1599
1606
1600 .notifications_buttons{
1607 .notifications_buttons{
1601 float: right;
1608 float: right;
1602 }
1609 }
1603
1610
1604 // Repositories
1611 // Repositories
1605
1612
1606 #summary.fields{
1613 #summary.fields{
1607 display: table;
1614 display: table;
1608
1615
1609 .field{
1616 .field{
1610 display: table-row;
1617 display: table-row;
1611
1618
1612 .label-summary{
1619 .label-summary{
1613 display: table-cell;
1620 display: table-cell;
1614 min-width: @label-summary-minwidth;
1621 min-width: @label-summary-minwidth;
1615 padding-top: @padding/2;
1622 padding-top: @padding/2;
1616 padding-bottom: @padding/2;
1623 padding-bottom: @padding/2;
1617 padding-right: @padding/2;
1624 padding-right: @padding/2;
1618 }
1625 }
1619
1626
1620 .input{
1627 .input{
1621 display: table-cell;
1628 display: table-cell;
1622 padding: @padding/2;
1629 padding: @padding/2;
1623
1630
1624 input{
1631 input{
1625 min-width: 29em;
1632 min-width: 29em;
1626 padding: @padding/4;
1633 padding: @padding/4;
1627 }
1634 }
1628 }
1635 }
1629 .statistics, .downloads{
1636 .statistics, .downloads{
1630 .disabled{
1637 .disabled{
1631 color: @grey4;
1638 color: @grey4;
1632 }
1639 }
1633 }
1640 }
1634 }
1641 }
1635 }
1642 }
1636
1643
1637 #summary{
1644 #summary{
1638 width: 70%;
1645 width: 70%;
1639 }
1646 }
1640
1647
1641
1648
1642 // Journal
1649 // Journal
1643 .journal.title {
1650 .journal.title {
1644 h5 {
1651 h5 {
1645 float: left;
1652 float: left;
1646 margin: 0;
1653 margin: 0;
1647 width: 70%;
1654 width: 70%;
1648 }
1655 }
1649
1656
1650 ul {
1657 ul {
1651 float: right;
1658 float: right;
1652 display: inline-block;
1659 display: inline-block;
1653 margin: 0;
1660 margin: 0;
1654 width: 30%;
1661 width: 30%;
1655 text-align: right;
1662 text-align: right;
1656
1663
1657 li {
1664 li {
1658 display: inline;
1665 display: inline;
1659 font-size: @journal-fontsize;
1666 font-size: @journal-fontsize;
1660 line-height: 1em;
1667 line-height: 1em;
1661
1668
1662 &:before { content: none; }
1669 &:before { content: none; }
1663 }
1670 }
1664 }
1671 }
1665 }
1672 }
1666
1673
1667 .filterexample {
1674 .filterexample {
1668 position: absolute;
1675 position: absolute;
1669 top: 95px;
1676 top: 95px;
1670 left: @contentpadding;
1677 left: @contentpadding;
1671 color: @rcblue;
1678 color: @rcblue;
1672 font-size: 11px;
1679 font-size: 11px;
1673 font-family: @text-regular;
1680 font-family: @text-regular;
1674 cursor: help;
1681 cursor: help;
1675
1682
1676 &:hover {
1683 &:hover {
1677 color: @rcdarkblue;
1684 color: @rcdarkblue;
1678 }
1685 }
1679
1686
1680 @media (max-width:768px) {
1687 @media (max-width:768px) {
1681 position: relative;
1688 position: relative;
1682 top: auto;
1689 top: auto;
1683 left: auto;
1690 left: auto;
1684 display: block;
1691 display: block;
1685 }
1692 }
1686 }
1693 }
1687
1694
1688
1695
1689 #journal{
1696 #journal{
1690 margin-bottom: @space;
1697 margin-bottom: @space;
1691
1698
1692 .journal_day{
1699 .journal_day{
1693 margin-bottom: @textmargin/2;
1700 margin-bottom: @textmargin/2;
1694 padding-bottom: @textmargin/2;
1701 padding-bottom: @textmargin/2;
1695 font-size: @journal-fontsize;
1702 font-size: @journal-fontsize;
1696 border-bottom: @border-thickness solid @border-default-color;
1703 border-bottom: @border-thickness solid @border-default-color;
1697 }
1704 }
1698
1705
1699 .journal_container{
1706 .journal_container{
1700 margin-bottom: @space;
1707 margin-bottom: @space;
1701
1708
1702 .journal_user{
1709 .journal_user{
1703 display: inline-block;
1710 display: inline-block;
1704 }
1711 }
1705 .journal_action_container{
1712 .journal_action_container{
1706 display: block;
1713 display: block;
1707 margin-top: @textmargin;
1714 margin-top: @textmargin;
1708
1715
1709 div{
1716 div{
1710 display: inline;
1717 display: inline;
1711 }
1718 }
1712
1719
1713 div.journal_action_params{
1720 div.journal_action_params{
1714 display: block;
1721 display: block;
1715 }
1722 }
1716
1723
1717 div.journal_repo:after{
1724 div.journal_repo:after{
1718 content: "\A";
1725 content: "\A";
1719 white-space: pre;
1726 white-space: pre;
1720 }
1727 }
1721
1728
1722 div.date{
1729 div.date{
1723 display: block;
1730 display: block;
1724 margin-bottom: @textmargin;
1731 margin-bottom: @textmargin;
1725 }
1732 }
1726 }
1733 }
1727 }
1734 }
1728 }
1735 }
1729
1736
1730 // Files
1737 // Files
1731 .edit-file-title {
1738 .edit-file-title {
1732 border-bottom: @border-thickness solid @border-default-color;
1739 border-bottom: @border-thickness solid @border-default-color;
1733
1740
1734 .breadcrumbs {
1741 .breadcrumbs {
1735 margin-bottom: 0;
1742 margin-bottom: 0;
1736 }
1743 }
1737 }
1744 }
1738
1745
1739 .edit-file-fieldset {
1746 .edit-file-fieldset {
1740 margin-top: @sidebarpadding;
1747 margin-top: @sidebarpadding;
1741
1748
1742 .fieldset {
1749 .fieldset {
1743 .left-label {
1750 .left-label {
1744 width: 13%;
1751 width: 13%;
1745 }
1752 }
1746 .right-content {
1753 .right-content {
1747 width: 87%;
1754 width: 87%;
1748 max-width: 100%;
1755 max-width: 100%;
1749 }
1756 }
1750 .filename-label {
1757 .filename-label {
1751 margin-top: 13px;
1758 margin-top: 13px;
1752 }
1759 }
1753 .commit-message-label {
1760 .commit-message-label {
1754 margin-top: 4px;
1761 margin-top: 4px;
1755 }
1762 }
1756 .file-upload-input {
1763 .file-upload-input {
1757 input {
1764 input {
1758 display: none;
1765 display: none;
1759 }
1766 }
1760 }
1767 }
1761 p {
1768 p {
1762 margin-top: 5px;
1769 margin-top: 5px;
1763 }
1770 }
1764
1771
1765 }
1772 }
1766 .custom-path-link {
1773 .custom-path-link {
1767 margin-left: 5px;
1774 margin-left: 5px;
1768 }
1775 }
1769 #commit {
1776 #commit {
1770 resize: vertical;
1777 resize: vertical;
1771 }
1778 }
1772 }
1779 }
1773
1780
1774 .delete-file-preview {
1781 .delete-file-preview {
1775 max-height: 250px;
1782 max-height: 250px;
1776 }
1783 }
1777
1784
1778 .new-file,
1785 .new-file,
1779 #filter_activate,
1786 #filter_activate,
1780 #filter_deactivate {
1787 #filter_deactivate {
1781 float: left;
1788 float: left;
1782 margin: 0 0 0 15px;
1789 margin: 0 0 0 15px;
1783 }
1790 }
1784
1791
1785 h3.files_location{
1792 h3.files_location{
1786 line-height: 2.4em;
1793 line-height: 2.4em;
1787 }
1794 }
1788
1795
1789 .browser-nav {
1796 .browser-nav {
1790 display: table;
1797 display: table;
1791 margin-bottom: @space;
1798 margin-bottom: @space;
1792
1799
1793
1800
1794 .info_box {
1801 .info_box {
1795 display: inline-table;
1802 display: inline-table;
1796 height: 2.5em;
1803 height: 2.5em;
1797
1804
1798 .browser-cur-rev, .info_box_elem {
1805 .browser-cur-rev, .info_box_elem {
1799 display: table-cell;
1806 display: table-cell;
1800 vertical-align: middle;
1807 vertical-align: middle;
1801 }
1808 }
1802
1809
1803 .info_box_elem {
1810 .info_box_elem {
1804 border-top: @border-thickness solid @rcblue;
1811 border-top: @border-thickness solid @rcblue;
1805 border-bottom: @border-thickness solid @rcblue;
1812 border-bottom: @border-thickness solid @rcblue;
1806
1813
1807 #at_rev, a {
1814 #at_rev, a {
1808 padding: 0.6em 0.9em;
1815 padding: 0.6em 0.9em;
1809 margin: 0;
1816 margin: 0;
1810 .box-shadow(none);
1817 .box-shadow(none);
1811 border: 0;
1818 border: 0;
1812 height: 12px;
1819 height: 12px;
1813 }
1820 }
1814
1821
1815 input#at_rev {
1822 input#at_rev {
1816 max-width: 50px;
1823 max-width: 50px;
1817 text-align: right;
1824 text-align: right;
1818 }
1825 }
1819
1826
1820 &.previous {
1827 &.previous {
1821 border: @border-thickness solid @rcblue;
1828 border: @border-thickness solid @rcblue;
1822 .disabled {
1829 .disabled {
1823 color: @grey4;
1830 color: @grey4;
1824 cursor: not-allowed;
1831 cursor: not-allowed;
1825 }
1832 }
1826 }
1833 }
1827
1834
1828 &.next {
1835 &.next {
1829 border: @border-thickness solid @rcblue;
1836 border: @border-thickness solid @rcblue;
1830 .disabled {
1837 .disabled {
1831 color: @grey4;
1838 color: @grey4;
1832 cursor: not-allowed;
1839 cursor: not-allowed;
1833 }
1840 }
1834 }
1841 }
1835 }
1842 }
1836
1843
1837 .browser-cur-rev {
1844 .browser-cur-rev {
1838
1845
1839 span{
1846 span{
1840 margin: 0;
1847 margin: 0;
1841 color: @rcblue;
1848 color: @rcblue;
1842 height: 12px;
1849 height: 12px;
1843 display: inline-block;
1850 display: inline-block;
1844 padding: 0.7em 1em ;
1851 padding: 0.7em 1em ;
1845 border: @border-thickness solid @rcblue;
1852 border: @border-thickness solid @rcblue;
1846 margin-right: @padding;
1853 margin-right: @padding;
1847 }
1854 }
1848 }
1855 }
1849 }
1856 }
1850
1857
1851 .search_activate {
1858 .search_activate {
1852 display: table-cell;
1859 display: table-cell;
1853 vertical-align: middle;
1860 vertical-align: middle;
1854
1861
1855 input, label{
1862 input, label{
1856 margin: 0;
1863 margin: 0;
1857 padding: 0;
1864 padding: 0;
1858 }
1865 }
1859
1866
1860 input{
1867 input{
1861 margin-left: @textmargin;
1868 margin-left: @textmargin;
1862 }
1869 }
1863
1870
1864 }
1871 }
1865 }
1872 }
1866
1873
1867 .file_author{
1868 margin-bottom: @padding;
1869
1870 div{
1871 display: inline-block;
1872 margin-right: 0.5em;
1873 }
1874 }
1875
1876 .browser-cur-rev{
1874 .browser-cur-rev{
1877 margin-bottom: @textmargin;
1875 margin-bottom: @textmargin;
1878 }
1876 }
1879
1877
1880 #node_filter_box_loading{
1878 #node_filter_box_loading{
1881 .info_text;
1879 .info_text;
1882 }
1880 }
1883
1881
1884 .browser-search {
1882 .browser-search {
1885 margin: -25px 0px 5px 0px;
1883 margin: -25px 0px 5px 0px;
1886 }
1884 }
1887
1885
1888 .node-filter {
1886 .node-filter {
1889 font-size: @repo-title-fontsize;
1887 font-size: @repo-title-fontsize;
1890 padding: 4px 0px 0px 0px;
1888 padding: 4px 0px 0px 0px;
1891
1889
1892 .node-filter-path {
1890 .node-filter-path {
1893 float: left;
1891 float: left;
1894 color: @grey4;
1892 color: @grey4;
1895 }
1893 }
1896 .node-filter-input {
1894 .node-filter-input {
1897 float: left;
1895 float: left;
1898 margin: -2px 0px 0px 2px;
1896 margin: -2px 0px 0px 2px;
1899 input {
1897 input {
1900 padding: 2px;
1898 padding: 2px;
1901 border: none;
1899 border: none;
1902 font-size: @repo-title-fontsize;
1900 font-size: @repo-title-fontsize;
1903 }
1901 }
1904 }
1902 }
1905 }
1903 }
1906
1904
1907
1905
1908 .browser-result{
1906 .browser-result{
1909 td a{
1907 td a{
1910 margin-left: 0.5em;
1908 margin-left: 0.5em;
1911 display: inline-block;
1909 display: inline-block;
1912
1910
1913 em{
1911 em{
1914 font-family: @text-bold;
1912 font-family: @text-bold;
1915 }
1913 }
1916 }
1914 }
1917 }
1915 }
1918
1916
1919 .browser-highlight{
1917 .browser-highlight{
1920 background-color: @grey5-alpha;
1918 background-color: @grey5-alpha;
1921 }
1919 }
1922
1920
1923
1921
1924 // Search
1922 // Search
1925
1923
1926 .search-form{
1924 .search-form{
1927 #q {
1925 #q {
1928 width: @search-form-width;
1926 width: @search-form-width;
1929 }
1927 }
1930 .fields{
1928 .fields{
1931 margin: 0 0 @space;
1929 margin: 0 0 @space;
1932 }
1930 }
1933
1931
1934 label{
1932 label{
1935 display: inline-block;
1933 display: inline-block;
1936 margin-right: @textmargin;
1934 margin-right: @textmargin;
1937 padding-top: 0.25em;
1935 padding-top: 0.25em;
1938 }
1936 }
1939
1937
1940
1938
1941 .results{
1939 .results{
1942 clear: both;
1940 clear: both;
1943 margin: 0 0 @padding;
1941 margin: 0 0 @padding;
1944 }
1942 }
1945 }
1943 }
1946
1944
1947 div.search-feedback-items {
1945 div.search-feedback-items {
1948 display: inline-block;
1946 display: inline-block;
1949 padding:0px 0px 0px 96px;
1947 padding:0px 0px 0px 96px;
1950 }
1948 }
1951
1949
1952 div.search-code-body {
1950 div.search-code-body {
1953 background-color: #ffffff; padding: 5px 0 5px 10px;
1951 background-color: #ffffff; padding: 5px 0 5px 10px;
1954 pre {
1952 pre {
1955 .match { background-color: #faffa6;}
1953 .match { background-color: #faffa6;}
1956 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
1954 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
1957 }
1955 }
1958 }
1956 }
1959
1957
1960 .expand_commit.search {
1958 .expand_commit.search {
1961 .show_more.open {
1959 .show_more.open {
1962 height: auto;
1960 height: auto;
1963 max-height: none;
1961 max-height: none;
1964 }
1962 }
1965 }
1963 }
1966
1964
1967 .search-results {
1965 .search-results {
1968
1966
1969 h2 {
1967 h2 {
1970 margin-bottom: 0;
1968 margin-bottom: 0;
1971 }
1969 }
1972 .codeblock {
1970 .codeblock {
1973 border: none;
1971 border: none;
1974 background: transparent;
1972 background: transparent;
1975 }
1973 }
1976
1974
1977 .codeblock-header {
1975 .codeblock-header {
1978 border: none;
1976 border: none;
1979 background: transparent;
1977 background: transparent;
1980 }
1978 }
1981
1979
1982 .code-body {
1980 .code-body {
1983 border: @border-thickness solid @border-default-color;
1981 border: @border-thickness solid @border-default-color;
1984 .border-radius(@border-radius);
1982 .border-radius(@border-radius);
1985 }
1983 }
1986
1984
1987 .td-commit {
1985 .td-commit {
1988 &:extend(pre);
1986 &:extend(pre);
1989 border-bottom: @border-thickness solid @border-default-color;
1987 border-bottom: @border-thickness solid @border-default-color;
1990 }
1988 }
1991
1989
1992 .message {
1990 .message {
1993 height: auto;
1991 height: auto;
1994 max-width: 350px;
1992 max-width: 350px;
1995 white-space: normal;
1993 white-space: normal;
1996 text-overflow: initial;
1994 text-overflow: initial;
1997 overflow: visible;
1995 overflow: visible;
1998
1996
1999 .match { background-color: #faffa6;}
1997 .match { background-color: #faffa6;}
2000 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
1998 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2001 }
1999 }
2002
2000
2003 }
2001 }
2004
2002
2005 table.rctable td.td-search-results div {
2003 table.rctable td.td-search-results div {
2006 max-width: 100%;
2004 max-width: 100%;
2007 }
2005 }
2008
2006
2009 #tip-box, .tip-box{
2007 #tip-box, .tip-box{
2010 padding: @menupadding/2;
2008 padding: @menupadding/2;
2011 display: block;
2009 display: block;
2012 border: @border-thickness solid @border-highlight-color;
2010 border: @border-thickness solid @border-highlight-color;
2013 .border-radius(@border-radius);
2011 .border-radius(@border-radius);
2014 background-color: white;
2012 background-color: white;
2015 z-index: 99;
2013 z-index: 99;
2016 white-space: pre-wrap;
2014 white-space: pre-wrap;
2017 }
2015 }
2018
2016
2019 #linktt {
2017 #linktt {
2020 width: 79px;
2018 width: 79px;
2021 }
2019 }
2022
2020
2023 #help_kb .modal-content{
2021 #help_kb .modal-content{
2024 max-width: 750px;
2022 max-width: 750px;
2025 margin: 10% auto;
2023 margin: 10% auto;
2026
2024
2027 table{
2025 table{
2028 td,th{
2026 td,th{
2029 border-bottom: none;
2027 border-bottom: none;
2030 line-height: 2.5em;
2028 line-height: 2.5em;
2031 }
2029 }
2032 th{
2030 th{
2033 padding-bottom: @textmargin/2;
2031 padding-bottom: @textmargin/2;
2034 }
2032 }
2035 td.keys{
2033 td.keys{
2036 text-align: center;
2034 text-align: center;
2037 }
2035 }
2038 }
2036 }
2039
2037
2040 .block-left{
2038 .block-left{
2041 width: 45%;
2039 width: 45%;
2042 margin-right: 5%;
2040 margin-right: 5%;
2043 }
2041 }
2044 .modal-footer{
2042 .modal-footer{
2045 clear: both;
2043 clear: both;
2046 }
2044 }
2047 .key.tag{
2045 .key.tag{
2048 padding: 0.5em;
2046 padding: 0.5em;
2049 background-color: @rcblue;
2047 background-color: @rcblue;
2050 color: white;
2048 color: white;
2051 border-color: @rcblue;
2049 border-color: @rcblue;
2052 .box-shadow(none);
2050 .box-shadow(none);
2053 }
2051 }
2054 }
2052 }
2055
2053
2056
2054
2057
2055
2058 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2056 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2059
2057
2060 @import 'statistics-graph';
2058 @import 'statistics-graph';
2061 @import 'tables';
2059 @import 'tables';
2062 @import 'forms';
2060 @import 'forms';
2063 @import 'diff';
2061 @import 'diff';
2064 @import 'summary';
2062 @import 'summary';
2065 @import 'navigation';
2063 @import 'navigation';
2066
2064
2067 //--- SHOW/HIDE SECTIONS --//
2065 //--- SHOW/HIDE SECTIONS --//
2068
2066
2069 .btn-collapse {
2067 .btn-collapse {
2070 float: right;
2068 float: right;
2071 text-align: right;
2069 text-align: right;
2072 font-family: @text-light;
2070 font-family: @text-light;
2073 font-size: @basefontsize;
2071 font-size: @basefontsize;
2074 cursor: pointer;
2072 cursor: pointer;
2075 border: none;
2073 border: none;
2076 color: @rcblue;
2074 color: @rcblue;
2077 }
2075 }
2078
2076
2079 table.rctable,
2077 table.rctable,
2080 table.dataTable {
2078 table.dataTable {
2081 .btn-collapse {
2079 .btn-collapse {
2082 float: right;
2080 float: right;
2083 text-align: right;
2081 text-align: right;
2084 }
2082 }
2085 }
2083 }
2086
2084
2087
2085
2088 // TODO: johbo: Fix for IE10, this avoids that we see a border
2086 // TODO: johbo: Fix for IE10, this avoids that we see a border
2089 // and padding around checkboxes and radio boxes. Move to the right place,
2087 // and padding around checkboxes and radio boxes. Move to the right place,
2090 // or better: Remove this once we did the form refactoring.
2088 // or better: Remove this once we did the form refactoring.
2091 input[type=checkbox],
2089 input[type=checkbox],
2092 input[type=radio] {
2090 input[type=radio] {
2093 padding: 0;
2091 padding: 0;
2094 border: none;
2092 border: none;
2095 }
2093 }
@@ -1,256 +1,260 b''
1 // summary.less
1 // summary.less
2 // For use in RhodeCode applications;
2 // For use in RhodeCode applications;
3 // Used for headers and file detail summary screens.
3 // Used for headers and file detail summary screens.
4
4
5 .summary {
5 .summary {
6 float: left;
6 float: left;
7 position: relative;
7 position: relative;
8 width: 100%;
8 width: 100%;
9 margin: 0;
9 margin: 0;
10 padding: 0;
10 padding: 0;
11
11
12 .summary-detail-header {
12 .summary-detail-header {
13 float: left;
13 float: left;
14 display: block;
14 display: block;
15 width: 100%;
15 width: 100%;
16 margin-bottom: @textmargin;
16 margin-bottom: @textmargin;
17 padding: 0 0 .5em 0;
17 padding: 0 0 .5em 0;
18 border-bottom: @border-thickness solid @border-default-color;
18 border-bottom: @border-thickness solid @border-default-color;
19
19
20 .breadcrumbs {
20 .breadcrumbs {
21 float: left;
21 float: left;
22 display: inline;
22 display: inline;
23 margin: 0;
23 margin: 0;
24 padding: 0;
24 padding: 0;
25 }
25 }
26 h4 {
26 h4 {
27 float: left;
27 float: left;
28 margin: 0 1em 0 0;
28 margin: 0 1em 0 0;
29 padding: 0;
29 padding: 0;
30 line-height: 1.2em;
30 line-height: 1.2em;
31 font-size: @basefontsize;
31 font-size: @basefontsize;
32 }
32 }
33
33
34 .action_link {
34 .action_link {
35 float: right;
35 float: right;
36 }
36 }
37
37
38 .new-file {
38 .new-file {
39 float: right;
39 float: right;
40 margin-top: -1.5em;
40 margin-top: -1.5em;
41 }
41 }
42 }
42 }
43
43
44 .summary-detail {
44 .summary-detail {
45 float: left;
45 float: left;
46 position: relative;
46 position: relative;
47 width: 73%;
47 width: 73%;
48 margin: 0 3% @space 0;
48 margin: 0 3% @space 0;
49 padding: 0;
49 padding: 0;
50
50
51 .file_diff_buttons {
51 .file_diff_buttons {
52 margin-top: @space;
52 margin-top: @space;
53 }
53 }
54
54
55 // commit message
55 // commit message
56 .commit {
56 .commit {
57 white-space: pre-wrap;
57 white-space: pre-wrap;
58 }
58 }
59
59
60 #clone_url,
60 #clone_url,
61 #clone_url_id {
61 #clone_url_id {
62 min-width: 29em;
62 min-width: 29em;
63 padding: @padding/4;
63 padding: @padding/4;
64 }
64 }
65
65
66 &.directory {
66 &.directory {
67 margin-bottom: 0;
67 margin-bottom: 0;
68 }
68 }
69
69
70 .desc {
70 .desc {
71 white-space: pre-wrap;
71 white-space: pre-wrap;
72 }
72 }
73 .disabled {
73 .disabled {
74 opacity: .5;
74 opacity: .5;
75 }
75 }
76 .help-block {
76 .help-block {
77 color: inherit;
77 color: inherit;
78 margin: 0;
78 margin: 0;
79 }
79 }
80 }
80 }
81
81
82 .sidebar-right {
82 .sidebar-right {
83 float: left;
83 float: left;
84 width: 24%;
84 width: 24%;
85 margin: 0;
85 margin: 0;
86 padding: 0;
86 padding: 0;
87
87
88 ul {
88 ul {
89 margin-left: 0;
89 margin-left: 0;
90 padding-left: 0;
90 padding-left: 0;
91
91
92 li {
92 li {
93
93
94 &:before {
94 &:before {
95 content: none;
95 content: none;
96 width: 0;
96 width: 0;
97 }
97 }
98 }
98 }
99 }
99 }
100 }
100 }
101
101
102 #clone_by_name, #clone_by_id{
102 #clone_by_name, #clone_by_id{
103 display: inline-block;
103 display: inline-block;
104 margin-left: @padding;
104 margin-left: @padding;
105 }
105 }
106
106
107 .codeblock {
107 .codeblock {
108 border: none;
108 border: none;
109 background-color: transparent;
109 background-color: transparent;
110 }
110 }
111
111
112 .code-body {
112 .code-body {
113 border: @border-thickness solid @border-default-color;
113 border: @border-thickness solid @border-default-color;
114 .border-radius(@border-radius);
114 .border-radius(@border-radius);
115 }
115 }
116 }
116 }
117
117
118 // this is used outside of just the summary
118 // this is used outside of just the summary
119 .fieldset, // similar to form fieldset
119 .fieldset, // similar to form fieldset
120 .summary .sidebar-right-content { // these have to match
120 .summary .sidebar-right-content { // these have to match
121 clear: both;
121 clear: both;
122 float: left;
122 float: left;
123 position: relative;
123 position: relative;
124 display:block;
124 display:block;
125 width: 100%;
125 width: 100%;
126 min-height: 1em;
126 min-height: 1em;
127 margin-bottom: @textmargin;
127 margin-bottom: @textmargin;
128 padding: 0;
128 padding: 0;
129 line-height: 1.2em;
129 line-height: 1.2em;
130
130
131 &:after { // clearfix
131 &:after { // clearfix
132 content: "";
132 content: "";
133 clear: both;
133 clear: both;
134 width: 100%;
134 width: 100%;
135 height: 1em;
135 height: 1em;
136 }
136 }
137 }
137 }
138
138
139 .summary .sidebar-right-content {
139 .summary .sidebar-right-content {
140 margin-bottom: @space;
140 margin-bottom: @space;
141
142 .rc-user {
143 min-width: 0;
144 }
141 }
145 }
142
146
143 .fieldset {
147 .fieldset {
144
148
145 .left-label { // similar to form legend
149 .left-label { // similar to form legend
146 float: left;
150 float: left;
147 display: block;
151 display: block;
148 width: 25%;
152 width: 25%;
149 margin: 0;
153 margin: 0;
150 padding: 0;
154 padding: 0;
151 font-family: @text-semibold;
155 font-family: @text-semibold;
152 }
156 }
153
157
154 .right-content { // similar to form fields
158 .right-content { // similar to form fields
155 float: left;
159 float: left;
156 display: block;
160 display: block;
157 width: 75%;
161 width: 75%;
158 margin: 0 0 0 -15%;
162 margin: 0 0 0 -15%;
159 padding: 0 0 0 15%;
163 padding: 0 0 0 15%;
160
164
161 .truncate-wrap,
165 .truncate-wrap,
162 .truncate {
166 .truncate {
163 max-width: 100%;
167 max-width: 100%;
164 width: 100%;
168 width: 100%;
165 }
169 }
166
170
167 .commit-long {
171 .commit-long {
168 overflow-x: auto;
172 overflow-x: auto;
169 }
173 }
170 }
174 }
171 }
175 }
172
176
173 // expand commit message
177 // expand commit message
174 #message_expand {
178 #message_expand {
175 clear: both;
179 clear: both;
176 display: block;
180 display: block;
177 color: @rcblue;
181 color: @rcblue;
178 cursor: pointer;
182 cursor: pointer;
179 }
183 }
180
184
181 #trimmed_message_box {
185 #trimmed_message_box {
182 max-height: floor(2 * @basefontsize * 1.2); // 2 lines * line-height
186 max-height: floor(2 * @basefontsize * 1.2); // 2 lines * line-height
183 overflow: hidden;
187 overflow: hidden;
184 }
188 }
185
189
186 // show/hide comments button
190 // show/hide comments button
187 .show-inline-comments {
191 .show-inline-comments {
188 display: inline;
192 display: inline;
189 cursor: pointer;
193 cursor: pointer;
190
194
191 .comments-show { display: inline; }
195 .comments-show { display: inline; }
192 .comments-hide { display: none; }
196 .comments-hide { display: none; }
193
197
194 &.comments-visible {
198 &.comments-visible {
195 .comments-show { display: none; }
199 .comments-show { display: none; }
196 .comments-hide { display: inline; }
200 .comments-hide { display: inline; }
197 }
201 }
198 }
202 }
199
203
200 // Quick Start section
204 // Quick Start section
201 .quick_start {
205 .quick_start {
202 float: left;
206 float: left;
203 display: block;
207 display: block;
204 position: relative;
208 position: relative;
205
209
206 // adds some space to make copy and paste easier
210 // adds some space to make copy and paste easier
207 .left-label,
211 .left-label,
208 .right-content {
212 .right-content {
209 line-height: 1.6em;
213 line-height: 1.6em;
210 }
214 }
211 }
215 }
212
216
213 .submodule {
217 .submodule {
214 .summary-detail {
218 .summary-detail {
215 width: 100%;
219 width: 100%;
216
220
217 .btn-collapse {
221 .btn-collapse {
218 display: none;
222 display: none;
219 }
223 }
220 }
224 }
221 }
225 }
222
226
223 .codeblock-header {
227 .codeblock-header {
224 float: left;
228 float: left;
225 display: block;
229 display: block;
226 width: 100%;
230 width: 100%;
227 margin: 0;
231 margin: 0;
228 padding: @space 0 @padding 0;
232 padding: @space 0 @padding 0;
229 border-top: @border-thickness solid @border-default-color;
233 border-top: @border-thickness solid @border-default-color;
230
234
231 .stats {
235 .stats {
232 float: left;
236 float: left;
233 width: 50%;
237 width: 50%;
234 }
238 }
235
239
236 .buttons {
240 .buttons {
237 float: right;
241 float: right;
238 width: 50%;
242 width: 50%;
239 text-align: right;
243 text-align: right;
240 color: @grey4;
244 color: @grey4;
241 }
245 }
242 }
246 }
243
247
244 #summary-menu-stats {
248 #summary-menu-stats {
245
249
246 .stats-bullet {
250 .stats-bullet {
247 color: @grey3;
251 color: @grey3;
248 min-width: 3em;
252 min-width: 3em;
249 }
253 }
250
254
251 .repo-size {
255 .repo-size {
252 margin-bottom: .5em;
256 margin-bottom: .5em;
253 }
257 }
254
258
255 }
259 }
256
260
@@ -1,58 +1,60 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.html"/>
3
2 %if c.users_log:
4 %if c.users_log:
3 <table class="rctable admin_log">
5 <table class="rctable admin_log">
4 <tr>
6 <tr>
5 <th>${_('Username')}</th>
7 <th>${_('Username')}</th>
6 <th>${_('Action')}</th>
8 <th>${_('Action')}</th>
7 <th>${_('Repository')}</th>
9 <th>${_('Repository')}</th>
8 <th>${_('Date')}</th>
10 <th>${_('Date')}</th>
9 <th>${_('From IP')}</th>
11 <th>${_('From IP')}</th>
10 </tr>
12 </tr>
11
13
12 %for cnt,l in enumerate(c.users_log):
14 %for cnt,l in enumerate(c.users_log):
13 <tr class="parity${cnt%2}">
15 <tr class="parity${cnt%2}">
14 <td class="td-user">
16 <td class="td-user">
15 %if l.user is not None:
17 %if l.user is not None:
16 ${h.link_to(l.user.username,h.url('edit_user', user_id=l.user.user_id))}
18 ${base.gravatar_with_user(l.user.email)}
17 %else:
19 %else:
18 ${l.username}
20 ${l.username}
19 %endif
21 %endif
20 </td>
22 </td>
21 <td class="td-journalaction">${h.action_parser(l)[0]()}
23 <td class="td-journalaction">${h.action_parser(l)[0]()}
22 <div class="journal_action_params">
24 <div class="journal_action_params">
23 ${h.literal(h.action_parser(l)[1]())}
25 ${h.literal(h.action_parser(l)[1]())}
24 </div>
26 </div>
25 </td>
27 </td>
26 <td class="td-componentname">
28 <td class="td-componentname">
27 %if l.repository is not None:
29 %if l.repository is not None:
28 ${h.link_to(l.repository.repo_name,h.url('summary_home',repo_name=l.repository.repo_name))}
30 ${h.link_to(l.repository.repo_name,h.url('summary_home',repo_name=l.repository.repo_name))}
29 %else:
31 %else:
30 ${l.repository_name}
32 ${l.repository_name}
31 %endif
33 %endif
32 </td>
34 </td>
33
35
34 <td class="td-time">${h.format_date(l.action_date)}</td>
36 <td class="td-time">${h.format_date(l.action_date)}</td>
35 <td class="td-ip">${l.user_ip}</td>
37 <td class="td-ip">${l.user_ip}</td>
36 </tr>
38 </tr>
37 %endfor
39 %endfor
38 </table>
40 </table>
39
41
40 <div class="pagination-wh pagination-left">
42 <div class="pagination-wh pagination-left">
41 ${c.users_log.pager('$link_previous ~2~ $link_next')}
43 ${c.users_log.pager('$link_previous ~2~ $link_next')}
42 </div>
44 </div>
43 %else:
45 %else:
44 ${_('No actions yet')}
46 ${_('No actions yet')}
45 %endif
47 %endif
46
48
47 <script type="text/javascript">
49 <script type="text/javascript">
48 $(function(){
50 $(function(){
49 //because this is loaded on every pjax request, it must run only once
51 //because this is loaded on every pjax request, it must run only once
50 //therefore the .one method
52 //therefore the .one method
51 $(document).on('pjax:complete',function(){
53 $(document).on('pjax:complete',function(){
52 show_more_event();
54 show_more_event();
53 });
55 });
54
56
55 $(document).pjax('#user_log .pager_link', '#user_log');
57 $(document).pjax('#user_log .pager_link', '#user_log');
56 });
58 });
57 </script>
59 </script>
58
60
@@ -1,655 +1,656 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.html"/>
2 <%inherit file="root.html"/>
3
3
4 <div class="outerwrapper">
4 <div class="outerwrapper">
5 <!-- HEADER -->
5 <!-- HEADER -->
6 <div class="header">
6 <div class="header">
7 <div id="header-inner" class="wrapper">
7 <div id="header-inner" class="wrapper">
8 <div id="logo">
8 <div id="logo">
9 <div class="logo-wrapper">
9 <div class="logo-wrapper">
10 <a href="${h.url('home')}"><img src="${h.url('/images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
10 <a href="${h.url('home')}"><img src="${h.url('/images/rhodecode-logo-white-216x60.png')}" alt="RhodeCode"/></a>
11 </div>
11 </div>
12 %if c.rhodecode_name:
12 %if c.rhodecode_name:
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
13 <div class="branding">- ${h.branding(c.rhodecode_name)}</div>
14 %endif
14 %endif
15 </div>
15 </div>
16 <!-- MENU BAR NAV -->
16 <!-- MENU BAR NAV -->
17 ${self.menu_bar_nav()}
17 ${self.menu_bar_nav()}
18 <!-- END MENU BAR NAV -->
18 <!-- END MENU BAR NAV -->
19 ${self.body()}
19 ${self.body()}
20 </div>
20 </div>
21 </div>
21 </div>
22 ${self.menu_bar_subnav()}
22 ${self.menu_bar_subnav()}
23 <!-- END HEADER -->
23 <!-- END HEADER -->
24
24
25 <!-- CONTENT -->
25 <!-- CONTENT -->
26 <div id="content" class="wrapper">
26 <div id="content" class="wrapper">
27 ${self.flash_msg()}
27 ${self.flash_msg()}
28 <div class="main">
28 <div class="main">
29 ${next.main()}
29 ${next.main()}
30 </div>
30 </div>
31 </div>
31 </div>
32 <!-- END CONTENT -->
32 <!-- END CONTENT -->
33
33
34 </div>
34 </div>
35 <!-- FOOTER -->
35 <!-- FOOTER -->
36 <div id="footer">
36 <div id="footer">
37 <div id="footer-inner" class="title wrapper">
37 <div id="footer-inner" class="title wrapper">
38 <div>
38 <div>
39 <p class="footer-link-right">
39 <p class="footer-link-right">
40 % if c.visual.show_version:
40 % if c.visual.show_version:
41 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
41 RhodeCode Enterprise ${c.rhodecode_version} ${c.rhodecode_edition}
42 % endif
42 % endif
43 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
43 &copy; 2010-${h.datetime.today().year}, <a href="${h.url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
44 % if c.visual.rhodecode_support_url:
44 % if c.visual.rhodecode_support_url:
45 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
45 <a href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a>
46 % endif
46 % endif
47 </p>
47 </p>
48 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
48 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <p class="server-instance" style="display:${sid}">
49 <p class="server-instance" style="display:${sid}">
50 ## display hidden instance ID if specially defined
50 ## display hidden instance ID if specially defined
51 % if c.rhodecode_instanceid:
51 % if c.rhodecode_instanceid:
52 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
52 ${_('RhodeCode instance id: %s') % c.rhodecode_instanceid}
53 % endif
53 % endif
54 </p>
54 </p>
55 </div>
55 </div>
56 </div>
56 </div>
57 </div>
57 </div>
58
58
59 <!-- END FOOTER -->
59 <!-- END FOOTER -->
60
60
61 ### MAKO DEFS ###
61 ### MAKO DEFS ###
62
62
63 <%def name="menu_bar_subnav()">
63 <%def name="menu_bar_subnav()">
64 </%def>
64 </%def>
65
65
66 <%def name="flash_msg()">
66 <%def name="flash_msg()">
67 <%include file="/base/flash_msg.html"/>
67 <%include file="/base/flash_msg.html"/>
68 </%def>
68 </%def>
69
69
70 <%def name="breadcrumbs(class_='breadcrumbs')">
70 <%def name="breadcrumbs(class_='breadcrumbs')">
71 <div class="${class_}">
71 <div class="${class_}">
72 ${self.breadcrumbs_links()}
72 ${self.breadcrumbs_links()}
73 </div>
73 </div>
74 </%def>
74 </%def>
75
75
76 <%def name="admin_menu()">
76 <%def name="admin_menu()">
77 <ul class="admin_menu submenu">
77 <ul class="admin_menu submenu">
78 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
78 <li><a href="${h.url('admin_home')}">${_('Admin journal')}</a></li>
79 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
79 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
80 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
80 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
81 <li><a href="${h.url('users')}">${_('Users')}</a></li>
81 <li><a href="${h.url('users')}">${_('Users')}</a></li>
82 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
82 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
83 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
83 <li><a href="${h.url('admin_permissions_application')}">${_('Permissions')}</a></li>
84 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
84 <li><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
85 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
85 <li><a href="${h.url('admin_defaults_repositories')}">${_('Defaults')}</a></li>
86 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
86 <li class="last"><a href="${h.url('admin_settings')}">${_('Settings')}</a></li>
87 </ul>
87 </ul>
88 </%def>
88 </%def>
89
89
90
90
91 <%def name="dt_info_panel(elements)">
91 <%def name="dt_info_panel(elements)">
92 <dl class="dl-horizontal">
92 <dl class="dl-horizontal">
93 %for dt, dd, title, show_items in elements:
93 %for dt, dd, title, show_items in elements:
94 <dt>${dt}:</dt>
94 <dt>${dt}:</dt>
95 <dd title="${title}">
95 <dd title="${title}">
96 %if callable(dd):
96 %if callable(dd):
97 ## allow lazy evaluation of elements
97 ## allow lazy evaluation of elements
98 ${dd()}
98 ${dd()}
99 %else:
99 %else:
100 ${dd}
100 ${dd}
101 %endif
101 %endif
102 %if show_items:
102 %if show_items:
103 <span class="btn-collapse" data-toggle="item-${h.md5(dt)[:6]}-details">${_('Show More')} </span>
103 <span class="btn-collapse" data-toggle="item-${h.md5(dt)[:6]}-details">${_('Show More')} </span>
104 %endif
104 %endif
105 </dd>
105 </dd>
106
106
107 %if show_items:
107 %if show_items:
108 <div class="collapsable-content" data-toggle="item-${h.md5(dt)[:6]}-details" style="display: none">
108 <div class="collapsable-content" data-toggle="item-${h.md5(dt)[:6]}-details" style="display: none">
109 %for item in show_items:
109 %for item in show_items:
110 <dt></dt>
110 <dt></dt>
111 <dd>${item}</dd>
111 <dd>${item}</dd>
112 %endfor
112 %endfor
113 </div>
113 </div>
114 %endif
114 %endif
115
115
116 %endfor
116 %endfor
117 </dl>
117 </dl>
118 </%def>
118 </%def>
119
119
120
120
121 <%def name="gravatar(email, size=16)">
121 <%def name="gravatar(email, size=16)">
122 <%
122 <%
123 if (size > 16):
123 if (size > 16):
124 gravatar_class = 'gravatar gravatar-large'
124 gravatar_class = 'gravatar gravatar-large'
125 else:
125 else:
126 gravatar_class = 'gravatar'
126 gravatar_class = 'gravatar'
127 %>
127 %>
128 <%doc>
128 <%doc>
129 TODO: johbo: For now we serve double size images to make it smooth
129 TODO: johbo: For now we serve double size images to make it smooth
130 for retina. This is how it worked until now. Should be replaced
130 for retina. This is how it worked until now. Should be replaced
131 with a better solution at some point.
131 with a better solution at some point.
132 </%doc>
132 </%doc>
133 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
133 <img class="${gravatar_class}" src="${h.gravatar_url(email, size * 2)}" height="${size}" width="${size}">
134 </%def>
134 </%def>
135
135
136
136
137 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
137 <%def name="gravatar_with_user(contact, size=16, show_disabled=False)">
138 <div class="rc-user tooltip" title="${contact}">
138 <% email = h.email_or_none(contact) %>
139 ${self.gravatar(h.email_or_none(contact), size)}
139 <div class="rc-user tooltip" title="${h.author_string(email)}">
140 ${self.gravatar(email, size)}
140 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
141 <span class="${'user user-disabled' if show_disabled else 'user'}"> ${h.link_to_user(contact)}</span>
141 </div>
142 </div>
142 </%def>
143 </%def>
143
144
144
145
145 ## admin menu used for people that have some admin resources
146 ## admin menu used for people that have some admin resources
146 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
147 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
147 <ul class="submenu">
148 <ul class="submenu">
148 %if repositories:
149 %if repositories:
149 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
150 <li><a href="${h.url('repos')}">${_('Repositories')}</a></li>
150 %endif
151 %endif
151 %if repository_groups:
152 %if repository_groups:
152 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
153 <li><a href="${h.url('repo_groups')}">${_('Repository groups')}</a></li>
153 %endif
154 %endif
154 %if user_groups:
155 %if user_groups:
155 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
156 <li><a href="${h.url('users_groups')}">${_('User groups')}</a></li>
156 %endif
157 %endif
157 </ul>
158 </ul>
158 </%def>
159 </%def>
159
160
160 <%def name="repo_page_title(repo_instance)">
161 <%def name="repo_page_title(repo_instance)">
161 <div class="title-content">
162 <div class="title-content">
162 <div class="title-main">
163 <div class="title-main">
163 ## SVN/HG/GIT icons
164 ## SVN/HG/GIT icons
164 %if h.is_hg(repo_instance):
165 %if h.is_hg(repo_instance):
165 <i class="icon-hg"></i>
166 <i class="icon-hg"></i>
166 %endif
167 %endif
167 %if h.is_git(repo_instance):
168 %if h.is_git(repo_instance):
168 <i class="icon-git"></i>
169 <i class="icon-git"></i>
169 %endif
170 %endif
170 %if h.is_svn(repo_instance):
171 %if h.is_svn(repo_instance):
171 <i class="icon-svn"></i>
172 <i class="icon-svn"></i>
172 %endif
173 %endif
173
174
174 ## public/private
175 ## public/private
175 %if repo_instance.private:
176 %if repo_instance.private:
176 <i class="icon-repo-private"></i>
177 <i class="icon-repo-private"></i>
177 %else:
178 %else:
178 <i class="icon-repo-public"></i>
179 <i class="icon-repo-public"></i>
179 %endif
180 %endif
180
181
181 ## repo name with group name
182 ## repo name with group name
182 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
183 ${h.breadcrumb_repo_link(c.rhodecode_db_repo)}
183
184
184 </div>
185 </div>
185
186
186 ## FORKED
187 ## FORKED
187 %if repo_instance.fork:
188 %if repo_instance.fork:
188 <p>
189 <p>
189 <i class="icon-code-fork"></i> ${_('Fork of')}
190 <i class="icon-code-fork"></i> ${_('Fork of')}
190 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
191 <a href="${h.url('summary_home',repo_name=repo_instance.fork.repo_name)}">${repo_instance.fork.repo_name}</a>
191 </p>
192 </p>
192 %endif
193 %endif
193
194
194 ## IMPORTED FROM REMOTE
195 ## IMPORTED FROM REMOTE
195 %if repo_instance.clone_uri:
196 %if repo_instance.clone_uri:
196 <p>
197 <p>
197 <i class="icon-code-fork"></i> ${_('Clone from')}
198 <i class="icon-code-fork"></i> ${_('Clone from')}
198 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
199 <a href="${h.url(h.safe_str(h.hide_credentials(repo_instance.clone_uri)))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
199 </p>
200 </p>
200 %endif
201 %endif
201
202
202 ## LOCKING STATUS
203 ## LOCKING STATUS
203 %if repo_instance.locked[0]:
204 %if repo_instance.locked[0]:
204 <p class="locking_locked">
205 <p class="locking_locked">
205 <i class="icon-repo-lock"></i>
206 <i class="icon-repo-lock"></i>
206 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
207 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
207 </p>
208 </p>
208 %elif repo_instance.enable_locking:
209 %elif repo_instance.enable_locking:
209 <p class="locking_unlocked">
210 <p class="locking_unlocked">
210 <i class="icon-repo-unlock"></i>
211 <i class="icon-repo-unlock"></i>
211 ${_('Repository not locked. Pull repository to lock it.')}
212 ${_('Repository not locked. Pull repository to lock it.')}
212 </p>
213 </p>
213 %endif
214 %endif
214
215
215 </div>
216 </div>
216 </%def>
217 </%def>
217
218
218 <%def name="repo_menu(active=None)">
219 <%def name="repo_menu(active=None)">
219 <%
220 <%
220 def is_active(selected):
221 def is_active(selected):
221 if selected == active:
222 if selected == active:
222 return "active"
223 return "active"
223 %>
224 %>
224
225
225 <!--- CONTEXT BAR -->
226 <!--- CONTEXT BAR -->
226 <div id="context-bar">
227 <div id="context-bar">
227 <div class="wrapper">
228 <div class="wrapper">
228 <ul id="context-pages" class="horizontal-list navigation">
229 <ul id="context-pages" class="horizontal-list navigation">
229 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
230 <li class="${is_active('summary')}"><a class="menulink" href="${h.url('summary_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
230 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
231 <li class="${is_active('changelog')}"><a class="menulink" href="${h.url('changelog_home', repo_name=c.repo_name)}"><div class="menulabel">${_('Changelog')}</div></a></li>
231 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
232 <li class="${is_active('files')}"><a class="menulink" href="${h.url('files_home', repo_name=c.repo_name, revision=c.rhodecode_db_repo.landing_rev[1])}"><div class="menulabel">${_('Files')}</div></a></li>
232 <li class="${is_active('compare')}">
233 <li class="${is_active('compare')}">
233 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
234 <a class="menulink" href="${h.url('compare_home',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a>
234 </li>
235 </li>
235 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
236 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
236 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
237 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
237 <li class="${is_active('showpullrequest')}">
238 <li class="${is_active('showpullrequest')}">
238 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
239 <a class="menulink" href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests for %s') % c.repo_name}">
239 %if c.repository_pull_requests:
240 %if c.repository_pull_requests:
240 <span class="pr_notifications">${c.repository_pull_requests}</span>
241 <span class="pr_notifications">${c.repository_pull_requests}</span>
241 %endif
242 %endif
242 <div class="menulabel">${_('Pull Requests')}</div>
243 <div class="menulabel">${_('Pull Requests')}</div>
243 </a>
244 </a>
244 </li>
245 </li>
245 %endif
246 %endif
246 <li class="${is_active('options')}">
247 <li class="${is_active('options')}">
247 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
248 <a class="menulink" href="#" class="dropdown"><div class="menulabel">${_('Options')} <div class="show_more"></div></div></a>
248 <ul class="submenu">
249 <ul class="submenu">
249 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
250 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
250 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
251 <li><a href="${h.url('edit_repo',repo_name=c.repo_name)}">${_('Settings')}</a></li>
251 %endif
252 %endif
252 %if c.rhodecode_db_repo.fork:
253 %if c.rhodecode_db_repo.fork:
253 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
254 <li><a href="${h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,source_ref_type=c.rhodecode_db_repo.landing_rev[0],source_ref=c.rhodecode_db_repo.landing_rev[1], target_repo=c.repo_name,target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1], merge=1)}">
254 ${_('Compare fork')}</a></li>
255 ${_('Compare fork')}</a></li>
255 %endif
256 %endif
256
257
257 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
258 <li><a href="${h.url('search_repo_home',repo_name=c.repo_name)}">${_('Search')}</a></li>
258
259
259 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
260 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
260 %if c.rhodecode_db_repo.locked[0]:
261 %if c.rhodecode_db_repo.locked[0]:
261 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
262 <li><a class="locking_del" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Unlock')}</a></li>
262 %else:
263 %else:
263 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
264 <li><a class="locking_add" href="${h.url('toggle_locking',repo_name=c.repo_name)}">${_('Lock')}</a></li>
264 %endif
265 %endif
265 %endif
266 %endif
266 %if c.rhodecode_user.username != h.DEFAULT_USER:
267 %if c.rhodecode_user.username != h.DEFAULT_USER:
267 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
268 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
268 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
269 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}">${_('Fork')}</a></li>
269 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
270 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}">${_('Create Pull Request')}</a></li>
270 %endif
271 %endif
271 %endif
272 %endif
272 </ul>
273 </ul>
273 </li>
274 </li>
274 </ul>
275 </ul>
275 </div>
276 </div>
276 <div class="clear"></div>
277 <div class="clear"></div>
277 </div>
278 </div>
278 <!--- END CONTEXT BAR -->
279 <!--- END CONTEXT BAR -->
279
280
280 </%def>
281 </%def>
281
282
282 <%def name="usermenu()">
283 <%def name="usermenu()">
283 ## USER MENU
284 ## USER MENU
284 <li id="quick_login_li">
285 <li id="quick_login_li">
285 <a id="quick_login_link" class="menulink childs">
286 <a id="quick_login_link" class="menulink childs">
286 ${gravatar(c.rhodecode_user.email, 20)}
287 ${gravatar(c.rhodecode_user.email, 20)}
287 <span class="user">
288 <span class="user">
288 %if c.rhodecode_user.username != h.DEFAULT_USER:
289 %if c.rhodecode_user.username != h.DEFAULT_USER:
289 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
290 <span class="menu_link_user">${c.rhodecode_user.username}</span><div class="show_more"></div>
290 %else:
291 %else:
291 <span>${_('Sign in')}</span>
292 <span>${_('Sign in')}</span>
292 %endif
293 %endif
293 </span>
294 </span>
294 </a>
295 </a>
295
296
296 <div class="user-menu submenu">
297 <div class="user-menu submenu">
297 <div id="quick_login">
298 <div id="quick_login">
298 %if c.rhodecode_user.username == h.DEFAULT_USER:
299 %if c.rhodecode_user.username == h.DEFAULT_USER:
299 <h4>${_('Sign in to your account')}</h4>
300 <h4>${_('Sign in to your account')}</h4>
300 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
301 ${h.form(h.route_path('login', _query={'came_from': h.url.current()}), needs_csrf_token=False)}
301 <div class="form form-vertical">
302 <div class="form form-vertical">
302 <div class="fields">
303 <div class="fields">
303 <div class="field">
304 <div class="field">
304 <div class="label">
305 <div class="label">
305 <label for="username">${_('Username')}:</label>
306 <label for="username">${_('Username')}:</label>
306 </div>
307 </div>
307 <div class="input">
308 <div class="input">
308 ${h.text('username',class_='focus',tabindex=1)}
309 ${h.text('username',class_='focus',tabindex=1)}
309 </div>
310 </div>
310
311
311 </div>
312 </div>
312 <div class="field">
313 <div class="field">
313 <div class="label">
314 <div class="label">
314 <label for="password">${_('Password')}:</label>
315 <label for="password">${_('Password')}:</label>
315 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'))}</span>
316 <span class="forgot_password">${h.link_to(_('(Forgot password?)'),h.route_path('reset_password'))}</span>
316 </div>
317 </div>
317 <div class="input">
318 <div class="input">
318 ${h.password('password',class_='focus',tabindex=2)}
319 ${h.password('password',class_='focus',tabindex=2)}
319 </div>
320 </div>
320 </div>
321 </div>
321 <div class="buttons">
322 <div class="buttons">
322 <div class="register">
323 <div class="register">
323 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
324 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
324 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
325 ${h.link_to(_("Don't have an account ?"),h.route_path('register'))}
325 %endif
326 %endif
326 </div>
327 </div>
327 <div class="submit">
328 <div class="submit">
328 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
329 ${h.submit('sign_in',_('Sign In'),class_="btn btn-small",tabindex=3)}
329 </div>
330 </div>
330 </div>
331 </div>
331 </div>
332 </div>
332 </div>
333 </div>
333 ${h.end_form()}
334 ${h.end_form()}
334 %else:
335 %else:
335 <div class="">
336 <div class="">
336 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
337 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
337 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
338 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
338 <div class="email">${c.rhodecode_user.email}</div>
339 <div class="email">${c.rhodecode_user.email}</div>
339 </div>
340 </div>
340 <div class="">
341 <div class="">
341 <ol class="links">
342 <ol class="links">
342 <li>${h.link_to(_(u'My account'),h.url('my_account'))}</li>
343 <li>${h.link_to(_(u'My account'),h.url('my_account'))}</li>
343 <li class="logout">
344 <li class="logout">
344 ${h.secure_form(h.route_path('logout'))}
345 ${h.secure_form(h.route_path('logout'))}
345 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
346 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
346 ${h.end_form()}
347 ${h.end_form()}
347 </li>
348 </li>
348 </ol>
349 </ol>
349 </div>
350 </div>
350 %endif
351 %endif
351 </div>
352 </div>
352 </div>
353 </div>
353 %if c.rhodecode_user.username != h.DEFAULT_USER:
354 %if c.rhodecode_user.username != h.DEFAULT_USER:
354 <div class="pill_container">
355 <div class="pill_container">
355 % if c.unread_notifications == 0:
356 % if c.unread_notifications == 0:
356 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
357 <a class="menu_link_notifications empty" href="${h.url('notifications')}">${c.unread_notifications}</a>
357 % else:
358 % else:
358 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
359 <a class="menu_link_notifications" href="${h.url('notifications')}">${c.unread_notifications}</a>
359 % endif
360 % endif
360 </div>
361 </div>
361 % endif
362 % endif
362 </li>
363 </li>
363 </%def>
364 </%def>
364
365
365 <%def name="menu_items(active=None)">
366 <%def name="menu_items(active=None)">
366 <%
367 <%
367 def is_active(selected):
368 def is_active(selected):
368 if selected == active:
369 if selected == active:
369 return "active"
370 return "active"
370 return ""
371 return ""
371 %>
372 %>
372 <ul id="quick" class="main_nav navigation horizontal-list">
373 <ul id="quick" class="main_nav navigation horizontal-list">
373 <!-- repo switcher -->
374 <!-- repo switcher -->
374 <li class="${is_active('repositories')} repo_switcher_li has_select2">
375 <li class="${is_active('repositories')} repo_switcher_li has_select2">
375 <input id="repo_switcher" name="repo_switcher" type="hidden">
376 <input id="repo_switcher" name="repo_switcher" type="hidden">
376 </li>
377 </li>
377
378
378 ## ROOT MENU
379 ## ROOT MENU
379 %if c.rhodecode_user.username != h.DEFAULT_USER:
380 %if c.rhodecode_user.username != h.DEFAULT_USER:
380 <li class="${is_active('journal')}">
381 <li class="${is_active('journal')}">
381 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
382 <a class="menulink" title="${_('Show activity journal')}" href="${h.url('journal')}">
382 <div class="menulabel">${_('Journal')}</div>
383 <div class="menulabel">${_('Journal')}</div>
383 </a>
384 </a>
384 </li>
385 </li>
385 %else:
386 %else:
386 <li class="${is_active('journal')}">
387 <li class="${is_active('journal')}">
387 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
388 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.url('public_journal')}">
388 <div class="menulabel">${_('Public journal')}</div>
389 <div class="menulabel">${_('Public journal')}</div>
389 </a>
390 </a>
390 </li>
391 </li>
391 %endif
392 %endif
392 <li class="${is_active('gists')}">
393 <li class="${is_active('gists')}">
393 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
394 <a class="menulink childs" title="${_('Show Gists')}" href="${h.url('gists')}">
394 <div class="menulabel">${_('Gists')}</div>
395 <div class="menulabel">${_('Gists')}</div>
395 </a>
396 </a>
396 </li>
397 </li>
397 <li class="${is_active('search')}">
398 <li class="${is_active('search')}">
398 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
399 <a class="menulink" title="${_('Search in repositories you have access to')}" href="${h.url('search')}">
399 <div class="menulabel">${_('Search')}</div>
400 <div class="menulabel">${_('Search')}</div>
400 </a>
401 </a>
401 </li>
402 </li>
402 % if h.HasPermissionAll('hg.admin')('access admin main page'):
403 % if h.HasPermissionAll('hg.admin')('access admin main page'):
403 <li class="${is_active('admin')}">
404 <li class="${is_active('admin')}">
404 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
405 <a class="menulink childs" title="${_('Admin settings')}" href="#" onclick="return false;">
405 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
406 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
406 </a>
407 </a>
407 ${admin_menu()}
408 ${admin_menu()}
408 </li>
409 </li>
409 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
410 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
410 <li class="${is_active('admin')}">
411 <li class="${is_active('admin')}">
411 <a class="menulink childs" title="${_('Delegated Admin settings')}">
412 <a class="menulink childs" title="${_('Delegated Admin settings')}">
412 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
413 <div class="menulabel">${_('Admin')} <div class="show_more"></div></div>
413 </a>
414 </a>
414 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
415 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
415 c.rhodecode_user.repository_groups_admin,
416 c.rhodecode_user.repository_groups_admin,
416 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
417 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
417 </li>
418 </li>
418 % endif
419 % endif
419 % if c.debug_style:
420 % if c.debug_style:
420 <li class="${is_active('debug_style')}">
421 <li class="${is_active('debug_style')}">
421 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
422 <a class="menulink" title="${_('Style')}" href="${h.url('debug_style_home')}">
422 <div class="menulabel">${_('Style')}</div>
423 <div class="menulabel">${_('Style')}</div>
423 </a>
424 </a>
424 </li>
425 </li>
425 % endif
426 % endif
426 ## render extra user menu
427 ## render extra user menu
427 ${usermenu()}
428 ${usermenu()}
428 </ul>
429 </ul>
429
430
430 <script type="text/javascript">
431 <script type="text/javascript">
431 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
432 var visual_show_public_icon = "${c.visual.show_public_icon}" == "True";
432
433
433 /*format the look of items in the list*/
434 /*format the look of items in the list*/
434 var format = function(state, escapeMarkup){
435 var format = function(state, escapeMarkup){
435 if (!state.id){
436 if (!state.id){
436 return state.text; // optgroup
437 return state.text; // optgroup
437 }
438 }
438 var obj_dict = state.obj;
439 var obj_dict = state.obj;
439 var tmpl = '';
440 var tmpl = '';
440
441
441 if(obj_dict && state.type == 'repo'){
442 if(obj_dict && state.type == 'repo'){
442 if(obj_dict['repo_type'] === 'hg'){
443 if(obj_dict['repo_type'] === 'hg'){
443 tmpl += '<i class="icon-hg"></i> ';
444 tmpl += '<i class="icon-hg"></i> ';
444 }
445 }
445 else if(obj_dict['repo_type'] === 'git'){
446 else if(obj_dict['repo_type'] === 'git'){
446 tmpl += '<i class="icon-git"></i> ';
447 tmpl += '<i class="icon-git"></i> ';
447 }
448 }
448 else if(obj_dict['repo_type'] === 'svn'){
449 else if(obj_dict['repo_type'] === 'svn'){
449 tmpl += '<i class="icon-svn"></i> ';
450 tmpl += '<i class="icon-svn"></i> ';
450 }
451 }
451 if(obj_dict['private']){
452 if(obj_dict['private']){
452 tmpl += '<i class="icon-lock" ></i> ';
453 tmpl += '<i class="icon-lock" ></i> ';
453 }
454 }
454 else if(visual_show_public_icon){
455 else if(visual_show_public_icon){
455 tmpl += '<i class="icon-unlock-alt"></i> ';
456 tmpl += '<i class="icon-unlock-alt"></i> ';
456 }
457 }
457 }
458 }
458 if(obj_dict && state.type == 'commit') {
459 if(obj_dict && state.type == 'commit') {
459 tmpl += '<i class="icon-tag"></i>';
460 tmpl += '<i class="icon-tag"></i>';
460 }
461 }
461 if(obj_dict && state.type == 'group'){
462 if(obj_dict && state.type == 'group'){
462 tmpl += '<i class="icon-folder-close"></i> ';
463 tmpl += '<i class="icon-folder-close"></i> ';
463 }
464 }
464 tmpl += escapeMarkup(state.text);
465 tmpl += escapeMarkup(state.text);
465 return tmpl;
466 return tmpl;
466 };
467 };
467
468
468 var formatResult = function(result, container, query, escapeMarkup) {
469 var formatResult = function(result, container, query, escapeMarkup) {
469 return format(result, escapeMarkup);
470 return format(result, escapeMarkup);
470 };
471 };
471
472
472 var formatSelection = function(data, container, escapeMarkup) {
473 var formatSelection = function(data, container, escapeMarkup) {
473 return format(data, escapeMarkup);
474 return format(data, escapeMarkup);
474 };
475 };
475
476
476 $("#repo_switcher").select2({
477 $("#repo_switcher").select2({
477 cachedDataSource: {},
478 cachedDataSource: {},
478 minimumInputLength: 2,
479 minimumInputLength: 2,
479 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
480 placeholder: '<div class="menulabel">${_('Go to')} <div class="show_more"></div></div>',
480 dropdownAutoWidth: true,
481 dropdownAutoWidth: true,
481 formatResult: formatResult,
482 formatResult: formatResult,
482 formatSelection: formatSelection,
483 formatSelection: formatSelection,
483 containerCssClass: "repo-switcher",
484 containerCssClass: "repo-switcher",
484 dropdownCssClass: "repo-switcher-dropdown",
485 dropdownCssClass: "repo-switcher-dropdown",
485 escapeMarkup: function(m){
486 escapeMarkup: function(m){
486 // don't escape our custom placeholder
487 // don't escape our custom placeholder
487 if(m.substr(0,23) == '<div class="menulabel">'){
488 if(m.substr(0,23) == '<div class="menulabel">'){
488 return m;
489 return m;
489 }
490 }
490
491
491 return Select2.util.escapeMarkup(m);
492 return Select2.util.escapeMarkup(m);
492 },
493 },
493 query: $.debounce(250, function(query){
494 query: $.debounce(250, function(query){
494 self = this;
495 self = this;
495 var cacheKey = query.term;
496 var cacheKey = query.term;
496 var cachedData = self.cachedDataSource[cacheKey];
497 var cachedData = self.cachedDataSource[cacheKey];
497
498
498 if (cachedData) {
499 if (cachedData) {
499 query.callback({results: cachedData.results});
500 query.callback({results: cachedData.results});
500 } else {
501 } else {
501 $.ajax({
502 $.ajax({
502 url: "${h.url('goto_switcher_data')}",
503 url: "${h.url('goto_switcher_data')}",
503 data: {'query': query.term},
504 data: {'query': query.term},
504 dataType: 'json',
505 dataType: 'json',
505 type: 'GET',
506 type: 'GET',
506 success: function(data) {
507 success: function(data) {
507 self.cachedDataSource[cacheKey] = data;
508 self.cachedDataSource[cacheKey] = data;
508 query.callback({results: data.results});
509 query.callback({results: data.results});
509 },
510 },
510 error: function(data, textStatus, errorThrown) {
511 error: function(data, textStatus, errorThrown) {
511 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
512 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
512 }
513 }
513 })
514 })
514 }
515 }
515 })
516 })
516 });
517 });
517
518
518 $("#repo_switcher").on('select2-selecting', function(e){
519 $("#repo_switcher").on('select2-selecting', function(e){
519 e.preventDefault();
520 e.preventDefault();
520 window.location = e.choice.url;
521 window.location = e.choice.url;
521 });
522 });
522
523
523 ## Global mouse bindings ##
524 ## Global mouse bindings ##
524
525
525 // general help "?"
526 // general help "?"
526 Mousetrap.bind(['?'], function(e) {
527 Mousetrap.bind(['?'], function(e) {
527 $('#help_kb').modal({})
528 $('#help_kb').modal({})
528 });
529 });
529
530
530 // / open the quick filter
531 // / open the quick filter
531 Mousetrap.bind(['/'], function(e) {
532 Mousetrap.bind(['/'], function(e) {
532 $("#repo_switcher").select2("open");
533 $("#repo_switcher").select2("open");
533
534
534 // return false to prevent default browser behavior
535 // return false to prevent default browser behavior
535 // and stop event from bubbling
536 // and stop event from bubbling
536 return false;
537 return false;
537 });
538 });
538
539
539 // general nav g + action
540 // general nav g + action
540 Mousetrap.bind(['g h'], function(e) {
541 Mousetrap.bind(['g h'], function(e) {
541 window.location = pyroutes.url('home');
542 window.location = pyroutes.url('home');
542 });
543 });
543 Mousetrap.bind(['g g'], function(e) {
544 Mousetrap.bind(['g g'], function(e) {
544 window.location = pyroutes.url('gists', {'private':1});
545 window.location = pyroutes.url('gists', {'private':1});
545 });
546 });
546 Mousetrap.bind(['g G'], function(e) {
547 Mousetrap.bind(['g G'], function(e) {
547 window.location = pyroutes.url('gists', {'public':1});
548 window.location = pyroutes.url('gists', {'public':1});
548 });
549 });
549 Mousetrap.bind(['n g'], function(e) {
550 Mousetrap.bind(['n g'], function(e) {
550 window.location = pyroutes.url('new_gist');
551 window.location = pyroutes.url('new_gist');
551 });
552 });
552 Mousetrap.bind(['n r'], function(e) {
553 Mousetrap.bind(['n r'], function(e) {
553 window.location = pyroutes.url('new_repo');
554 window.location = pyroutes.url('new_repo');
554 });
555 });
555
556
556 % if hasattr(c, 'repo_name') and hasattr(c, 'rhodecode_db_repo'):
557 % if hasattr(c, 'repo_name') and hasattr(c, 'rhodecode_db_repo'):
557 // nav in repo context
558 // nav in repo context
558 Mousetrap.bind(['g s'], function(e) {
559 Mousetrap.bind(['g s'], function(e) {
559 window.location = pyroutes.url('summary_home', {'repo_name': REPO_NAME});
560 window.location = pyroutes.url('summary_home', {'repo_name': REPO_NAME});
560 });
561 });
561 Mousetrap.bind(['g c'], function(e) {
562 Mousetrap.bind(['g c'], function(e) {
562 window.location = pyroutes.url('changelog_home', {'repo_name': REPO_NAME});
563 window.location = pyroutes.url('changelog_home', {'repo_name': REPO_NAME});
563 });
564 });
564 Mousetrap.bind(['g F'], function(e) {
565 Mousetrap.bind(['g F'], function(e) {
565 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': '', 'search': '1'});
566 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': '', 'search': '1'});
566 });
567 });
567 Mousetrap.bind(['g f'], function(e) {
568 Mousetrap.bind(['g f'], function(e) {
568 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': ''});
569 window.location = pyroutes.url('files_home', {'repo_name': REPO_NAME, 'revision': '${c.rhodecode_db_repo.landing_rev[1]}', 'f_path': ''});
569 });
570 });
570 Mousetrap.bind(['g p'], function(e) {
571 Mousetrap.bind(['g p'], function(e) {
571 window.location = pyroutes.url('pullrequest_show_all', {'repo_name': REPO_NAME});
572 window.location = pyroutes.url('pullrequest_show_all', {'repo_name': REPO_NAME});
572 });
573 });
573 Mousetrap.bind(['g o'], function(e) {
574 Mousetrap.bind(['g o'], function(e) {
574 window.location = pyroutes.url('edit_repo', {'repo_name': REPO_NAME});
575 window.location = pyroutes.url('edit_repo', {'repo_name': REPO_NAME});
575 });
576 });
576 Mousetrap.bind(['g O'], function(e) {
577 Mousetrap.bind(['g O'], function(e) {
577 window.location = pyroutes.url('edit_repo_perms', {'repo_name': REPO_NAME});
578 window.location = pyroutes.url('edit_repo_perms', {'repo_name': REPO_NAME});
578 });
579 });
579 % endif
580 % endif
580
581
581 </script>
582 </script>
582 <script src="${h.url('/js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
583 <script src="${h.url('/js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
583 </%def>
584 </%def>
584
585
585 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
586 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
586 <div class="modal-dialog">
587 <div class="modal-dialog">
587 <div class="modal-content">
588 <div class="modal-content">
588 <div class="modal-header">
589 <div class="modal-header">
589 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
590 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
590 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
591 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
591 </div>
592 </div>
592 <div class="modal-body">
593 <div class="modal-body">
593 <div class="block-left">
594 <div class="block-left">
594 <table class="keyboard-mappings">
595 <table class="keyboard-mappings">
595 <tbody>
596 <tbody>
596 <tr>
597 <tr>
597 <th></th>
598 <th></th>
598 <th>${_('Site-wide shortcuts')}</th>
599 <th>${_('Site-wide shortcuts')}</th>
599 </tr>
600 </tr>
600 <%
601 <%
601 elems = [
602 elems = [
602 ('/', 'Open quick search box'),
603 ('/', 'Open quick search box'),
603 ('g h', 'Goto home page'),
604 ('g h', 'Goto home page'),
604 ('g g', 'Goto my private gists page'),
605 ('g g', 'Goto my private gists page'),
605 ('g G', 'Goto my public gists page'),
606 ('g G', 'Goto my public gists page'),
606 ('n r', 'New repository page'),
607 ('n r', 'New repository page'),
607 ('n g', 'New gist page'),
608 ('n g', 'New gist page'),
608 ]
609 ]
609 %>
610 %>
610 %for key, desc in elems:
611 %for key, desc in elems:
611 <tr>
612 <tr>
612 <td class="keys">
613 <td class="keys">
613 <span class="key tag">${key}</span>
614 <span class="key tag">${key}</span>
614 </td>
615 </td>
615 <td>${desc}</td>
616 <td>${desc}</td>
616 </tr>
617 </tr>
617 %endfor
618 %endfor
618 </tbody>
619 </tbody>
619 </table>
620 </table>
620 </div>
621 </div>
621 <div class="block-left">
622 <div class="block-left">
622 <table class="keyboard-mappings">
623 <table class="keyboard-mappings">
623 <tbody>
624 <tbody>
624 <tr>
625 <tr>
625 <th></th>
626 <th></th>
626 <th>${_('Repositories')}</th>
627 <th>${_('Repositories')}</th>
627 </tr>
628 </tr>
628 <%
629 <%
629 elems = [
630 elems = [
630 ('g s', 'Goto summary page'),
631 ('g s', 'Goto summary page'),
631 ('g c', 'Goto changelog page'),
632 ('g c', 'Goto changelog page'),
632 ('g f', 'Goto files page'),
633 ('g f', 'Goto files page'),
633 ('g F', 'Goto files page with file search activated'),
634 ('g F', 'Goto files page with file search activated'),
634 ('g p', 'Goto pull requests page'),
635 ('g p', 'Goto pull requests page'),
635 ('g o', 'Goto repository settings'),
636 ('g o', 'Goto repository settings'),
636 ('g O', 'Goto repository permissions settings'),
637 ('g O', 'Goto repository permissions settings'),
637 ]
638 ]
638 %>
639 %>
639 %for key, desc in elems:
640 %for key, desc in elems:
640 <tr>
641 <tr>
641 <td class="keys">
642 <td class="keys">
642 <span class="key tag">${key}</span>
643 <span class="key tag">${key}</span>
643 </td>
644 </td>
644 <td>${desc}</td>
645 <td>${desc}</td>
645 </tr>
646 </tr>
646 %endfor
647 %endfor
647 </tbody>
648 </tbody>
648 </table>
649 </table>
649 </div>
650 </div>
650 </div>
651 </div>
651 <div class="modal-footer">
652 <div class="modal-footer">
652 </div>
653 </div>
653 </div><!-- /.modal-content -->
654 </div><!-- /.modal-content -->
654 </div><!-- /.modal-dialog -->
655 </div><!-- /.modal-dialog -->
655 </div><!-- /.modal -->
656 </div><!-- /.modal -->
@@ -1,415 +1,414 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4
4
5 <%def name="title()">
5 <%def name="title()">
6 ${_('%s Changelog') % c.repo_name}
6 ${_('%s Changelog') % c.repo_name}
7 %if c.changelog_for_path:
7 %if c.changelog_for_path:
8 /${c.changelog_for_path}
8 /${c.changelog_for_path}
9 %endif
9 %endif
10 %if c.rhodecode_name:
10 %if c.rhodecode_name:
11 &middot; ${h.branding(c.rhodecode_name)}
11 &middot; ${h.branding(c.rhodecode_name)}
12 %endif
12 %endif
13 </%def>
13 </%def>
14
14
15 <%def name="breadcrumbs_links()">
15 <%def name="breadcrumbs_links()">
16 %if c.changelog_for_path:
16 %if c.changelog_for_path:
17 /${c.changelog_for_path}
17 /${c.changelog_for_path}
18 %endif
18 %endif
19 ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)}
19 ${ungettext('showing %d out of %d commit', 'showing %d out of %d commits', c.showing_commits) % (c.showing_commits, c.total_cs)}
20 </%def>
20 </%def>
21
21
22 <%def name="menu_bar_nav()">
22 <%def name="menu_bar_nav()">
23 ${self.menu_items(active='repositories')}
23 ${self.menu_items(active='repositories')}
24 </%def>
24 </%def>
25
25
26 <%def name="menu_bar_subnav()">
26 <%def name="menu_bar_subnav()">
27 ${self.repo_menu(active='changelog')}
27 ${self.repo_menu(active='changelog')}
28 </%def>
28 </%def>
29
29
30 <%def name="main()">
30 <%def name="main()">
31
31
32 <div class="box">
32 <div class="box">
33 <div class="title">
33 <div class="title">
34 ${self.repo_page_title(c.rhodecode_db_repo)}
34 ${self.repo_page_title(c.rhodecode_db_repo)}
35 <ul class="links">
35 <ul class="links">
36 <li>
36 <li>
37 <a href="#" class="btn btn-small" id="rev_range_container" style="display:none;"></a>
37 <a href="#" class="btn btn-small" id="rev_range_container" style="display:none;"></a>
38 %if c.rhodecode_db_repo.fork:
38 %if c.rhodecode_db_repo.fork:
39 <span>
39 <span>
40 <a id="compare_fork_button"
40 <a id="compare_fork_button"
41 title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}"
41 title="${_('Compare fork with %s' % c.rhodecode_db_repo.fork.repo_name)}"
42 class="btn btn-small"
42 class="btn btn-small"
43 href="${h.url('compare_url',
43 href="${h.url('compare_url',
44 repo_name=c.rhodecode_db_repo.fork.repo_name,
44 repo_name=c.rhodecode_db_repo.fork.repo_name,
45 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
45 source_ref_type=c.rhodecode_db_repo.landing_rev[0],
46 source_ref=c.rhodecode_db_repo.landing_rev[1],
46 source_ref=c.rhodecode_db_repo.landing_rev[1],
47 target_repo=c.repo_name,
47 target_repo=c.repo_name,
48 target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
48 target_ref_type='branch' if request.GET.get('branch') else c.rhodecode_db_repo.landing_rev[0],
49 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
49 target_ref=request.GET.get('branch') or c.rhodecode_db_repo.landing_rev[1],
50 merge=1)
50 merge=1)
51 }">
51 }">
52 <i class="icon-loop"></i>
52 <i class="icon-loop"></i>
53 ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)}
53 ${_('Compare fork with Parent (%s)' % c.rhodecode_db_repo.fork.repo_name)}
54 </a>
54 </a>
55 </span>
55 </span>
56 %endif
56 %endif
57
57
58 ## pr open link
58 ## pr open link
59 %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo):
59 %if h.is_hg(c.rhodecode_repo) or h.is_git(c.rhodecode_repo):
60 <span>
60 <span>
61 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}">
61 <a id="open_new_pull_request" class="btn btn-small btn-success" href="${h.url('pullrequest_home',repo_name=c.repo_name)}">
62 ${_('Open new pull request')}
62 ${_('Open new pull request')}
63 </a>
63 </a>
64 </span>
64 </span>
65 %endif
65 %endif
66
66
67 ## clear selection
67 ## clear selection
68 <div title="${_('Clear selection')}" class="btn" id="rev_range_clear" style="display:none">
68 <div title="${_('Clear selection')}" class="btn" id="rev_range_clear" style="display:none">
69 ${_('Clear selection')}
69 ${_('Clear selection')}
70 </div>
70 </div>
71
71
72 </li>
72 </li>
73 </ul>
73 </ul>
74 </div>
74 </div>
75
75
76 % if c.pagination:
76 % if c.pagination:
77
77
78 <div class="graph-header">
78 <div class="graph-header">
79 <div id="filter_changelog">
79 <div id="filter_changelog">
80 ${h.hidden('branch_filter')}
80 ${h.hidden('branch_filter')}
81 %if c.selected_name:
81 %if c.selected_name:
82 <div class="btn btn-default" id="clear_filter" >
82 <div class="btn btn-default" id="clear_filter" >
83 ${_('Clear filter')}
83 ${_('Clear filter')}
84 </div>
84 </div>
85 %endif
85 %endif
86 </div>
86 </div>
87 ${self.breadcrumbs('breadcrumbs_light')}
87 ${self.breadcrumbs('breadcrumbs_light')}
88 </div>
88 </div>
89
89
90 <div id="graph">
90 <div id="graph">
91 <div class="graph-col-wrapper">
91 <div class="graph-col-wrapper">
92 <div id="graph_nodes">
92 <div id="graph_nodes">
93 <div id="graph_canvas" data-graph='${c.jsdata|n}'></div>
93 <div id="graph_canvas" data-graph='${c.jsdata|n}'></div>
94 </div>
94 </div>
95 <div id="graph_content" class="main-content graph_full_width">
95 <div id="graph_content" class="main-content graph_full_width">
96
96
97 <div class="table">
97 <div class="table">
98 <table id="changesets" class="rctable">
98 <table id="changesets" class="rctable">
99 <tr>
99 <tr>
100 <th></th>
100 <th></th>
101 <th></th>
101 <th></th>
102 <th>${_('Author')}</th>
102 <th>${_('Author')}</th>
103 <th>${_('Age')}</th>
103 <th>${_('Age')}</th>
104 <th></th>
104 <th></th>
105 <th>${_('Commit Message')}</th>
105 <th>${_('Commit Message')}</th>
106 <th>${_('Commit')}</th>
106 <th>${_('Commit')}</th>
107 <th></th>
107 <th></th>
108 <th>${_('Refs')}</th>
108 <th>${_('Refs')}</th>
109 </tr>
109 </tr>
110 <tbody>
110 <tbody>
111 %for cnt,commit in enumerate(c.pagination):
111 %for cnt,commit in enumerate(c.pagination):
112 <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
112 <tr id="chg_${cnt+1}" class="container ${'tablerow%s' % (cnt%2)}">
113
113
114 <td class="td-checkbox">
114 <td class="td-checkbox">
115 ${h.checkbox(commit.raw_id,class_="commit-range")}
115 ${h.checkbox(commit.raw_id,class_="commit-range")}
116 </td>
116 </td>
117 <td class="td-status">
117 <td class="td-status">
118
118
119 %if c.statuses.get(commit.raw_id):
119 %if c.statuses.get(commit.raw_id):
120 <div class="changeset-status-ico">
120 <div class="changeset-status-ico">
121 %if c.statuses.get(commit.raw_id)[2]:
121 %if c.statuses.get(commit.raw_id)[2]:
122 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
122 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (h.commit_status_lbl(c.statuses.get(commit.raw_id)[0]), c.statuses.get(commit.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(commit.raw_id)[3],pull_request_id=c.statuses.get(commit.raw_id)[2])}">
123 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
123 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
124 </a>
124 </a>
125 %else:
125 %else:
126 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
126 <a class="tooltip" title="${_('Commit status: %s') % h.commit_status_lbl(c.statuses.get(commit.raw_id)[0])}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
127 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
127 <div class="${'flag_status %s' % c.statuses.get(commit.raw_id)[0]}"></div>
128 </a>
128 </a>
129 %endif
129 %endif
130 </div>
130 </div>
131 %endif
131 %endif
132 </td>
132 </td>
133 <td class="td-user">
133 <td class="td-user">
134 ${self.gravatar(h.email_or_none(commit.author))}
134 ${self.gravatar_with_user(commit.author)}
135 <span title="${commit.author}" class="user">${h.link_to_user(commit.author, length=22)}</span>
136 </td>
135 </td>
137 <td class="td-time">
136 <td class="td-time">
138 ${h.age_component(commit.date)}
137 ${h.age_component(commit.date)}
139 </td>
138 </td>
140
139
141 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}">
140 <td class="td-message expand_commit" data-commit-id="${commit.raw_id}" title="${_('Expand commit message')}">
142 <div class="show_more_col">
141 <div class="show_more_col">
143 <i class="show_more"></i>&nbsp;
142 <i class="show_more"></i>&nbsp;
144 </div>
143 </div>
145 </td>
144 </td>
146 <td class="mid td-description">
145 <td class="mid td-description">
147 <div class="log-container truncate-wrap">
146 <div class="log-container truncate-wrap">
148 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
147 <div class="message truncate" id="c-${commit.raw_id}">${h.urlify_commit_message(commit.message, c.repo_name)}</div>
149 </div>
148 </div>
150 </td>
149 </td>
151
150
152 <td class="td-hash">
151 <td class="td-hash">
153 <code>
152 <code>
154 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">
153 <a href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id)}">
155 <span class="commit_hash">${h.show_id(commit)}</span>
154 <span class="commit_hash">${h.show_id(commit)}</span>
156 </a>
155 </a>
157 </code>
156 </code>
158 </td>
157 </td>
159
158
160 <td class="td-comments comments-col">
159 <td class="td-comments comments-col">
161 %if c.comments.get(commit.raw_id):
160 %if c.comments.get(commit.raw_id):
162 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
161 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=commit.raw_id,anchor='comment-%s' % c.comments[commit.raw_id][0].comment_id)}">
163 ${len(c.comments[commit.raw_id])} <i class="icon-comment icon-comment-colored"></i>
162 ${len(c.comments[commit.raw_id])} <i class="icon-comment icon-comment-colored"></i>
164 </a>
163 </a>
165 %endif
164 %endif
166 </td>
165 </td>
167
166
168 <td class="td-tags tags-col truncate-wrap">
167 <td class="td-tags tags-col truncate-wrap">
169 <div class="truncate tags-truncate" id="t-${commit.raw_id}">
168 <div class="truncate tags-truncate" id="t-${commit.raw_id}">
170 ## branch
169 ## branch
171 %if commit.branch:
170 %if commit.branch:
172 <span class="branchtag tag" title="${_('Branch %s') % commit.branch}">
171 <span class="branchtag tag" title="${_('Branch %s') % commit.branch}">
173 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
172 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=commit.branch)}"><i class="icon-code-fork"></i>${h.shorter(commit.branch)}</a>
174 </span>
173 </span>
175 %endif
174 %endif
176
175
177 ## bookmarks
176 ## bookmarks
178 %if h.is_hg(c.rhodecode_repo):
177 %if h.is_hg(c.rhodecode_repo):
179 %for book in commit.bookmarks:
178 %for book in commit.bookmarks:
180 <span class="tag booktag" title="${_('Bookmark %s') % book}">
179 <span class="tag booktag" title="${_('Bookmark %s') % book}">
181 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
180 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
182 </span>
181 </span>
183 %endfor
182 %endfor
184 %endif
183 %endif
185
184
186 ## tags
185 ## tags
187 %for tag in commit.tags:
186 %for tag in commit.tags:
188 <span class="tagtag tag" title="${_('Tag %s') % tag}">
187 <span class="tagtag tag" title="${_('Tag %s') % tag}">
189 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
188 <a href="${h.url('files_home',repo_name=c.repo_name,revision=commit.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
190 </span>
189 </span>
191 %endfor
190 %endfor
192
191
193 </div>
192 </div>
194 </td>
193 </td>
195 </tr>
194 </tr>
196 %endfor
195 %endfor
197 </tbody>
196 </tbody>
198 </table>
197 </table>
199 </div>
198 </div>
200 </div>
199 </div>
201 </div>
200 </div>
202 <div class="pagination-wh pagination-left">
201 <div class="pagination-wh pagination-left">
203 ${c.pagination.pager('$link_previous ~2~ $link_next')}
202 ${c.pagination.pager('$link_previous ~2~ $link_next')}
204 </div>
203 </div>
205
204
206 <script type="text/javascript" src="${h.url('/js/jquery.commits-graph.js')}"></script>
205 <script type="text/javascript" src="${h.url('/js/jquery.commits-graph.js')}"></script>
207 <script type="text/javascript">
206 <script type="text/javascript">
208 var cache = {};
207 var cache = {};
209 $(function(){
208 $(function(){
210
209
211 // Create links to commit ranges when range checkboxes are selected
210 // Create links to commit ranges when range checkboxes are selected
212 var $commitCheckboxes = $('.commit-range');
211 var $commitCheckboxes = $('.commit-range');
213 // cache elements
212 // cache elements
214 var $commitRangeContainer = $('#rev_range_container');
213 var $commitRangeContainer = $('#rev_range_container');
215 var $commitRangeClear = $('#rev_range_clear');
214 var $commitRangeClear = $('#rev_range_clear');
216
215
217 var checkboxRangeSelector = function(e){
216 var checkboxRangeSelector = function(e){
218 var selectedCheckboxes = [];
217 var selectedCheckboxes = [];
219 for (pos in $commitCheckboxes){
218 for (pos in $commitCheckboxes){
220 if($commitCheckboxes[pos].checked){
219 if($commitCheckboxes[pos].checked){
221 selectedCheckboxes.push($commitCheckboxes[pos]);
220 selectedCheckboxes.push($commitCheckboxes[pos]);
222 }
221 }
223 }
222 }
224 var open_new_pull_request = $('#open_new_pull_request');
223 var open_new_pull_request = $('#open_new_pull_request');
225 if(open_new_pull_request){
224 if(open_new_pull_request){
226 var selected_changes = selectedCheckboxes.length;
225 var selected_changes = selectedCheckboxes.length;
227 if (selected_changes > 1 || selected_changes == 1 && templateContext.repo_type != 'hg') {
226 if (selected_changes > 1 || selected_changes == 1 && templateContext.repo_type != 'hg') {
228 open_new_pull_request.hide();
227 open_new_pull_request.hide();
229 } else {
228 } else {
230 if (selected_changes == 1) {
229 if (selected_changes == 1) {
231 open_new_pull_request.html(_gettext('Open new pull request for selected commit'));
230 open_new_pull_request.html(_gettext('Open new pull request for selected commit'));
232 } else if (selected_changes == 0) {
231 } else if (selected_changes == 0) {
233 open_new_pull_request.html(_gettext('Open new pull request'));
232 open_new_pull_request.html(_gettext('Open new pull request'));
234 }
233 }
235 open_new_pull_request.show();
234 open_new_pull_request.show();
236 }
235 }
237 }
236 }
238
237
239 if (selectedCheckboxes.length>0){
238 if (selectedCheckboxes.length>0){
240 var revEnd = selectedCheckboxes[0].name;
239 var revEnd = selectedCheckboxes[0].name;
241 var revStart = selectedCheckboxes[selectedCheckboxes.length-1].name;
240 var revStart = selectedCheckboxes[selectedCheckboxes.length-1].name;
242 var url = pyroutes.url('changeset_home',
241 var url = pyroutes.url('changeset_home',
243 {'repo_name': '${c.repo_name}',
242 {'repo_name': '${c.repo_name}',
244 'revision': revStart+'...'+revEnd});
243 'revision': revStart+'...'+revEnd});
245
244
246 var link = (revStart == revEnd)
245 var link = (revStart == revEnd)
247 ? _gettext('Show selected commit __S')
246 ? _gettext('Show selected commit __S')
248 : _gettext('Show selected commits __S ... __E');
247 : _gettext('Show selected commits __S ... __E');
249
248
250 link = link.replace('__S', revStart.substr(0,6));
249 link = link.replace('__S', revStart.substr(0,6));
251 link = link.replace('__E', revEnd.substr(0,6));
250 link = link.replace('__E', revEnd.substr(0,6));
252
251
253 $commitRangeContainer
252 $commitRangeContainer
254 .attr('href',url)
253 .attr('href',url)
255 .html(link)
254 .html(link)
256 .show();
255 .show();
257
256
258 $commitRangeClear.show();
257 $commitRangeClear.show();
259 var _url = pyroutes.url('pullrequest_home',
258 var _url = pyroutes.url('pullrequest_home',
260 {'repo_name': '${c.repo_name}',
259 {'repo_name': '${c.repo_name}',
261 'commit': revEnd});
260 'commit': revEnd});
262 open_new_pull_request.attr('href', _url);
261 open_new_pull_request.attr('href', _url);
263 $('#compare_fork_button').hide();
262 $('#compare_fork_button').hide();
264 } else {
263 } else {
265 $commitRangeContainer.hide();
264 $commitRangeContainer.hide();
266 $commitRangeClear.hide();
265 $commitRangeClear.hide();
267
266
268 %if c.branch_name:
267 %if c.branch_name:
269 var _url = pyroutes.url('pullrequest_home',
268 var _url = pyroutes.url('pullrequest_home',
270 {'repo_name': '${c.repo_name}',
269 {'repo_name': '${c.repo_name}',
271 'branch':'${c.branch_name}'});
270 'branch':'${c.branch_name}'});
272 open_new_pull_request.attr('href', _url);
271 open_new_pull_request.attr('href', _url);
273 %else:
272 %else:
274 var _url = pyroutes.url('pullrequest_home',
273 var _url = pyroutes.url('pullrequest_home',
275 {'repo_name': '${c.repo_name}'});
274 {'repo_name': '${c.repo_name}'});
276 open_new_pull_request.attr('href', _url);
275 open_new_pull_request.attr('href', _url);
277 %endif
276 %endif
278 $('#compare_fork_button').show();
277 $('#compare_fork_button').show();
279 }
278 }
280 };
279 };
281
280
282 $commitCheckboxes.on('click', checkboxRangeSelector);
281 $commitCheckboxes.on('click', checkboxRangeSelector);
283
282
284 $commitRangeClear.on('click',function(e) {
283 $commitRangeClear.on('click',function(e) {
285 $commitCheckboxes.attr('checked', false)
284 $commitCheckboxes.attr('checked', false)
286 checkboxRangeSelector();
285 checkboxRangeSelector();
287 e.preventDefault();
286 e.preventDefault();
288 });
287 });
289
288
290 // make sure the buttons are consistent when navigate back and forth
289 // make sure the buttons are consistent when navigate back and forth
291 checkboxRangeSelector();
290 checkboxRangeSelector();
292
291
293
292
294 var msgs = $('.message');
293 var msgs = $('.message');
295 // get first element height
294 // get first element height
296 var el = $('#graph_content .container')[0];
295 var el = $('#graph_content .container')[0];
297 var row_h = el.clientHeight;
296 var row_h = el.clientHeight;
298 for (var i=0; i < msgs.length; i++) {
297 for (var i=0; i < msgs.length; i++) {
299 var m = msgs[i];
298 var m = msgs[i];
300
299
301 var h = m.clientHeight;
300 var h = m.clientHeight;
302 var pad = $(m).css('padding');
301 var pad = $(m).css('padding');
303 if (h > row_h) {
302 if (h > row_h) {
304 var offset = row_h - (h+12);
303 var offset = row_h - (h+12);
305 $(m.nextElementSibling).css('display','block');
304 $(m.nextElementSibling).css('display','block');
306 $(m.nextElementSibling).css('margin-top',offset+'px');
305 $(m.nextElementSibling).css('margin-top',offset+'px');
307 }
306 }
308 }
307 }
309
308
310 $('.expand_commit').on('click',function(e){
309 $('.expand_commit').on('click',function(e){
311 var target_expand = $(this);
310 var target_expand = $(this);
312 var cid = target_expand.data('commitId');
311 var cid = target_expand.data('commitId');
313
312
314 if (target_expand.hasClass('open')){
313 if (target_expand.hasClass('open')){
315 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
314 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'});
316 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'});
315 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden', 'white-space':'nowrap'});
317 target_expand.removeClass('open');
316 target_expand.removeClass('open');
318 }
317 }
319 else {
318 else {
320 $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'});
319 $('#c-'+cid).css({'height': 'auto', 'white-space': 'pre-line', 'text-overflow': 'initial', 'overflow':'visible'});
321 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'});
320 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible', 'white-space':'normal'});
322 target_expand.addClass('open');
321 target_expand.addClass('open');
323 }
322 }
324 // redraw the graph
323 // redraw the graph
325 graph_options.height = $("#changesets").height();
324 graph_options.height = $("#changesets").height();
326 $("canvas").remove();
325 $("canvas").remove();
327 $("[data-graph]").commits(graph_options);
326 $("[data-graph]").commits(graph_options);
328 });
327 });
329
328
330 $("#clear_filter").on("click", function() {
329 $("#clear_filter").on("click", function() {
331 var filter = {'repo_name': '${c.repo_name}'};
330 var filter = {'repo_name': '${c.repo_name}'};
332 window.location = pyroutes.url('changelog_home', filter);
331 window.location = pyroutes.url('changelog_home', filter);
333 });
332 });
334
333
335 $("#branch_filter").select2({
334 $("#branch_filter").select2({
336 'dropdownAutoWidth': true,
335 'dropdownAutoWidth': true,
337 'width': 'resolve',
336 'width': 'resolve',
338 'placeholder': "${c.selected_name or _('Filter changelog')}",
337 'placeholder': "${c.selected_name or _('Filter changelog')}",
339 containerCssClass: "drop-menu",
338 containerCssClass: "drop-menu",
340 dropdownCssClass: "drop-menu-dropdown",
339 dropdownCssClass: "drop-menu-dropdown",
341 query: function(query){
340 query: function(query){
342 var key = 'cache';
341 var key = 'cache';
343 var cached = cache[key] ;
342 var cached = cache[key] ;
344 if(cached) {
343 if(cached) {
345 var data = {results: []};
344 var data = {results: []};
346 //filter results
345 //filter results
347 $.each(cached.results, function(){
346 $.each(cached.results, function(){
348 var section = this.text;
347 var section = this.text;
349 var children = [];
348 var children = [];
350 $.each(this.children, function(){
349 $.each(this.children, function(){
351 if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
350 if(query.term.length == 0 || this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ){
352 children.push({'id': this.id, 'text': this.text, 'type': this.type})
351 children.push({'id': this.id, 'text': this.text, 'type': this.type})
353 }
352 }
354 });
353 });
355 data.results.push({'text': section, 'children': children});
354 data.results.push({'text': section, 'children': children});
356 query.callback({results: data.results});
355 query.callback({results: data.results});
357 });
356 });
358 }else{
357 }else{
359 $.ajax({
358 $.ajax({
360 url: pyroutes.url('repo_refs_changelog_data', {'repo_name': '${c.repo_name}'}),
359 url: pyroutes.url('repo_refs_changelog_data', {'repo_name': '${c.repo_name}'}),
361 data: {},
360 data: {},
362 dataType: 'json',
361 dataType: 'json',
363 type: 'GET',
362 type: 'GET',
364 success: function(data) {
363 success: function(data) {
365 cache[key] = data;
364 cache[key] = data;
366 query.callback({results: data.results});
365 query.callback({results: data.results});
367 }
366 }
368 })
367 })
369 }
368 }
370 }
369 }
371 });
370 });
372
371
373 $('#branch_filter').on('change', function(e){
372 $('#branch_filter').on('change', function(e){
374 var data = $('#branch_filter').select2('data');
373 var data = $('#branch_filter').select2('data');
375 var selected = data.text;
374 var selected = data.text;
376 var filter = {'repo_name': '${c.repo_name}'};
375 var filter = {'repo_name': '${c.repo_name}'};
377 if(data.type == 'branch' || data.type == 'branch_closed'){
376 if(data.type == 'branch' || data.type == 'branch_closed'){
378 filter["branch"] = selected;
377 filter["branch"] = selected;
379 }
378 }
380 else if (data.type == 'book'){
379 else if (data.type == 'book'){
381 filter["bookmark"] = selected;
380 filter["bookmark"] = selected;
382 }
381 }
383 window.location = pyroutes.url('changelog_home', filter);
382 window.location = pyroutes.url('changelog_home', filter);
384 });
383 });
385
384
386 // Determine max number of edges per row in graph
385 // Determine max number of edges per row in graph
387 var jsdata = $.parseJSON($("[data-graph]").attr('data-graph'));
386 var jsdata = $.parseJSON($("[data-graph]").attr('data-graph'));
388 var edgeCount = 1;
387 var edgeCount = 1;
389 $.each(jsdata, function(i, item){
388 $.each(jsdata, function(i, item){
390 $.each(item[2], function(key, value) {
389 $.each(item[2], function(key, value) {
391 if (value[1] > edgeCount){
390 if (value[1] > edgeCount){
392 edgeCount = value[1];
391 edgeCount = value[1];
393 }
392 }
394 });
393 });
395 });
394 });
396 var x_step = Math.min(18, Math.floor(86 / edgeCount));
395 var x_step = Math.min(18, Math.floor(86 / edgeCount));
397 var graph_options = {
396 var graph_options = {
398 width: 100,
397 width: 100,
399 height: $("#changesets").height(),
398 height: $("#changesets").height(),
400 x_step: x_step,
399 x_step: x_step,
401 y_step: 42,
400 y_step: 42,
402 dotRadius: 3.5,
401 dotRadius: 3.5,
403 lineWidth: 2.5
402 lineWidth: 2.5
404 };
403 };
405 $("[data-graph]").commits(graph_options);
404 $("[data-graph]").commits(graph_options);
406
405
407 });
406 });
408
407
409 </script>
408 </script>
410 %else:
409 %else:
411 ${_('There are no changes yet')}
410 ${_('There are no changes yet')}
412 %endif
411 %endif
413 </div>
412 </div>
414 </div>
413 </div>
415 </%def>
414 </%def>
@@ -1,127 +1,126 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%namespace name="base" file="/base/base.html"/>
2 <%namespace name="base" file="/base/base.html"/>
3 %if c.repo_commits:
3 %if c.repo_commits:
4 <table class="rctable repo_summary table_disp">
4 <table class="rctable repo_summary table_disp">
5 <tr>
5 <tr>
6 <th>${_('Commit')}</th>
6 <th>${_('Commit')}</th>
7 <th class="status" colspan="2"></th>
7 <th class="status" colspan="2"></th>
8 <th>${_('Commit message')}</th>
8 <th>${_('Commit message')}</th>
9 <th>${_('Age')}</th>
9 <th>${_('Age')}</th>
10 <th>${_('Author')}</th>
10 <th>${_('Author')}</th>
11 <th>${_('Refs')}</th>
11 <th>${_('Refs')}</th>
12 </tr>
12 </tr>
13 %for cnt,cs in enumerate(c.repo_commits):
13 %for cnt,cs in enumerate(c.repo_commits):
14 <tr class="parity${cnt%2}">
14 <tr class="parity${cnt%2}">
15 <td class="td-commit">
15 <td class="td-commit">
16 <pre><a href="${h.url('changeset_home', repo_name=c.repo_name, revision=cs.raw_id)}">${h.show_id(cs)}</a></pre>
16 <pre><a href="${h.url('changeset_home', repo_name=c.repo_name, revision=cs.raw_id)}">${h.show_id(cs)}</a></pre>
17 </td>
17 </td>
18 <td class="td-status">
18 <td class="td-status">
19 %if c.statuses.get(cs.raw_id):
19 %if c.statuses.get(cs.raw_id):
20 <div class="changeset-status-ico shortlog">
20 <div class="changeset-status-ico shortlog">
21 %if c.statuses.get(cs.raw_id)[2]:
21 %if c.statuses.get(cs.raw_id)[2]:
22 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (c.statuses.get(cs.raw_id)[0], c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
22 <a class="tooltip" title="${_('Commit status: %s\nClick to open associated pull request #%s') % (c.statuses.get(cs.raw_id)[0], c.statuses.get(cs.raw_id)[2])}" href="${h.url('pullrequest_show',repo_name=c.statuses.get(cs.raw_id)[3],pull_request_id=c.statuses.get(cs.raw_id)[2])}">
23 <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div>
23 <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div>
24 </a>
24 </a>
25 %else:
25 %else:
26 <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div>
26 <div class="${'flag_status %s' % c.statuses.get(cs.raw_id)[0]}"></div>
27 %endif
27 %endif
28 </div>
28 </div>
29 %endif
29 %endif
30 </td>
30 </td>
31 <td class="td-comments">
31 <td class="td-comments">
32 %if c.comments.get(cs.raw_id,[]):
32 %if c.comments.get(cs.raw_id,[]):
33 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
33 <a title="${_('Commit has comments')}" href="${h.url('changeset_home',repo_name=c.repo_name,revision=cs.raw_id,anchor='comment-%s' % c.comments[cs.raw_id][0].comment_id)}">
34 <i class="icon-comment icon-comment-colored"></i> ${len(c.comments[cs.raw_id])}
34 <i class="icon-comment icon-comment-colored"></i> ${len(c.comments[cs.raw_id])}
35 </a>
35 </a>
36 %endif
36 %endif
37 </td>
37 </td>
38 <td class="td-message">
38 <td class="td-message">
39 ${h.urlify_commit_message(h.truncate(cs.message, 50), c.repo_name)}
39 ${h.urlify_commit_message(h.truncate(cs.message, 50), c.repo_name)}
40 </td>
40 </td>
41 <td class="td-time">
41 <td class="td-time">
42 ${h.age_component(cs.date)}
42 ${h.age_component(cs.date)}
43 </td>
43 </td>
44
44
45 <td class="td-user author">
45 <td class="td-user author">
46 ${base.gravatar(h.email_or_none(cs.author), 16)}
46 ${base.gravatar_with_user(cs.author)}
47 <span title="${cs.author}" class="user">${h.link_to_user(cs.author, length=22)}</span>
48 </td>
47 </td>
49 <td class="td-tags truncate-wrap">
48 <td class="td-tags truncate-wrap">
50 <div class="truncate tags-truncate"><div class="autoexpand">
49 <div class="truncate tags-truncate"><div class="autoexpand">
51 %if h.is_hg(c.rhodecode_repo):
50 %if h.is_hg(c.rhodecode_repo):
52 %for book in cs.bookmarks:
51 %for book in cs.bookmarks:
53 <span class="booktag tag" title="${_('Bookmark %s') % book}">
52 <span class="booktag tag" title="${_('Bookmark %s') % book}">
54 <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
53 <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
55 </span>
54 </span>
56 %endfor
55 %endfor
57 %endif
56 %endif
58 ## tags
57 ## tags
59 %for tag in cs.tags:
58 %for tag in cs.tags:
60 <span class="tagtag tag" title="${_('Tag %s') % tag}">
59 <span class="tagtag tag" title="${_('Tag %s') % tag}">
61 <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
60 <a href="${h.url('files_home',repo_name=c.repo_name,revision=cs.raw_id)}"><i class="icon-tag"></i>${h.shorter(tag)}</a>
62 </span>
61 </span>
63 %endfor
62 %endfor
64
63
65 ## branch
64 ## branch
66 %if cs.branch:
65 %if cs.branch:
67 <span class="branchtag tag" title="${_('Branch %s') % cs.branch}">
66 <span class="branchtag tag" title="${_('Branch %s') % cs.branch}">
68 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=cs.branch)}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a>
67 <a href="${h.url('changelog_home',repo_name=c.repo_name,branch=cs.branch)}"><i class="icon-code-fork"></i>${h.shorter(cs.branch)}</a>
69 </span>
68 </span>
70 %endif
69 %endif
71 </div>
70 </div>
72 </td>
71 </td>
73 </tr>
72 </tr>
74 %endfor
73 %endfor
75
74
76 </table>
75 </table>
77
76
78 <script type="text/javascript">
77 <script type="text/javascript">
79 $(document).pjax('#shortlog_data .pager_link','#shortlog_data', {timeout: 2000, scrollTo: false });
78 $(document).pjax('#shortlog_data .pager_link','#shortlog_data', {timeout: 2000, scrollTo: false });
80 $(document).on('pjax:success', function(){ timeagoActivate(); });
79 $(document).on('pjax:success', function(){ timeagoActivate(); });
81 </script>
80 </script>
82
81
83 <div class="pagination-wh pagination-left">
82 <div class="pagination-wh pagination-left">
84 ${c.repo_commits.pager('$link_previous ~2~ $link_next')}
83 ${c.repo_commits.pager('$link_previous ~2~ $link_next')}
85 </div>
84 </div>
86 %else:
85 %else:
87
86
88 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
87 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name):
89 <div class="quick_start">
88 <div class="quick_start">
90 <div class="fieldset">
89 <div class="fieldset">
91 <div class="left-label">${_('Add or upload files directly via RhodeCode:')}</div>
90 <div class="left-label">${_('Add or upload files directly via RhodeCode:')}</div>
92 <div class="right-content">
91 <div class="right-content">
93 <div id="add_node_id" class="add_node">
92 <div id="add_node_id" class="add_node">
94 <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='', anchor='edit')}" class="btn btn-default">${_('Add New File')}</a>
93 <a href="${h.url('files_add_home',repo_name=c.repo_name,revision=0,f_path='', anchor='edit')}" class="btn btn-default">${_('Add New File')}</a>
95 </div>
94 </div>
96 </div>
95 </div>
97 %endif
96 %endif
98 </div>
97 </div>
99
98
100 %if not h.is_svn(c.rhodecode_repo):
99 %if not h.is_svn(c.rhodecode_repo):
101 <div class="fieldset">
100 <div class="fieldset">
102 <div class="left-label">${_('Push new repo:')}</div>
101 <div class="left-label">${_('Push new repo:')}</div>
103 <div class="right-content">
102 <div class="right-content">
104 <pre>
103 <pre>
105 ${c.rhodecode_repo.alias} clone ${c.clone_repo_url}
104 ${c.rhodecode_repo.alias} clone ${c.clone_repo_url}
106 ${c.rhodecode_repo.alias} add README # add first file
105 ${c.rhodecode_repo.alias} add README # add first file
107 ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message
106 ${c.rhodecode_repo.alias} commit -m "Initial" # commit with message
108 ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back
107 ${c.rhodecode_repo.alias} push ${'origin master' if h.is_git(c.rhodecode_repo) else ''} # push changes back
109 </pre>
108 </pre>
110 </div>
109 </div>
111 </div>
110 </div>
112 <div class="fieldset">
111 <div class="fieldset">
113 <div class="left-label">${_('Existing repository?')}</div>
112 <div class="left-label">${_('Existing repository?')}</div>
114 <div class="right-content">
113 <div class="right-content">
115 <pre>
114 <pre>
116 %if h.is_git(c.rhodecode_repo):
115 %if h.is_git(c.rhodecode_repo):
117 git remote add origin ${c.clone_repo_url}
116 git remote add origin ${c.clone_repo_url}
118 git push -u origin master
117 git push -u origin master
119 %else:
118 %else:
120 hg push ${c.clone_repo_url}
119 hg push ${c.clone_repo_url}
121 %endif
120 %endif
122 </pre>
121 </pre>
123 </div>
122 </div>
124 </div>
123 </div>
125 %endif
124 %endif
126 </div>
125 </div>
127 %endif
126 %endif
@@ -1,395 +1,393 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%inherit file="/base/base.html"/>
3 <%inherit file="/base/base.html"/>
4 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
4 <%namespace name="diff_block" file="/changeset/diff_block.html"/>
5
5
6 <%def name="title()">
6 <%def name="title()">
7 ${_('%s Commit') % c.repo_name} - ${h.show_id(c.commit)}
7 ${_('%s Commit') % c.repo_name} - ${h.show_id(c.commit)}
8 %if c.rhodecode_name:
8 %if c.rhodecode_name:
9 &middot; ${h.branding(c.rhodecode_name)}
9 &middot; ${h.branding(c.rhodecode_name)}
10 %endif
10 %endif
11 </%def>
11 </%def>
12
12
13 <%def name="menu_bar_nav()">
13 <%def name="menu_bar_nav()">
14 ${self.menu_items(active='repositories')}
14 ${self.menu_items(active='repositories')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_subnav()">
17 <%def name="menu_bar_subnav()">
18 ${self.repo_menu(active='changelog')}
18 ${self.repo_menu(active='changelog')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22 <script>
22 <script>
23 // TODO: marcink switch this to pyroutes
23 // TODO: marcink switch this to pyroutes
24 AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
24 AJAX_COMMENT_DELETE_URL = "${url('changeset_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
25 templateContext.commit_data.commit_id = "${c.commit.raw_id}";
25 templateContext.commit_data.commit_id = "${c.commit.raw_id}";
26 </script>
26 </script>
27 <div class="box">
27 <div class="box">
28 <div class="title">
28 <div class="title">
29 ${self.repo_page_title(c.rhodecode_db_repo)}
29 ${self.repo_page_title(c.rhodecode_db_repo)}
30 </div>
30 </div>
31
31
32 <div id="changeset_compare_view_content" class="summary changeset">
32 <div id="changeset_compare_view_content" class="summary changeset">
33 <div class="summary-detail">
33 <div class="summary-detail">
34 <div class="summary-detail-header">
34 <div class="summary-detail-header">
35 <span class="breadcrumbs files_location">
35 <span class="breadcrumbs files_location">
36 <h4>${_('Commit')}
36 <h4>${_('Commit')}
37 <code>
37 <code>
38 ${h.show_id(c.commit)}
38 ${h.show_id(c.commit)}
39 </code>
39 </code>
40 </h4>
40 </h4>
41 </span>
41 </span>
42 <span id="parent_link">
42 <span id="parent_link">
43 <a href="#" title="${_('Parent Commit')}">${_('Parent')}</a>
43 <a href="#" title="${_('Parent Commit')}">${_('Parent')}</a>
44 </span>
44 </span>
45 |
45 |
46 <span id="child_link">
46 <span id="child_link">
47 <a href="#" title="${_('Child Commit')}">${_('Child')}</a>
47 <a href="#" title="${_('Child Commit')}">${_('Child')}</a>
48 </span>
48 </span>
49 </div>
49 </div>
50
50
51 <div class="fieldset">
51 <div class="fieldset">
52 <div class="left-label">
52 <div class="left-label">
53 ${_('Description')}:
53 ${_('Description')}:
54 </div>
54 </div>
55 <div class="right-content">
55 <div class="right-content">
56 <div id="trimmed_message_box" class="commit">${h.urlify_commit_message(c.commit.message,c.repo_name)}</div>
56 <div id="trimmed_message_box" class="commit">${h.urlify_commit_message(c.commit.message,c.repo_name)}</div>
57 <div id="message_expand" style="display:none;">
57 <div id="message_expand" style="display:none;">
58 ${_('Expand')}
58 ${_('Expand')}
59 </div>
59 </div>
60 </div>
60 </div>
61 </div>
61 </div>
62
62
63 %if c.statuses:
63 %if c.statuses:
64 <div class="fieldset">
64 <div class="fieldset">
65 <div class="left-label">
65 <div class="left-label">
66 ${_('Commit status')}:
66 ${_('Commit status')}:
67 </div>
67 </div>
68 <div class="right-content">
68 <div class="right-content">
69 <div class="changeset-status-ico">
69 <div class="changeset-status-ico">
70 <div class="${'flag_status %s' % c.statuses[0]} pull-left"></div>
70 <div class="${'flag_status %s' % c.statuses[0]} pull-left"></div>
71 </div>
71 </div>
72 <div title="${_('Commit status')}" class="changeset-status-lbl">[${h.commit_status_lbl(c.statuses[0])}]</div>
72 <div title="${_('Commit status')}" class="changeset-status-lbl">[${h.commit_status_lbl(c.statuses[0])}]</div>
73 </div>
73 </div>
74 </div>
74 </div>
75 %endif
75 %endif
76
76
77 <div class="fieldset">
77 <div class="fieldset">
78 <div class="left-label">
78 <div class="left-label">
79 ${_('References')}:
79 ${_('References')}:
80 </div>
80 </div>
81 <div class="right-content">
81 <div class="right-content">
82 <div class="tags">
82 <div class="tags">
83
83
84 %if c.commit.merge:
84 %if c.commit.merge:
85 <span class="mergetag tag">
85 <span class="mergetag tag">
86 ${_('merge')}
86 ${_('merge')}
87 </span>
87 </span>
88 %endif
88 %endif
89
89
90 %if h.is_hg(c.rhodecode_repo):
90 %if h.is_hg(c.rhodecode_repo):
91 %for book in c.commit.bookmarks:
91 %for book in c.commit.bookmarks:
92 <span class="booktag tag" title="${_('Bookmark %s') % book}">
92 <span class="booktag tag" title="${_('Bookmark %s') % book}">
93 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
93 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-bookmark"></i>${h.shorter(book)}</a>
94 </span>
94 </span>
95 %endfor
95 %endfor
96 %endif
96 %endif
97
97
98 %for tag in c.commit.tags:
98 %for tag in c.commit.tags:
99 <span class="tagtag tag" title="${_('Tag %s') % tag}">
99 <span class="tagtag tag" title="${_('Tag %s') % tag}">
100 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-tag"></i>${tag}</a>
100 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-tag"></i>${tag}</a>
101 </span>
101 </span>
102 %endfor
102 %endfor
103
103
104 %if c.commit.branch:
104 %if c.commit.branch:
105 <span class="branchtag tag" title="${_('Branch %s') % c.commit.branch}">
105 <span class="branchtag tag" title="${_('Branch %s') % c.commit.branch}">
106 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-code-fork"></i>${h.shorter(c.commit.branch)}</a>
106 <a href="${h.url('files_home',repo_name=c.repo_name,revision=c.commit.raw_id)}"><i class="icon-code-fork"></i>${h.shorter(c.commit.branch)}</a>
107 </span>
107 </span>
108 %endif
108 %endif
109 </div>
109 </div>
110 </div>
110 </div>
111 </div>
111 </div>
112
112
113 <div class="fieldset">
113 <div class="fieldset">
114 <div class="left-label">
114 <div class="left-label">
115 ${_('Diffs')}:
115 ${_('Diffs')}:
116 </div>
116 </div>
117 <div class="right-content">
117 <div class="right-content">
118 <div class="diff-actions">
118 <div class="diff-actions">
119 <a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id)}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
119 <a href="${h.url('changeset_raw_home',repo_name=c.repo_name,revision=c.commit.raw_id)}" class="tooltip" title="${h.tooltip(_('Raw diff'))}">
120 ${_('Raw Diff')}
120 ${_('Raw Diff')}
121 </a>
121 </a>
122 |
122 |
123 <a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision=c.commit.raw_id)}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
123 <a href="${h.url('changeset_patch_home',repo_name=c.repo_name,revision=c.commit.raw_id)}" class="tooltip" title="${h.tooltip(_('Patch diff'))}">
124 ${_('Patch Diff')}
124 ${_('Patch Diff')}
125 </a>
125 </a>
126 |
126 |
127 <a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision=c.commit.raw_id,diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
127 <a href="${h.url('changeset_download_home',repo_name=c.repo_name,revision=c.commit.raw_id,diff='download')}" class="tooltip" title="${h.tooltip(_('Download diff'))}">
128 ${_('Download Diff')}
128 ${_('Download Diff')}
129 </a>
129 </a>
130 |
130 |
131 ${c.ignorews_url(request.GET)}
131 ${c.ignorews_url(request.GET)}
132 |
132 |
133 ${c.context_url(request.GET)}
133 ${c.context_url(request.GET)}
134 </div>
134 </div>
135 </div>
135 </div>
136 </div>
136 </div>
137
137
138 <div class="fieldset">
138 <div class="fieldset">
139 <div class="left-label">
139 <div class="left-label">
140 ${_('Comments')}:
140 ${_('Comments')}:
141 </div>
141 </div>
142 <div class="right-content">
142 <div class="right-content">
143 <div class="comments-number">
143 <div class="comments-number">
144 %if c.comments:
144 %if c.comments:
145 <a href="#comments">${ungettext("%d Commit comment", "%d Commit comments", len(c.comments)) % len(c.comments)}</a>,
145 <a href="#comments">${ungettext("%d Commit comment", "%d Commit comments", len(c.comments)) % len(c.comments)}</a>,
146 %else:
146 %else:
147 ${ungettext("%d Commit comment", "%d Commit comments", len(c.comments)) % len(c.comments)}
147 ${ungettext("%d Commit comment", "%d Commit comments", len(c.comments)) % len(c.comments)}
148 %endif
148 %endif
149 %if c.inline_cnt:
149 %if c.inline_cnt:
150 ## this is replaced with a proper link to first comment via JS linkifyComments() func
150 ## this is replaced with a proper link to first comment via JS linkifyComments() func
151 <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
151 <a href="#inline-comments" id="inline-comments-counter">${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}</a>
152 %else:
152 %else:
153 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
153 ${ungettext("%d Inline Comment", "%d Inline Comments", c.inline_cnt) % c.inline_cnt}
154 %endif
154 %endif
155 </div>
155 </div>
156 </div>
156 </div>
157 </div>
157 </div>
158
158
159 </div> <!-- end summary-detail -->
159 </div> <!-- end summary-detail -->
160
160
161 <div id="commit-stats" class="sidebar-right">
161 <div id="commit-stats" class="sidebar-right">
162 <div class="summary-detail-header">
162 <div class="summary-detail-header">
163 <h4 class="item">
163 <h4 class="item">
164 ${_('Author')}
164 ${_('Author')}
165 </h4>
165 </h4>
166 </div>
166 </div>
167 <div class="sidebar-right-content">
167 <div class="sidebar-right-content">
168 <img alt="gravatar" class="gravatar" title="${h.email_or_none(c.commit.author)}" src="${h.gravatar_url(h.email_or_none(c.commit.author), 40)}" height="16" width="16">
168 ${self.gravatar_with_user(c.commit.author)}
169 <span class="author">
169 <div class="user-inline-data">- ${h.age_component(c.commit.date)}</div>
170 ${h.link_to_user(c.commit.author)} - ${h.age_component(c.commit.date)}
171 </span>
172 </div>
170 </div>
173 </div><!-- end sidebar -->
171 </div><!-- end sidebar -->
174 </div> <!-- end summary -->
172 </div> <!-- end summary -->
175 <div class="cs_files_title">
173 <div class="cs_files_title">
176 <span class="cs_files_expand">
174 <span class="cs_files_expand">
177 <span id="files_link"><a href="#" title="${_('Browse files at current commit')}">${_('Browse files')}</a></span> |
175 <span id="files_link"><a href="#" title="${_('Browse files at current commit')}">${_('Browse files')}</a></span> |
178
176
179 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
177 <span id="expand_all_files">${_('Expand All')}</span> | <span id="collapse_all_files">${_('Collapse All')}</span>
180 </span>
178 </span>
181 <h2>
179 <h2>
182 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
180 ${diff_block.diff_summary_text(len(c.files), c.lines_added, c.lines_deleted, c.limited_diff)}
183 </h2>
181 </h2>
184 </div>
182 </div>
185 </div>
183 </div>
186
184
187 <div class="cs_files">
185 <div class="cs_files">
188
186
189 %if not c.files:
187 %if not c.files:
190 <p class="empty_data">${_('No files')}</p>
188 <p class="empty_data">${_('No files')}</p>
191 %endif
189 %endif
192
190
193 <table class="compare_view_files commit_diff">
191 <table class="compare_view_files commit_diff">
194 %for FID, (cs1, cs2, change, path, diff, stats, file) in c.changes[c.commit.raw_id].iteritems():
192 %for FID, (cs1, cs2, change, path, diff, stats, file) in c.changes[c.commit.raw_id].iteritems():
195 <tr class="cs_${change} collapse_file" fid="${FID}">
193 <tr class="cs_${change} collapse_file" fid="${FID}">
196 <td class="cs_icon_td">
194 <td class="cs_icon_td">
197 <span class="collapse_file_icon" fid="${FID}"></span>
195 <span class="collapse_file_icon" fid="${FID}"></span>
198 </td>
196 </td>
199 <td class="cs_icon_td">
197 <td class="cs_icon_td">
200 <div class="flag_status not_reviewed hidden"></div>
198 <div class="flag_status not_reviewed hidden"></div>
201 </td>
199 </td>
202 <td class="cs_${change}" id="a_${FID}">
200 <td class="cs_${change}" id="a_${FID}">
203 <div class="node">
201 <div class="node">
204 <a href="#a_${FID}">
202 <a href="#a_${FID}">
205 <i class="icon-file-${change.lower()}"></i>
203 <i class="icon-file-${change.lower()}"></i>
206 ${h.safe_unicode(path)}
204 ${h.safe_unicode(path)}
207 </a>
205 </a>
208 </div>
206 </div>
209 </td>
207 </td>
210 <td>
208 <td>
211 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
209 <div class="changes pull-right">${h.fancy_file_stats(stats)}</div>
212 <div class="comment-bubble pull-right" data-path="${path}">
210 <div class="comment-bubble pull-right" data-path="${path}">
213 <i class="icon-comment"></i>
211 <i class="icon-comment"></i>
214 </div>
212 </div>
215 </td>
213 </td>
216 </tr>
214 </tr>
217 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
215 <tr fid="${FID}" id="diff_${FID}" class="diff_links">
218 <td></td>
216 <td></td>
219 <td></td>
217 <td></td>
220 <td class="cs_${change}">
218 <td class="cs_${change}">
221 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), cs1, cs2, change, file)}
219 ${diff_block.diff_menu(c.repo_name, h.safe_unicode(path), cs1, cs2, change, file)}
222 </td>
220 </td>
223 <td class="td-actions rc-form">
221 <td class="td-actions rc-form">
224 ${c.ignorews_url(request.GET, h.FID(cs2,path))} |
222 ${c.ignorews_url(request.GET, h.FID(cs2,path))} |
225 ${c.context_url(request.GET, h.FID(cs2,path))} |
223 ${c.context_url(request.GET, h.FID(cs2,path))} |
226 <div data-comment-id="${h.FID(cs2,path)}" class="btn-link show-inline-comments comments-visible">
224 <div data-comment-id="${h.FID(cs2,path)}" class="btn-link show-inline-comments comments-visible">
227 <span class="comments-show">${_('Show comments')}</span>
225 <span class="comments-show">${_('Show comments')}</span>
228 <span class="comments-hide">${_('Hide comments')}</span>
226 <span class="comments-hide">${_('Hide comments')}</span>
229 </div>
227 </div>
230 </td>
228 </td>
231 </tr>
229 </tr>
232 <tr id="tr_${FID}">
230 <tr id="tr_${FID}">
233 <td></td>
231 <td></td>
234 <td></td>
232 <td></td>
235 <td class="injected_diff" colspan="2">
233 <td class="injected_diff" colspan="2">
236 <div class="diff-container" id="${'diff-container-%s' % (id(change))}">
234 <div class="diff-container" id="${'diff-container-%s' % (id(change))}">
237 <div id="${FID}" class="diffblock margined comm">
235 <div id="${FID}" class="diffblock margined comm">
238 <div class="code-body">
236 <div class="code-body">
239 <div class="full_f_path" path="${h.safe_unicode(path)}"></div>
237 <div class="full_f_path" path="${h.safe_unicode(path)}"></div>
240 ${diff|n}
238 ${diff|n}
241 % if file and file["is_limited_diff"]:
239 % if file and file["is_limited_diff"]:
242 % if file["exceeds_limit"]:
240 % if file["exceeds_limit"]:
243 ${diff_block.file_message()}
241 ${diff_block.file_message()}
244 % else:
242 % else:
245 <h5>${_('Diff was truncated. File content available only in full diff.')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a></h5>
243 <h5>${_('Diff was truncated. File content available only in full diff.')} <a href="${h.url.current(fulldiff=1, **request.GET.mixed())}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a></h5>
246 % endif
244 % endif
247 % endif
245 % endif
248 </div>
246 </div>
249 </div>
247 </div>
250 </div>
248 </div>
251 </td>
249 </td>
252 </tr>
250 </tr>
253 %endfor
251 %endfor
254 </table>
252 </table>
255 </div>
253 </div>
256
254
257 % if c.limited_diff:
255 % if c.limited_diff:
258 ${diff_block.changeset_message()}
256 ${diff_block.changeset_message()}
259 % endif
257 % endif
260
258
261 ## template for inline comment form
259 ## template for inline comment form
262 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
260 <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
263 ${comment.comment_inline_form()}
261 ${comment.comment_inline_form()}
264
262
265 ## render comments and inlines
263 ## render comments and inlines
266 ${comment.generate_comments()}
264 ${comment.generate_comments()}
267
265
268 ## main comment form and it status
266 ## main comment form and it status
269 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision=c.commit.raw_id),
267 ${comment.comments(h.url('changeset_comment', repo_name=c.repo_name, revision=c.commit.raw_id),
270 h.commit_status(c.rhodecode_db_repo, c.commit.raw_id))}
268 h.commit_status(c.rhodecode_db_repo, c.commit.raw_id))}
271
269
272 ## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
270 ## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
273 <script type="text/javascript">
271 <script type="text/javascript">
274
272
275 $(document).ready(function() {
273 $(document).ready(function() {
276
274
277 var boxmax = parseInt($('#trimmed_message_box').css('max-height'), 10);
275 var boxmax = parseInt($('#trimmed_message_box').css('max-height'), 10);
278 if($('#trimmed_message_box').height() === boxmax){
276 if($('#trimmed_message_box').height() === boxmax){
279 $('#message_expand').show();
277 $('#message_expand').show();
280 }
278 }
281
279
282 $('#message_expand').on('click', function(e){
280 $('#message_expand').on('click', function(e){
283 $('#trimmed_message_box').css('max-height', 'none');
281 $('#trimmed_message_box').css('max-height', 'none');
284 $(this).hide();
282 $(this).hide();
285 });
283 });
286
284
287 $('.show-inline-comments').on('click', function(e){
285 $('.show-inline-comments').on('click', function(e){
288 var boxid = $(this).attr('data-comment-id');
286 var boxid = $(this).attr('data-comment-id');
289 var button = $(this);
287 var button = $(this);
290
288
291 if(button.hasClass("comments-visible")) {
289 if(button.hasClass("comments-visible")) {
292 $('#{0} .inline-comments'.format(boxid)).each(function(index){
290 $('#{0} .inline-comments'.format(boxid)).each(function(index){
293 $(this).hide();
291 $(this).hide();
294 })
292 })
295 button.removeClass("comments-visible");
293 button.removeClass("comments-visible");
296 } else {
294 } else {
297 $('#{0} .inline-comments'.format(boxid)).each(function(index){
295 $('#{0} .inline-comments'.format(boxid)).each(function(index){
298 $(this).show();
296 $(this).show();
299 })
297 })
300 button.addClass("comments-visible");
298 button.addClass("comments-visible");
301 }
299 }
302 });
300 });
303
301
304
302
305 // next links
303 // next links
306 $('#child_link').on('click', function(e){
304 $('#child_link').on('click', function(e){
307 // fetch via ajax what is going to be the next link, if we have
305 // fetch via ajax what is going to be the next link, if we have
308 // >1 links show them to user to choose
306 // >1 links show them to user to choose
309 if(!$('#child_link').hasClass('disabled')){
307 if(!$('#child_link').hasClass('disabled')){
310 $.ajax({
308 $.ajax({
311 url: '${h.url('changeset_children',repo_name=c.repo_name, revision=c.commit.raw_id)}',
309 url: '${h.url('changeset_children',repo_name=c.repo_name, revision=c.commit.raw_id)}',
312 success: function(data) {
310 success: function(data) {
313 if(data.results.length === 0){
311 if(data.results.length === 0){
314 $('#child_link').html('${_('No Child Commits')}').addClass('disabled');
312 $('#child_link').html('${_('No Child Commits')}').addClass('disabled');
315 }
313 }
316 if(data.results.length === 1){
314 if(data.results.length === 1){
317 var commit = data.results[0];
315 var commit = data.results[0];
318 window.location = pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': commit.raw_id});
316 window.location = pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': commit.raw_id});
319 }
317 }
320 else if(data.results.length === 2){
318 else if(data.results.length === 2){
321 $('#child_link').addClass('disabled');
319 $('#child_link').addClass('disabled');
322 $('#child_link').addClass('double');
320 $('#child_link').addClass('double');
323 var _html = '';
321 var _html = '';
324 _html +='<a title="__title__" href="__url__">__rev__</a> '
322 _html +='<a title="__title__" href="__url__">__rev__</a> '
325 .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
323 .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
326 .replace('__title__', data.results[0].message)
324 .replace('__title__', data.results[0].message)
327 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[0].raw_id}));
325 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[0].raw_id}));
328 _html +=' | '
326 _html +=' | '
329 _html +='<a title="__title__" href="__url__">__rev__</a> '
327 _html +='<a title="__title__" href="__url__">__rev__</a> '
330 .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
328 .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
331 .replace('__title__', data.results[1].message)
329 .replace('__title__', data.results[1].message)
332 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[1].raw_id}));
330 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[1].raw_id}));
333 $('#child_link').html(_html);
331 $('#child_link').html(_html);
334 }
332 }
335 }
333 }
336 });
334 });
337 e.preventDefault();
335 e.preventDefault();
338 }
336 }
339 });
337 });
340
338
341 // prev links
339 // prev links
342 $('#parent_link').on('click', function(e){
340 $('#parent_link').on('click', function(e){
343 // fetch via ajax what is going to be the next link, if we have
341 // fetch via ajax what is going to be the next link, if we have
344 // >1 links show them to user to choose
342 // >1 links show them to user to choose
345 if(!$('#parent_link').hasClass('disabled')){
343 if(!$('#parent_link').hasClass('disabled')){
346 $.ajax({
344 $.ajax({
347 url: '${h.url('changeset_parents',repo_name=c.repo_name, revision=c.commit.raw_id)}',
345 url: '${h.url('changeset_parents',repo_name=c.repo_name, revision=c.commit.raw_id)}',
348 success: function(data) {
346 success: function(data) {
349 if(data.results.length === 0){
347 if(data.results.length === 0){
350 $('#parent_link').html('${_('No Parent Commits')}').addClass('disabled');
348 $('#parent_link').html('${_('No Parent Commits')}').addClass('disabled');
351 }
349 }
352 if(data.results.length === 1){
350 if(data.results.length === 1){
353 var commit = data.results[0];
351 var commit = data.results[0];
354 window.location = pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': commit.raw_id});
352 window.location = pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': commit.raw_id});
355 }
353 }
356 else if(data.results.length === 2){
354 else if(data.results.length === 2){
357 $('#parent_link').addClass('disabled');
355 $('#parent_link').addClass('disabled');
358 $('#parent_link').addClass('double');
356 $('#parent_link').addClass('double');
359 var _html = '';
357 var _html = '';
360 _html +='<a title="__title__" href="__url__">Parent __rev__</a>'
358 _html +='<a title="__title__" href="__url__">Parent __rev__</a>'
361 .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
359 .replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
362 .replace('__title__', data.results[0].message)
360 .replace('__title__', data.results[0].message)
363 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[0].raw_id}));
361 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[0].raw_id}));
364 _html +=' | '
362 _html +=' | '
365 _html +='<a title="__title__" href="__url__">Parent __rev__</a>'
363 _html +='<a title="__title__" href="__url__">Parent __rev__</a>'
366 .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
364 .replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
367 .replace('__title__', data.results[1].message)
365 .replace('__title__', data.results[1].message)
368 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[1].raw_id}));
366 .replace('__url__', pyroutes.url('changeset_home', {'repo_name': '${c.repo_name}','revision': data.results[1].raw_id}));
369 $('#parent_link').html(_html);
367 $('#parent_link').html(_html);
370 }
368 }
371 }
369 }
372 });
370 });
373 e.preventDefault();
371 e.preventDefault();
374 }
372 }
375 });
373 });
376
374
377 if (location.href.indexOf('#') != -1) {
375 if (location.href.indexOf('#') != -1) {
378 var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#');
376 var id = '#'+location.href.substring(location.href.indexOf('#') + 1).split('#');
379 var line = $('html').find(id);
377 var line = $('html').find(id);
380 offsetScroll(line, 70);
378 offsetScroll(line, 70);
381 }
379 }
382
380
383 // browse tree @ revision
381 // browse tree @ revision
384 $('#files_link').on('click', function(e){
382 $('#files_link').on('click', function(e){
385 window.location = '${h.url('files_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path='')}';
383 window.location = '${h.url('files_home',repo_name=c.repo_name, revision=c.commit.raw_id, f_path='')}';
386 e.preventDefault();
384 e.preventDefault();
387 });
385 });
388
386
389 // inject comments into their proper positions
387 // inject comments into their proper positions
390 var file_comments = $('.inline-comment-placeholder');
388 var file_comments = $('.inline-comment-placeholder');
391 renderInlineComments(file_comments, true);
389 renderInlineComments(file_comments, true);
392 })
390 })
393 </script>
391 </script>
394
392
395 </%def>
393 </%def>
@@ -1,297 +1,299 b''
1 ## DATA TABLE RE USABLE ELEMENTS
1 ## DATA TABLE RE USABLE ELEMENTS
2 ## usage:
2 ## usage:
3 ## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
3 ## <%namespace name="dt" file="/data_table/_dt_elements.html"/>
4 <%namespace name="base" file="/base/base.html"/>
4 <%namespace name="base" file="/base/base.html"/>
5
5
6 ## REPOSITORY RENDERERS
6 ## REPOSITORY RENDERERS
7 <%def name="quick_menu(repo_name)">
7 <%def name="quick_menu(repo_name)">
8 <i class="pointer icon-more"></i>
8 <i class="pointer icon-more"></i>
9 <div class="menu_items_container hidden">
9 <div class="menu_items_container hidden">
10 <ul class="menu_items">
10 <ul class="menu_items">
11 <li>
11 <li>
12 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}">
12 <a title="${_('Summary')}" href="${h.url('summary_home',repo_name=repo_name)}">
13 <span>${_('Summary')}</span>
13 <span>${_('Summary')}</span>
14 </a>
14 </a>
15 </li>
15 </li>
16 <li>
16 <li>
17 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}">
17 <a title="${_('Changelog')}" href="${h.url('changelog_home',repo_name=repo_name)}">
18 <span>${_('Changelog')}</span>
18 <span>${_('Changelog')}</span>
19 </a>
19 </a>
20 </li>
20 </li>
21 <li>
21 <li>
22 <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}">
22 <a title="${_('Files')}" href="${h.url('files_home',repo_name=repo_name)}">
23 <span>${_('Files')}</span>
23 <span>${_('Files')}</span>
24 </a>
24 </a>
25 </li>
25 </li>
26 <li>
26 <li>
27 <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}">
27 <a title="${_('Fork')}" href="${h.url('repo_fork_home',repo_name=repo_name)}">
28 <span>${_('Fork')}</span>
28 <span>${_('Fork')}</span>
29 </a>
29 </a>
30 </li>
30 </li>
31 </ul>
31 </ul>
32 </div>
32 </div>
33 </%def>
33 </%def>
34
34
35 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
35 <%def name="repo_name(name,rtype,rstate,private,fork_of,short_name=False,admin=False)">
36 <%
36 <%
37 def get_name(name,short_name=short_name):
37 def get_name(name,short_name=short_name):
38 if short_name:
38 if short_name:
39 return name.split('/')[-1]
39 return name.split('/')[-1]
40 else:
40 else:
41 return name
41 return name
42 %>
42 %>
43 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
43 <div class="${'repo_state_pending' if rstate == 'repo_state_pending' else ''} truncate">
44 ##NAME
44 ##NAME
45 <a href="${h.url('edit_repo' if admin else 'summary_home',repo_name=name)}">
45 <a href="${h.url('edit_repo' if admin else 'summary_home',repo_name=name)}">
46
46
47 ##TYPE OF REPO
47 ##TYPE OF REPO
48 %if h.is_hg(rtype):
48 %if h.is_hg(rtype):
49 <span title="${_('Mercurial repository')}"><i class="icon-hg"></i></span>
49 <span title="${_('Mercurial repository')}"><i class="icon-hg"></i></span>
50 %elif h.is_git(rtype):
50 %elif h.is_git(rtype):
51 <span title="${_('Git repository')}"><i class="icon-git"></i></span>
51 <span title="${_('Git repository')}"><i class="icon-git"></i></span>
52 %elif h.is_svn(rtype):
52 %elif h.is_svn(rtype):
53 <span title="${_('Subversion repository')}"><i class="icon-svn"></i></span>
53 <span title="${_('Subversion repository')}"><i class="icon-svn"></i></span>
54 %endif
54 %endif
55
55
56 ##PRIVATE/PUBLIC
56 ##PRIVATE/PUBLIC
57 %if private and c.visual.show_private_icon:
57 %if private and c.visual.show_private_icon:
58 <i class="icon-lock" title="${_('Private repository')}"></i>
58 <i class="icon-lock" title="${_('Private repository')}"></i>
59 %elif not private and c.visual.show_public_icon:
59 %elif not private and c.visual.show_public_icon:
60 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
60 <i class="icon-unlock-alt" title="${_('Public repository')}"></i>
61 %else:
61 %else:
62 <span></span>
62 <span></span>
63 %endif
63 %endif
64 ${get_name(name)}
64 ${get_name(name)}
65 </a>
65 </a>
66 %if fork_of:
66 %if fork_of:
67 <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
67 <a href="${h.url('summary_home',repo_name=fork_of.repo_name)}"><i class="icon-code-fork"></i></a>
68 %endif
68 %endif
69 %if rstate == 'repo_state_pending':
69 %if rstate == 'repo_state_pending':
70 <i class="icon-cogs" title="${_('Repository creating in progress...')}"></i>
70 <i class="icon-cogs" title="${_('Repository creating in progress...')}"></i>
71 %endif
71 %endif
72 </div>
72 </div>
73 </%def>
73 </%def>
74
74
75 <%def name="last_change(last_change)">
75 <%def name="last_change(last_change)">
76 ${h.age_component(last_change)}
76 ${h.age_component(last_change)}
77 </%def>
77 </%def>
78
78
79 <%def name="revision(name,rev,tip,author,last_msg)">
79 <%def name="revision(name,rev,tip,author,last_msg)">
80 <div>
80 <div>
81 %if rev >= 0:
81 %if rev >= 0:
82 <code><a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></code>
82 <code><a title="${h.tooltip('%s:\n\n%s' % (author,last_msg))}" class="tooltip" href="${h.url('changeset_home',repo_name=name,revision=tip)}">${'r%s:%s' % (rev,h.short_id(tip))}</a></code>
83 %else:
83 %else:
84 ${_('No commits yet')}
84 ${_('No commits yet')}
85 %endif
85 %endif
86 </div>
86 </div>
87 </%def>
87 </%def>
88
88
89 <%def name="rss(name)">
89 <%def name="rss(name)">
90 %if c.rhodecode_user.username != h.DEFAULT_USER:
90 %if c.rhodecode_user.username != h.DEFAULT_USER:
91 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a>
91 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a>
92 %else:
92 %else:
93 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a>
93 <a title="${_('Subscribe to %s rss feed')% name}" href="${h.url('rss_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a>
94 %endif
94 %endif
95 </%def>
95 </%def>
96
96
97 <%def name="atom(name)">
97 <%def name="atom(name)">
98 %if c.rhodecode_user.username != h.DEFAULT_USER:
98 %if c.rhodecode_user.username != h.DEFAULT_USER:
99 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a>
99 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name,auth_token=c.rhodecode_user.feed_token)}"><i class="icon-rss-sign"></i></a>
100 %else:
100 %else:
101 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a>
101 <a title="${_('Subscribe to %s atom feed')% name}" href="${h.url('atom_feed_home',repo_name=name)}"><i class="icon-rss-sign"></i></a>
102 %endif
102 %endif
103 </%def>
103 </%def>
104
104
105 <%def name="user_gravatar(email, size=16)">
105 <%def name="user_gravatar(email, size=16)">
106 <div class="rc-user tooltip" title="${h.author_string(email)}">
106 ${base.gravatar(email, 16)}
107 ${base.gravatar(email, 16)}
108 </div>
107 </%def>
109 </%def>
108
110
109 <%def name="repo_actions(repo_name, super_user=True)">
111 <%def name="repo_actions(repo_name, super_user=True)">
110 <div>
112 <div>
111 <div class="grid_edit">
113 <div class="grid_edit">
112 <a href="${h.url('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
114 <a href="${h.url('edit_repo',repo_name=repo_name)}" title="${_('Edit')}">
113 <i class="icon-pencil"></i>Edit</a>
115 <i class="icon-pencil"></i>Edit</a>
114 </div>
116 </div>
115 <div class="grid_delete">
117 <div class="grid_delete">
116 ${h.secure_form(h.url('repo', repo_name=repo_name),method='delete')}
118 ${h.secure_form(h.url('repo', repo_name=repo_name),method='delete')}
117 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
119 ${h.submit('remove_%s' % repo_name,_('Delete'),class_="btn btn-link btn-danger",
118 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
120 onclick="return confirm('"+_('Confirm to delete this repository: %s') % repo_name+"');")}
119 ${h.end_form()}
121 ${h.end_form()}
120 </div>
122 </div>
121 </div>
123 </div>
122 </%def>
124 </%def>
123
125
124 <%def name="repo_state(repo_state)">
126 <%def name="repo_state(repo_state)">
125 <div>
127 <div>
126 %if repo_state == 'repo_state_pending':
128 %if repo_state == 'repo_state_pending':
127 <div class="tag tag4">${_('Creating')}</div>
129 <div class="tag tag4">${_('Creating')}</div>
128 %elif repo_state == 'repo_state_created':
130 %elif repo_state == 'repo_state_created':
129 <div class="tag tag1">${_('Created')}</div>
131 <div class="tag tag1">${_('Created')}</div>
130 %else:
132 %else:
131 <div class="tag alert2" title="${repo_state}">invalid</div>
133 <div class="tag alert2" title="${repo_state}">invalid</div>
132 %endif
134 %endif
133 </div>
135 </div>
134 </%def>
136 </%def>
135
137
136
138
137 ## REPO GROUP RENDERERS
139 ## REPO GROUP RENDERERS
138 <%def name="quick_repo_group_menu(repo_group_name)">
140 <%def name="quick_repo_group_menu(repo_group_name)">
139 <i class="pointer icon-more"></i>
141 <i class="pointer icon-more"></i>
140 <div class="menu_items_container hidden">
142 <div class="menu_items_container hidden">
141 <ul class="menu_items">
143 <ul class="menu_items">
142 <li>
144 <li>
143 <a href="${h.url('repo_group_home',group_name=repo_group_name)}">
145 <a href="${h.url('repo_group_home',group_name=repo_group_name)}">
144 <span class="icon">
146 <span class="icon">
145 <i class="icon-file-text"></i>
147 <i class="icon-file-text"></i>
146 </span>
148 </span>
147 <span>${_('Summary')}</span>
149 <span>${_('Summary')}</span>
148 </a>
150 </a>
149 </li>
151 </li>
150
152
151 </ul>
153 </ul>
152 </div>
154 </div>
153 </%def>
155 </%def>
154
156
155 <%def name="repo_group_name(repo_group_name, children_groups=None)">
157 <%def name="repo_group_name(repo_group_name, children_groups=None)">
156 <div>
158 <div>
157 <a href="${h.url('repo_group_home',group_name=repo_group_name)}">
159 <a href="${h.url('repo_group_home',group_name=repo_group_name)}">
158 <i class="icon-folder-close" title="${_('Repository group')}"></i>
160 <i class="icon-folder-close" title="${_('Repository group')}"></i>
159 %if children_groups:
161 %if children_groups:
160 ${h.literal(' &raquo; '.join(children_groups))}
162 ${h.literal(' &raquo; '.join(children_groups))}
161 %else:
163 %else:
162 ${repo_group_name}
164 ${repo_group_name}
163 %endif
165 %endif
164 </a>
166 </a>
165 </div>
167 </div>
166 </%def>
168 </%def>
167
169
168 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
170 <%def name="repo_group_actions(repo_group_id, repo_group_name, gr_count)">
169 <div class="grid_edit">
171 <div class="grid_edit">
170 <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
172 <a href="${h.url('edit_repo_group',group_name=repo_group_name)}" title="${_('Edit')}">Edit</a>
171 </div>
173 </div>
172 <div class="grid_delete">
174 <div class="grid_delete">
173 ${h.secure_form(h.url('delete_repo_group', group_name=repo_group_name),method='delete')}
175 ${h.secure_form(h.url('delete_repo_group', group_name=repo_group_name),method='delete')}
174 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
176 ${h.submit('remove_%s' % repo_group_name,_('Delete'),class_="btn btn-link btn-danger",
175 onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
177 onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_count) % (repo_group_name, gr_count)+"');")}
176 ${h.end_form()}
178 ${h.end_form()}
177 </div>
179 </div>
178 </%def>
180 </%def>
179
181
180
182
181 <%def name="user_actions(user_id, username)">
183 <%def name="user_actions(user_id, username)">
182 <div class="grid_edit">
184 <div class="grid_edit">
183 <a href="${h.url('edit_user',user_id=user_id)}" title="${_('Edit')}">
185 <a href="${h.url('edit_user',user_id=user_id)}" title="${_('Edit')}">
184 <i class="icon-pencil"></i>Edit</a>
186 <i class="icon-pencil"></i>Edit</a>
185 </div>
187 </div>
186 <div class="grid_delete">
188 <div class="grid_delete">
187 ${h.secure_form(h.url('delete_user', user_id=user_id),method='delete')}
189 ${h.secure_form(h.url('delete_user', user_id=user_id),method='delete')}
188 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
190 ${h.submit('remove_',_('Delete'),id="remove_user_%s" % user_id, class_="btn btn-link btn-danger",
189 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
191 onclick="return confirm('"+_('Confirm to delete this user: %s') % username+"');")}
190 ${h.end_form()}
192 ${h.end_form()}
191 </div>
193 </div>
192 </%def>
194 </%def>
193
195
194 <%def name="user_group_actions(user_group_id, user_group_name)">
196 <%def name="user_group_actions(user_group_id, user_group_name)">
195 <div class="grid_edit">
197 <div class="grid_edit">
196 <a href="${h.url('edit_users_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
198 <a href="${h.url('edit_users_group', user_group_id=user_group_id)}" title="${_('Edit')}">Edit</a>
197 </div>
199 </div>
198 <div class="grid_delete">
200 <div class="grid_delete">
199 ${h.secure_form(h.url('delete_users_group', user_group_id=user_group_id),method='delete')}
201 ${h.secure_form(h.url('delete_users_group', user_group_id=user_group_id),method='delete')}
200 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
202 ${h.submit('remove_',_('Delete'),id="remove_group_%s" % user_group_id, class_="btn btn-link btn-danger",
201 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
203 onclick="return confirm('"+_('Confirm to delete this user group: %s') % user_group_name+"');")}
202 ${h.end_form()}
204 ${h.end_form()}
203 </div>
205 </div>
204 </%def>
206 </%def>
205
207
206
208
207 <%def name="user_name(user_id, username)">
209 <%def name="user_name(user_id, username)">
208 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.url('edit_user', user_id=user_id))}
210 ${h.link_to(h.person(username, 'username_or_name_or_email'), h.url('edit_user', user_id=user_id))}
209 </%def>
211 </%def>
210
212
211 <%def name="user_profile(username)">
213 <%def name="user_profile(username)">
212 ${base.gravatar_with_user(username, 16)}
214 ${base.gravatar_with_user(username, 16)}
213 </%def>
215 </%def>
214
216
215 <%def name="user_group_name(user_group_id, user_group_name)">
217 <%def name="user_group_name(user_group_id, user_group_name)">
216 <div>
218 <div>
217 <a href="${h.url('edit_users_group', user_group_id=user_group_id)}">
219 <a href="${h.url('edit_users_group', user_group_id=user_group_id)}">
218 <i class="icon-group" title="${_('User group')}"></i> ${user_group_name}</a>
220 <i class="icon-group" title="${_('User group')}"></i> ${user_group_name}</a>
219 </div>
221 </div>
220 </%def>
222 </%def>
221
223
222
224
223 ## GISTS
225 ## GISTS
224
226
225 <%def name="gist_gravatar(full_contact)">
227 <%def name="gist_gravatar(full_contact)">
226 <div class="gist_gravatar">
228 <div class="gist_gravatar">
227 ${base.gravatar(full_contact, 30)}
229 ${base.gravatar(full_contact, 30)}
228 </div>
230 </div>
229 </%def>
231 </%def>
230
232
231 <%def name="gist_access_id(gist_access_id, full_contact)">
233 <%def name="gist_access_id(gist_access_id, full_contact)">
232 <div>
234 <div>
233 <b>
235 <b>
234 <a href="${h.url('gist',gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
236 <a href="${h.url('gist',gist_id=gist_access_id)}">gist: ${gist_access_id}</a>
235 </b>
237 </b>
236 </div>
238 </div>
237 </%def>
239 </%def>
238
240
239 <%def name="gist_author(full_contact, created_on, expires)">
241 <%def name="gist_author(full_contact, created_on, expires)">
240 ${base.gravatar_with_user(full_contact, 16)}
242 ${base.gravatar_with_user(full_contact, 16)}
241 </%def>
243 </%def>
242
244
243
245
244 <%def name="gist_created(created_on)">
246 <%def name="gist_created(created_on)">
245 <div class="created">
247 <div class="created">
246 ${h.age_component(created_on, time_is_local=True)}
248 ${h.age_component(created_on, time_is_local=True)}
247 </div>
249 </div>
248 </%def>
250 </%def>
249
251
250 <%def name="gist_expires(expires)">
252 <%def name="gist_expires(expires)">
251 <div class="created">
253 <div class="created">
252 %if expires == -1:
254 %if expires == -1:
253 ${_('never')}
255 ${_('never')}
254 %else:
256 %else:
255 ${h.age_component(h.time_to_utcdatetime(expires))}
257 ${h.age_component(h.time_to_utcdatetime(expires))}
256 %endif
258 %endif
257 </div>
259 </div>
258 </%def>
260 </%def>
259
261
260 <%def name="gist_type(gist_type)">
262 <%def name="gist_type(gist_type)">
261 %if gist_type != 'public':
263 %if gist_type != 'public':
262 <div class="tag">${_('Private')}</div>
264 <div class="tag">${_('Private')}</div>
263 %endif
265 %endif
264 </%def>
266 </%def>
265
267
266 <%def name="gist_description(gist_description)">
268 <%def name="gist_description(gist_description)">
267 ${gist_description}
269 ${gist_description}
268 </%def>
270 </%def>
269
271
270
272
271 ## PULL REQUESTS GRID RENDERERS
273 ## PULL REQUESTS GRID RENDERERS
272 <%def name="pullrequest_status(status)">
274 <%def name="pullrequest_status(status)">
273 <div class="${'flag_status %s' % status} pull-left"></div>
275 <div class="${'flag_status %s' % status} pull-left"></div>
274 </%def>
276 </%def>
275
277
276 <%def name="pullrequest_title(title, description)">
278 <%def name="pullrequest_title(title, description)">
277 ${title} <br/>
279 ${title} <br/>
278 ${h.shorter(description, 40)}
280 ${h.shorter(description, 40)}
279 </%def>
281 </%def>
280
282
281 <%def name="pullrequest_comments(comments_nr)">
283 <%def name="pullrequest_comments(comments_nr)">
282 <i class="icon-comment icon-comment-colored"></i> ${comments_nr}
284 <i class="icon-comment icon-comment-colored"></i> ${comments_nr}
283 </%def>
285 </%def>
284
286
285 <%def name="pullrequest_name(pull_request_id, target_repo_name)">
287 <%def name="pullrequest_name(pull_request_id, target_repo_name)">
286 <a href="${h.url('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
288 <a href="${h.url('pullrequest_show',repo_name=target_repo_name,pull_request_id=pull_request_id)}">
287 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
289 ${_('Pull request #%(pr_number)s') % {'pr_number': pull_request_id,}}
288 </a>
290 </a>
289 </%def>
291 </%def>
290
292
291 <%def name="pullrequest_updated_on(updated_on)">
293 <%def name="pullrequest_updated_on(updated_on)">
292 ${h.age_component(h.time_to_utcdatetime(updated_on))}
294 ${h.age_component(h.time_to_utcdatetime(updated_on))}
293 </%def>
295 </%def>
294
296
295 <%def name="pullrequest_author(full_contact)">
297 <%def name="pullrequest_author(full_contact)">
296 ${base.gravatar_with_user(full_contact, 16)}
298 ${base.gravatar_with_user(full_contact, 16)}
297 </%def>
299 </%def>
@@ -1,36 +1,30 b''
1 <%namespace name="base" file="/base/base.html"/>
1 <%namespace name="base" file="/base/base.html"/>
2
2
3 <div class="summary-detail-header">
3 <div class="summary-detail-header">
4 <h4 class="item">
4 <h4 class="item">
5 % if c.file_author:
5 % if c.file_author:
6 ${_('Last Author')}
6 ${_('Last Author')}
7 % else:
7 % else:
8 ${h.literal(ungettext(u'File Author (%s)',u'File Authors (%s)',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }
8 ${h.literal(ungettext(u'File Author (%s)',u'File Authors (%s)',len(c.authors)) % ('<b>%s</b>' % len(c.authors))) }
9 % endif
9 % endif
10 </h4>
10 </h4>
11 <a href="#" id="show_authors" class="action_link">${_('Show All')}</a>
11 <a href="#" id="show_authors" class="action_link">${_('Show All')}</a>
12 </div>
12 </div>
13
13
14 % if c.authors:
14 % if c.authors:
15 <ul class="pull-left">
15 <ul class="sidebar-right-content">
16 % for email, user in sorted(c.authors, key=lambda e: c.file_last_commit.author_email!=e[0]):
16 % for email, user in sorted(c.authors, key=lambda e: c.file_last_commit.author_email!=e[0]):
17 <li class="file_author">
17 <li class="file_author">
18 <div class="contributor tooltip" title="${h.tooltip(user)}">
18 <div class="rc-user tooltip" title="${h.author_string(email)}">
19 ${base.gravatar(email, 16)}
19 ${base.gravatar(email, 16)}
20 <span class="author user">
20 <span class="user">${h.link_to_user(user)}</span>
21 ## case initial page load we only have last commit author
21 </div>
22 % if c.file_author:
22 % if c.file_author:
23 ${h.link_to_user(user)} - ${h.age_component(c.file_last_commit.date)}
23 <div class="user-inline-data">- ${h.age_component(c.file_last_commit.date)}</div>
24 % else:
24 % elif c.file_last_commit.author_email==email:
25 % if c.file_last_commit.author_email==email:
25 <div class="user-inline-data"> (${_('last author')})</div>
26 <strong>${h.link_to_user(user)}</strong> (${_('last author')})
27 % else:
28 ${h.link_to_user(user)}
29 % endif
26 % endif
30 % endif
31 </span>
32 </div>
33 </li>
27 </li>
34 % endfor
28 % endfor
35 </ul>
29 </ul>
36 % endif
30 % endif
@@ -1,14 +1,12 b''
1 <%namespace name="base" file="/base/base.html"/>
1 <%namespace name="base" file="/base/base.html"/>
2
2
3 <div class="summary-detail-header">
3 <div class="summary-detail-header">
4 <h4 class="item">
4 <h4 class="item">
5 ${_('Commit Author')}
5 ${_('Commit Author')}
6 </h4>
6 </h4>
7 </div>
7 </div>
8 <div class="sidebar-right-content">
8 <div class="sidebar-right-content">
9 ${base.gravatar(h.email_or_none(c.commit.author), 16)}
9 ${base.gravatar_with_user(c.commit.author)}
10 <span class="author user">
10 <div class="user-inline-data">- ${h.age_component(c.commit.date)}</div>
11 ${h.link_to_user(c.commit.author)} - ${h.age_component(c.commit.date)}
12 </span>
13 </div>
11 </div>
14
12
General Comments 0
You need to be logged in to leave comments. Login now