##// END OF EJS Templates
search: add support for elastic search 6...
dan -
r3319:b8fd1d7a default
parent child Browse files
Show More
@@ -0,0 +1,257 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 import re
21
22 import pygments.filter
23 import pygments.filters
24 from pygments.token import Comment
25
26 HL_BEG_MARKER = '__RCSearchHLMarkBEG__'
27 HL_END_MARKER = '__RCSearchHLMarkEND__'
28 HL_MARKER_RE = '{}(.*?){}'.format(HL_BEG_MARKER, HL_END_MARKER)
29
30
31 class ElasticSearchHLFilter(pygments.filters.Filter):
32 _names = [HL_BEG_MARKER, HL_END_MARKER]
33
34 def __init__(self, **options):
35 pygments.filters.Filter.__init__(self, **options)
36
37 def filter(self, lexer, stream):
38 def tokenize(_value):
39 for token in re.split('({}|{})'.format(
40 self._names[0], self._names[1]), _value):
41 if token:
42 yield token
43
44 hl = False
45 for ttype, value in stream:
46
47 if self._names[0] in value or self._names[1] in value:
48 for item in tokenize(value):
49 if item == self._names[0]:
50 # skip marker, but start HL
51 hl = True
52 continue
53 elif item == self._names[1]:
54 hl = False
55 continue
56
57 if hl:
58 yield Comment.ElasticMatch, item
59 else:
60 yield ttype, item
61 else:
62 if hl:
63 yield Comment.ElasticMatch, value
64 else:
65 yield ttype, value
66
67
68 def extract_phrases(text_query):
69 """
70 Extracts phrases from search term string making sure phrases
71 contained in double quotes are kept together - and discarding empty values
72 or fully whitespace values eg.
73
74 'some text "a phrase" more' => ['some', 'text', 'a phrase', 'more']
75
76 """
77
78 in_phrase = False
79 buf = ''
80 phrases = []
81 for char in text_query:
82 if in_phrase:
83 if char == '"': # end phrase
84 phrases.append(buf)
85 buf = ''
86 in_phrase = False
87 continue
88 else:
89 buf += char
90 continue
91 else:
92 if char == '"': # start phrase
93 in_phrase = True
94 phrases.append(buf)
95 buf = ''
96 continue
97 elif char == ' ':
98 phrases.append(buf)
99 buf = ''
100 continue
101 else:
102 buf += char
103
104 phrases.append(buf)
105 phrases = [phrase.strip() for phrase in phrases if phrase.strip()]
106 return phrases
107
108
109 def get_matching_phrase_offsets(text, phrases):
110 """
111 Returns a list of string offsets in `text` that the list of `terms` match
112
113 >>> get_matching_phrase_offsets('some text here', ['some', 'here'])
114 [(0, 4), (10, 14)]
115
116 """
117 phrases = phrases or []
118 offsets = []
119
120 for phrase in phrases:
121 for match in re.finditer(phrase, text):
122 offsets.append((match.start(), match.end()))
123
124 return offsets
125
126
127 def get_matching_markers_offsets(text, markers=None):
128 """
129 Returns a list of string offsets in `text` that the are between matching markers
130
131 >>> get_matching_markers_offsets('$1some$2 text $1here$2 marked', ['\$1(.*?)\$2'])
132 [(0, 5), (16, 22)]
133
134 """
135 markers = markers or [HL_MARKER_RE]
136 offsets = []
137
138 if markers:
139 for mark in markers:
140 for match in re.finditer(mark, text):
141 offsets.append((match.start(), match.end()))
142
143 return offsets
144
145
146 def normalize_text_for_matching(x):
147 """
148 Replaces all non alfanum characters to spaces and lower cases the string,
149 useful for comparing two text strings without punctuation
150 """
151 return re.sub(r'[^\w]', ' ', x.lower())
152
153
154 def get_matching_line_offsets(lines, terms=None, markers=None):
155 """ Return a set of `lines` indices (starting from 1) matching a
156 text search query, along with `context` lines above/below matching lines
157
158 :param lines: list of strings representing lines
159 :param terms: search term string to match in lines eg. 'some text'
160 :param markers: instead of terms, use highlight markers instead that
161 mark beginning and end for matched item. eg. ['START(.*?)END']
162
163 eg.
164
165 text = '''
166 words words words
167 words words words
168 some text some
169 words words words
170 words words words
171 text here what
172 '''
173 get_matching_line_offsets(text, 'text', context=1)
174 6, {3: [(5, 9)], 6: [(0, 4)]]
175
176 """
177 matching_lines = {}
178 line_index = 0
179
180 if terms:
181 phrases = [normalize_text_for_matching(phrase)
182 for phrase in extract_phrases(terms)]
183
184 for line_index, line in enumerate(lines.splitlines(), start=1):
185 normalized_line = normalize_text_for_matching(line)
186 match_offsets = get_matching_phrase_offsets(normalized_line, phrases)
187 if match_offsets:
188 matching_lines[line_index] = match_offsets
189
190 else:
191 markers = markers or [HL_MARKER_RE]
192 for line_index, line in enumerate(lines.splitlines(), start=1):
193 match_offsets = get_matching_markers_offsets(line, markers=markers)
194 if match_offsets:
195 matching_lines[line_index] = match_offsets
196
197 return line_index, matching_lines
198
199
200 def lucene_query_parser():
201 # from pyparsing lucene_grammar
202 from pyparsing import (
203 Literal, CaselessKeyword, Forward, Regex, QuotedString, Suppress,
204 Optional, Group, infixNotation, opAssoc, ParserElement, pyparsing_common)
205
206 ParserElement.enablePackrat()
207
208 COLON, LBRACK, RBRACK, LBRACE, RBRACE, TILDE, CARAT = map(Literal, ":[]{}~^")
209 LPAR, RPAR = map(Suppress, "()")
210 and_, or_, not_, to_ = map(CaselessKeyword, "AND OR NOT TO".split())
211 keyword = and_ | or_ | not_ | to_
212
213 expression = Forward()
214
215 valid_word = Regex(r'([a-zA-Z0-9*_+.-]|\\[!(){}\[\]^"~*?\\:])+').setName("word")
216 valid_word.setParseAction(
217 lambda t: t[0]
218 .replace('\\\\', chr(127))
219 .replace('\\', '')
220 .replace(chr(127), '\\')
221 )
222
223 string = QuotedString('"')
224
225 required_modifier = Literal("+")("required")
226 prohibit_modifier = Literal("-")("prohibit")
227 integer = Regex(r"\d+").setParseAction(lambda t: int(t[0]))
228 proximity_modifier = Group(TILDE + integer("proximity"))
229 number = pyparsing_common.fnumber()
230 fuzzy_modifier = TILDE + Optional(number, default=0.5)("fuzzy")
231
232 term = Forward()
233 field_name = valid_word().setName("fieldname")
234 incl_range_search = Group(LBRACK + term("lower") + to_ + term("upper") + RBRACK)
235 excl_range_search = Group(LBRACE + term("lower") + to_ + term("upper") + RBRACE)
236 range_search = incl_range_search("incl_range") | excl_range_search("excl_range")
237 boost = (CARAT + number("boost"))
238
239 string_expr = Group(string + proximity_modifier) | string
240 word_expr = Group(valid_word + fuzzy_modifier) | valid_word
241 term << (Optional(field_name("field") + COLON) +
242 (word_expr | string_expr | range_search | Group(
243 LPAR + expression + RPAR)) +
244 Optional(boost))
245 term.setParseAction(lambda t: [t] if 'field' in t or 'boost' in t else None)
246
247 expression << infixNotation(
248 term,
249 [
250 (required_modifier | prohibit_modifier, 1, opAssoc.RIGHT),
251 ((not_ | '!').setParseAction(lambda: "NOT"), 1, opAssoc.RIGHT),
252 ((and_ | '&&').setParseAction(lambda: "AND"), 2, opAssoc.LEFT),
253 (Optional(or_ | '||').setParseAction(lambda: "OR"), 2, opAssoc.LEFT),
254 ]
255 )
256
257 return expression
@@ -0,0 +1,100 b''
1 # -*- coding: utf-8 -*-
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
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
7 # (only), as published by the Free Software Foundation.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
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/>.
16 #
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
21 import copy
22 import mock
23 import pytest
24
25 from rhodecode.lib.index import search_utils
26
27
28 @pytest.mark.parametrize('test_text, expected_output', [
29 ('some text', ['some', 'text']),
30 ('some text', ['some', 'text']),
31 ('some text "with a phrase"', ['some', 'text', 'with a phrase']),
32 ('"a phrase" "another phrase"', ['a phrase', 'another phrase']),
33 ('"justphrase"', ['justphrase']),
34 ('""', []),
35 ('', []),
36 (' ', []),
37 ('" "', []),
38 ])
39 def test_extract_phrases(test_text, expected_output):
40 assert search_utils.extract_phrases(test_text) == expected_output
41
42
43 @pytest.mark.parametrize('test_text, text_phrases, expected_output', [
44 ('some text here', ['some', 'here'], [(0, 4), (10, 14)]),
45 ('here here there', ['here'], [(0, 4), (5, 9), (11, 15)]),
46 ('irrelevant', ['not found'], []),
47 ('irrelevant', ['not found'], []),
48 ])
49 def test_get_matching_phrase_offsets(test_text, text_phrases, expected_output):
50 assert search_utils.get_matching_phrase_offsets(
51 test_text, text_phrases) == expected_output
52
53
54 @pytest.mark.parametrize('test_text, text_phrases, expected_output', [
55 ('__RCSearchHLMarkBEG__some__RCSearchHLMarkEND__ text __RCSearchHLMarkBEG__here__RCSearchHLMarkEND__', [], [(0, 46), (52, 98)]),
56 ('__RCSearchHLMarkBEG__here__RCSearchHLMarkEND__ __RCSearchHLMarkBEG__here__RCSearchHLMarkEND__ there', [], [(0, 46), (47, 93)]),
57 ('some text __RCSearchHLMarkBEG__here__RCSearchHLMarkEND__', [], [(10, 56)]),
58 ('__RCSearchHLMarkBEG__here__RCSearchHLMarkEND__ __RCSearchHLMarkBEG__here__RCSearchHLMarkEND__ __RCSearchHLMarkBEG__there__RCSearchHLMarkEND__', [], [(0, 46), (47, 93), (94, 141)]),
59 ('irrelevant', ['not found'], []),
60 ('irrelevant', ['not found'], []),
61 ])
62 def test_get_matching_marker_offsets(test_text, text_phrases, expected_output):
63
64 assert search_utils.get_matching_markers_offsets(test_text) == expected_output
65
66
67 def test_normalize_text_for_matching():
68 assert search_utils.normalize_text_for_matching(
69 'OJjfe)*#$*@)$JF*)3r2f80h') == 'ojjfe jf 3r2f80h'
70
71
72 def test_get_matching_line_offsets():
73 words = '\n'.join([
74 'words words words',
75 'words words words',
76 'some text some',
77 'words words words',
78 'words words words',
79 'text here what'
80 ])
81 total_lines, matched_offsets = \
82 search_utils.get_matching_line_offsets(words, terms='text')
83 assert total_lines == 6
84 assert matched_offsets == {3: [(5, 9)], 6: [(0, 4)]}
85
86
87 def test_get_matching_line_offsets_using_markers():
88 words = '\n'.join([
89 'words words words',
90 'words words words',
91 'some __1__text__2__ some',
92 'words words words',
93 'words words words',
94 '__1__text__2__ here what'
95 ])
96 total_lines, matched_offsets = \
97 search_utils.get_matching_line_offsets(words, terms=None,
98 markers=['__1__(.*?)__2__'])
99 assert total_lines == 6
100 assert matched_offsets == {3: [(5, 19)], 6: [(0, 14)]}
@@ -1,2289 +1,2336 b''
1 # Generated by pip2nix 0.8.0.dev1
1 # Generated by pip2nix 0.8.0.dev1
2 # See https://github.com/johbo/pip2nix
2 # See https://github.com/johbo/pip2nix
3
3
4 { pkgs, fetchurl, fetchgit, fetchhg }:
4 { pkgs, fetchurl, fetchgit, fetchhg }:
5
5
6 self: super: {
6 self: super: {
7 "alembic" = super.buildPythonPackage {
7 "alembic" = super.buildPythonPackage {
8 name = "alembic-1.0.5";
8 name = "alembic-1.0.5";
9 doCheck = false;
9 doCheck = false;
10 propagatedBuildInputs = [
10 propagatedBuildInputs = [
11 self."sqlalchemy"
11 self."sqlalchemy"
12 self."mako"
12 self."mako"
13 self."python-editor"
13 self."python-editor"
14 self."python-dateutil"
14 self."python-dateutil"
15 ];
15 ];
16 src = fetchurl {
16 src = fetchurl {
17 url = "https://files.pythonhosted.org/packages/1c/65/b8e4f5b2f345bb13b5e0a3fddd892b0b3f0e8ad4880e954fdc6a50d00d84/alembic-1.0.5.tar.gz";
17 url = "https://files.pythonhosted.org/packages/1c/65/b8e4f5b2f345bb13b5e0a3fddd892b0b3f0e8ad4880e954fdc6a50d00d84/alembic-1.0.5.tar.gz";
18 sha256 = "0rpjqp2iq6p49x1nli18ivak1izz547nnjxi110mzrgc1v7dxzz9";
18 sha256 = "0rpjqp2iq6p49x1nli18ivak1izz547nnjxi110mzrgc1v7dxzz9";
19 };
19 };
20 meta = {
20 meta = {
21 license = [ pkgs.lib.licenses.mit ];
21 license = [ pkgs.lib.licenses.mit ];
22 };
22 };
23 };
23 };
24 "amqp" = super.buildPythonPackage {
24 "amqp" = super.buildPythonPackage {
25 name = "amqp-2.3.1";
25 name = "amqp-2.3.1";
26 doCheck = false;
26 doCheck = false;
27 propagatedBuildInputs = [
27 propagatedBuildInputs = [
28 self."vine"
28 self."vine"
29 ];
29 ];
30 src = fetchurl {
30 src = fetchurl {
31 url = "https://files.pythonhosted.org/packages/1b/32/242ff76cd802766f11c89c72f3389b5c8de4bdfbab406137b90c5fae8b05/amqp-2.3.1.tar.gz";
31 url = "https://files.pythonhosted.org/packages/1b/32/242ff76cd802766f11c89c72f3389b5c8de4bdfbab406137b90c5fae8b05/amqp-2.3.1.tar.gz";
32 sha256 = "0wlfnvhmfrn7c8qif2jyvsm63ibdxp02ss564qwrvqfhz0di72s0";
32 sha256 = "0wlfnvhmfrn7c8qif2jyvsm63ibdxp02ss564qwrvqfhz0di72s0";
33 };
33 };
34 meta = {
34 meta = {
35 license = [ pkgs.lib.licenses.bsdOriginal ];
35 license = [ pkgs.lib.licenses.bsdOriginal ];
36 };
36 };
37 };
37 };
38 "appenlight-client" = super.buildPythonPackage {
38 "appenlight-client" = super.buildPythonPackage {
39 name = "appenlight-client-0.6.26";
39 name = "appenlight-client-0.6.26";
40 doCheck = false;
40 doCheck = false;
41 propagatedBuildInputs = [
41 propagatedBuildInputs = [
42 self."webob"
42 self."webob"
43 self."requests"
43 self."requests"
44 self."six"
44 self."six"
45 ];
45 ];
46 src = fetchurl {
46 src = fetchurl {
47 url = "https://files.pythonhosted.org/packages/2e/56/418fc10379b96e795ee39a15e69a730c222818af04c3821fa354eaa859ec/appenlight_client-0.6.26.tar.gz";
47 url = "https://files.pythonhosted.org/packages/2e/56/418fc10379b96e795ee39a15e69a730c222818af04c3821fa354eaa859ec/appenlight_client-0.6.26.tar.gz";
48 sha256 = "0s9xw3sb8s3pk73k78nnq4jil3q4mk6bczfa1fmgfx61kdxl2712";
48 sha256 = "0s9xw3sb8s3pk73k78nnq4jil3q4mk6bczfa1fmgfx61kdxl2712";
49 };
49 };
50 meta = {
50 meta = {
51 license = [ pkgs.lib.licenses.bsdOriginal ];
51 license = [ pkgs.lib.licenses.bsdOriginal ];
52 };
52 };
53 };
53 };
54 "atomicwrites" = super.buildPythonPackage {
54 "atomicwrites" = super.buildPythonPackage {
55 name = "atomicwrites-1.2.1";
55 name = "atomicwrites-1.2.1";
56 doCheck = false;
56 doCheck = false;
57 src = fetchurl {
57 src = fetchurl {
58 url = "https://files.pythonhosted.org/packages/ac/ed/a311712ef6b4355035489f665e63e1a73f9eb371929e3c98e5efd451069e/atomicwrites-1.2.1.tar.gz";
58 url = "https://files.pythonhosted.org/packages/ac/ed/a311712ef6b4355035489f665e63e1a73f9eb371929e3c98e5efd451069e/atomicwrites-1.2.1.tar.gz";
59 sha256 = "1vmkbw9j0qammwxbxycrs39gvdg4lc2d4lk98kwf8ag2manyi6pc";
59 sha256 = "1vmkbw9j0qammwxbxycrs39gvdg4lc2d4lk98kwf8ag2manyi6pc";
60 };
60 };
61 meta = {
61 meta = {
62 license = [ pkgs.lib.licenses.mit ];
62 license = [ pkgs.lib.licenses.mit ];
63 };
63 };
64 };
64 };
65 "attrs" = super.buildPythonPackage {
65 "attrs" = super.buildPythonPackage {
66 name = "attrs-18.2.0";
66 name = "attrs-18.2.0";
67 doCheck = false;
67 doCheck = false;
68 src = fetchurl {
68 src = fetchurl {
69 url = "https://files.pythonhosted.org/packages/0f/9e/26b1d194aab960063b266170e53c39f73ea0d0d3f5ce23313e0ec8ee9bdf/attrs-18.2.0.tar.gz";
69 url = "https://files.pythonhosted.org/packages/0f/9e/26b1d194aab960063b266170e53c39f73ea0d0d3f5ce23313e0ec8ee9bdf/attrs-18.2.0.tar.gz";
70 sha256 = "0s9ydh058wmmf5v391pym877x4ahxg45dw6a0w4c7s5wgpigdjqh";
70 sha256 = "0s9ydh058wmmf5v391pym877x4ahxg45dw6a0w4c7s5wgpigdjqh";
71 };
71 };
72 meta = {
72 meta = {
73 license = [ pkgs.lib.licenses.mit ];
73 license = [ pkgs.lib.licenses.mit ];
74 };
74 };
75 };
75 };
76 "authomatic" = super.buildPythonPackage {
76 "authomatic" = super.buildPythonPackage {
77 name = "authomatic-0.1.0.post1";
77 name = "authomatic-0.1.0.post1";
78 doCheck = false;
78 doCheck = false;
79 src = fetchurl {
79 src = fetchurl {
80 url = "https://code.rhodecode.com/upstream/authomatic/archive/90a9ce60cc405ae8a2bf5c3713acd5d78579a04e.tar.gz?md5=3c68720a1322b25254009518d1ff6801";
80 url = "https://code.rhodecode.com/upstream/authomatic/archive/90a9ce60cc405ae8a2bf5c3713acd5d78579a04e.tar.gz?md5=3c68720a1322b25254009518d1ff6801";
81 sha256 = "1cgk0a86sbsjbri06gf5z5l4npwkjdxw6fdnwl4vvfmxs2sx9yxw";
81 sha256 = "1cgk0a86sbsjbri06gf5z5l4npwkjdxw6fdnwl4vvfmxs2sx9yxw";
82 };
82 };
83 meta = {
83 meta = {
84 license = [ pkgs.lib.licenses.mit ];
84 license = [ pkgs.lib.licenses.mit ];
85 };
85 };
86 };
86 };
87 "babel" = super.buildPythonPackage {
87 "babel" = super.buildPythonPackage {
88 name = "babel-1.3";
88 name = "babel-1.3";
89 doCheck = false;
89 doCheck = false;
90 propagatedBuildInputs = [
90 propagatedBuildInputs = [
91 self."pytz"
91 self."pytz"
92 ];
92 ];
93 src = fetchurl {
93 src = fetchurl {
94 url = "https://files.pythonhosted.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
94 url = "https://files.pythonhosted.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
95 sha256 = "0bnin777lc53nxd1hp3apq410jj5wx92n08h7h4izpl4f4sx00lz";
95 sha256 = "0bnin777lc53nxd1hp3apq410jj5wx92n08h7h4izpl4f4sx00lz";
96 };
96 };
97 meta = {
97 meta = {
98 license = [ pkgs.lib.licenses.bsdOriginal ];
98 license = [ pkgs.lib.licenses.bsdOriginal ];
99 };
99 };
100 };
100 };
101 "backports.shutil-get-terminal-size" = super.buildPythonPackage {
101 "backports.shutil-get-terminal-size" = super.buildPythonPackage {
102 name = "backports.shutil-get-terminal-size-1.0.0";
102 name = "backports.shutil-get-terminal-size-1.0.0";
103 doCheck = false;
103 doCheck = false;
104 src = fetchurl {
104 src = fetchurl {
105 url = "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
105 url = "https://files.pythonhosted.org/packages/ec/9c/368086faa9c016efce5da3e0e13ba392c9db79e3ab740b763fe28620b18b/backports.shutil_get_terminal_size-1.0.0.tar.gz";
106 sha256 = "107cmn7g3jnbkp826zlj8rrj19fam301qvaqf0f3905f5217lgki";
106 sha256 = "107cmn7g3jnbkp826zlj8rrj19fam301qvaqf0f3905f5217lgki";
107 };
107 };
108 meta = {
108 meta = {
109 license = [ pkgs.lib.licenses.mit ];
109 license = [ pkgs.lib.licenses.mit ];
110 };
110 };
111 };
111 };
112 "beaker" = super.buildPythonPackage {
112 "beaker" = super.buildPythonPackage {
113 name = "beaker-1.9.1";
113 name = "beaker-1.9.1";
114 doCheck = false;
114 doCheck = false;
115 propagatedBuildInputs = [
115 propagatedBuildInputs = [
116 self."funcsigs"
116 self."funcsigs"
117 ];
117 ];
118 src = fetchurl {
118 src = fetchurl {
119 url = "https://files.pythonhosted.org/packages/ca/14/a626188d0d0c7b55dd7cf1902046c2743bd392a7078bb53073e13280eb1e/Beaker-1.9.1.tar.gz";
119 url = "https://files.pythonhosted.org/packages/ca/14/a626188d0d0c7b55dd7cf1902046c2743bd392a7078bb53073e13280eb1e/Beaker-1.9.1.tar.gz";
120 sha256 = "08arsn61r255lhz6hcpn2lsiqpg30clla805ysx06wmbhvb6w9rj";
120 sha256 = "08arsn61r255lhz6hcpn2lsiqpg30clla805ysx06wmbhvb6w9rj";
121 };
121 };
122 meta = {
122 meta = {
123 license = [ pkgs.lib.licenses.bsdOriginal ];
123 license = [ pkgs.lib.licenses.bsdOriginal ];
124 };
124 };
125 };
125 };
126 "beautifulsoup4" = super.buildPythonPackage {
126 "beautifulsoup4" = super.buildPythonPackage {
127 name = "beautifulsoup4-4.6.3";
127 name = "beautifulsoup4-4.6.3";
128 doCheck = false;
128 doCheck = false;
129 src = fetchurl {
129 src = fetchurl {
130 url = "https://files.pythonhosted.org/packages/88/df/86bffad6309f74f3ff85ea69344a078fc30003270c8df6894fca7a3c72ff/beautifulsoup4-4.6.3.tar.gz";
130 url = "https://files.pythonhosted.org/packages/88/df/86bffad6309f74f3ff85ea69344a078fc30003270c8df6894fca7a3c72ff/beautifulsoup4-4.6.3.tar.gz";
131 sha256 = "041dhalzjciw6qyzzq7a2k4h1yvyk76xigp35hv5ibnn448ydy4h";
131 sha256 = "041dhalzjciw6qyzzq7a2k4h1yvyk76xigp35hv5ibnn448ydy4h";
132 };
132 };
133 meta = {
133 meta = {
134 license = [ pkgs.lib.licenses.mit ];
134 license = [ pkgs.lib.licenses.mit ];
135 };
135 };
136 };
136 };
137 "billiard" = super.buildPythonPackage {
137 "billiard" = super.buildPythonPackage {
138 name = "billiard-3.5.0.3";
138 name = "billiard-3.5.0.3";
139 doCheck = false;
139 doCheck = false;
140 src = fetchurl {
140 src = fetchurl {
141 url = "https://files.pythonhosted.org/packages/39/ac/f5571210cca2e4f4532e38aaff242f26c8654c5e2436bee966c230647ccc/billiard-3.5.0.3.tar.gz";
141 url = "https://files.pythonhosted.org/packages/39/ac/f5571210cca2e4f4532e38aaff242f26c8654c5e2436bee966c230647ccc/billiard-3.5.0.3.tar.gz";
142 sha256 = "1riwiiwgb141151md4ykx49qrz749akj5k8g290ji9bsqjyj4yqx";
142 sha256 = "1riwiiwgb141151md4ykx49qrz749akj5k8g290ji9bsqjyj4yqx";
143 };
143 };
144 meta = {
144 meta = {
145 license = [ pkgs.lib.licenses.bsdOriginal ];
145 license = [ pkgs.lib.licenses.bsdOriginal ];
146 };
146 };
147 };
147 };
148 "bleach" = super.buildPythonPackage {
148 "bleach" = super.buildPythonPackage {
149 name = "bleach-3.0.2";
149 name = "bleach-3.0.2";
150 doCheck = false;
150 doCheck = false;
151 propagatedBuildInputs = [
151 propagatedBuildInputs = [
152 self."six"
152 self."six"
153 self."webencodings"
153 self."webencodings"
154 ];
154 ];
155 src = fetchurl {
155 src = fetchurl {
156 url = "https://files.pythonhosted.org/packages/ae/31/680afc7d44040004296a2d8f0584983c2f2386448cd9d0964197e6c1160e/bleach-3.0.2.tar.gz";
156 url = "https://files.pythonhosted.org/packages/ae/31/680afc7d44040004296a2d8f0584983c2f2386448cd9d0964197e6c1160e/bleach-3.0.2.tar.gz";
157 sha256 = "06474zg7f73hv8h1xw2wcsmvn2ygj73zxgxxqg8zcx8ap1srdls8";
157 sha256 = "06474zg7f73hv8h1xw2wcsmvn2ygj73zxgxxqg8zcx8ap1srdls8";
158 };
158 };
159 meta = {
159 meta = {
160 license = [ pkgs.lib.licenses.asl20 ];
160 license = [ pkgs.lib.licenses.asl20 ];
161 };
161 };
162 };
162 };
163 "bumpversion" = super.buildPythonPackage {
163 "bumpversion" = super.buildPythonPackage {
164 name = "bumpversion-0.5.3";
164 name = "bumpversion-0.5.3";
165 doCheck = false;
165 doCheck = false;
166 src = fetchurl {
166 src = fetchurl {
167 url = "https://files.pythonhosted.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
167 url = "https://files.pythonhosted.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
168 sha256 = "0zn7694yfipxg35ikkfh7kvgl2fissha3dnqad2c5bvsvmrwhi37";
168 sha256 = "0zn7694yfipxg35ikkfh7kvgl2fissha3dnqad2c5bvsvmrwhi37";
169 };
169 };
170 meta = {
170 meta = {
171 license = [ pkgs.lib.licenses.mit ];
171 license = [ pkgs.lib.licenses.mit ];
172 };
172 };
173 };
173 };
174 "celery" = super.buildPythonPackage {
174 "celery" = super.buildPythonPackage {
175 name = "celery-4.1.1";
175 name = "celery-4.1.1";
176 doCheck = false;
176 doCheck = false;
177 propagatedBuildInputs = [
177 propagatedBuildInputs = [
178 self."pytz"
178 self."pytz"
179 self."billiard"
179 self."billiard"
180 self."kombu"
180 self."kombu"
181 ];
181 ];
182 src = fetchurl {
182 src = fetchurl {
183 url = "https://files.pythonhosted.org/packages/e9/cf/a4c0597effca20c57eb586324e41d1180bc8f13a933da41e0646cff69f02/celery-4.1.1.tar.gz";
183 url = "https://files.pythonhosted.org/packages/e9/cf/a4c0597effca20c57eb586324e41d1180bc8f13a933da41e0646cff69f02/celery-4.1.1.tar.gz";
184 sha256 = "1xbir4vw42n2ir9lanhwl7w69zpmj7lbi66fxm2b7pyvkcss7wni";
184 sha256 = "1xbir4vw42n2ir9lanhwl7w69zpmj7lbi66fxm2b7pyvkcss7wni";
185 };
185 };
186 meta = {
186 meta = {
187 license = [ pkgs.lib.licenses.bsdOriginal ];
187 license = [ pkgs.lib.licenses.bsdOriginal ];
188 };
188 };
189 };
189 };
190 "chameleon" = super.buildPythonPackage {
190 "chameleon" = super.buildPythonPackage {
191 name = "chameleon-2.24";
191 name = "chameleon-2.24";
192 doCheck = false;
192 doCheck = false;
193 src = fetchurl {
193 src = fetchurl {
194 url = "https://files.pythonhosted.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
194 url = "https://files.pythonhosted.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
195 sha256 = "0ykqr7syxfa6h9adjfnsv1gdsca2xzm22vmic8859n0f0j09abj5";
195 sha256 = "0ykqr7syxfa6h9adjfnsv1gdsca2xzm22vmic8859n0f0j09abj5";
196 };
196 };
197 meta = {
197 meta = {
198 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
198 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
199 };
199 };
200 };
200 };
201 "channelstream" = super.buildPythonPackage {
201 "channelstream" = super.buildPythonPackage {
202 name = "channelstream-0.5.2";
202 name = "channelstream-0.5.2";
203 doCheck = false;
203 doCheck = false;
204 propagatedBuildInputs = [
204 propagatedBuildInputs = [
205 self."gevent"
205 self."gevent"
206 self."ws4py"
206 self."ws4py"
207 self."pyramid"
207 self."pyramid"
208 self."pyramid-jinja2"
208 self."pyramid-jinja2"
209 self."itsdangerous"
209 self."itsdangerous"
210 self."requests"
210 self."requests"
211 self."six"
211 self."six"
212 ];
212 ];
213 src = fetchurl {
213 src = fetchurl {
214 url = "https://files.pythonhosted.org/packages/2b/31/29a8e085cf5bf97fa88e7b947adabfc581a18a3463adf77fb6dada34a65f/channelstream-0.5.2.tar.gz";
214 url = "https://files.pythonhosted.org/packages/2b/31/29a8e085cf5bf97fa88e7b947adabfc581a18a3463adf77fb6dada34a65f/channelstream-0.5.2.tar.gz";
215 sha256 = "1qbm4xdl5hfkja683x546bncg3rqq8qv79w1m1a1wd48cqqzb6rm";
215 sha256 = "1qbm4xdl5hfkja683x546bncg3rqq8qv79w1m1a1wd48cqqzb6rm";
216 };
216 };
217 meta = {
217 meta = {
218 license = [ pkgs.lib.licenses.bsdOriginal ];
218 license = [ pkgs.lib.licenses.bsdOriginal ];
219 };
219 };
220 };
220 };
221 "click" = super.buildPythonPackage {
221 "click" = super.buildPythonPackage {
222 name = "click-6.6";
222 name = "click-6.6";
223 doCheck = false;
223 doCheck = false;
224 src = fetchurl {
224 src = fetchurl {
225 url = "https://files.pythonhosted.org/packages/7a/00/c14926d8232b36b08218067bcd5853caefb4737cda3f0a47437151344792/click-6.6.tar.gz";
225 url = "https://files.pythonhosted.org/packages/7a/00/c14926d8232b36b08218067bcd5853caefb4737cda3f0a47437151344792/click-6.6.tar.gz";
226 sha256 = "1sggipyz52crrybwbr9xvwxd4aqigvplf53k9w3ygxmzivd1jsnc";
226 sha256 = "1sggipyz52crrybwbr9xvwxd4aqigvplf53k9w3ygxmzivd1jsnc";
227 };
227 };
228 meta = {
228 meta = {
229 license = [ pkgs.lib.licenses.bsdOriginal ];
229 license = [ pkgs.lib.licenses.bsdOriginal ];
230 };
230 };
231 };
231 };
232 "colander" = super.buildPythonPackage {
232 "colander" = super.buildPythonPackage {
233 name = "colander-1.5.1";
233 name = "colander-1.5.1";
234 doCheck = false;
234 doCheck = false;
235 propagatedBuildInputs = [
235 propagatedBuildInputs = [
236 self."translationstring"
236 self."translationstring"
237 self."iso8601"
237 self."iso8601"
238 self."enum34"
238 self."enum34"
239 ];
239 ];
240 src = fetchurl {
240 src = fetchurl {
241 url = "https://files.pythonhosted.org/packages/ec/d1/fcca811a0a692c69d27e36b4d11a73acb98b4bab48323442642b6fd4386d/colander-1.5.1.tar.gz";
241 url = "https://files.pythonhosted.org/packages/ec/d1/fcca811a0a692c69d27e36b4d11a73acb98b4bab48323442642b6fd4386d/colander-1.5.1.tar.gz";
242 sha256 = "18ah4cwwxnpm6qxi6x9ipy51dal4spd343h44s5wd01cnhgrwsyq";
242 sha256 = "18ah4cwwxnpm6qxi6x9ipy51dal4spd343h44s5wd01cnhgrwsyq";
243 };
243 };
244 meta = {
244 meta = {
245 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
245 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
246 };
246 };
247 };
247 };
248 "configobj" = super.buildPythonPackage {
248 "configobj" = super.buildPythonPackage {
249 name = "configobj-5.0.6";
249 name = "configobj-5.0.6";
250 doCheck = false;
250 doCheck = false;
251 propagatedBuildInputs = [
251 propagatedBuildInputs = [
252 self."six"
252 self."six"
253 ];
253 ];
254 src = fetchurl {
254 src = fetchurl {
255 url = "https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c";
255 url = "https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c";
256 sha256 = "1hhcxirwvg58grlfr177b3awhbq8hlx1l3lh69ifl1ki7lfd1s1x";
256 sha256 = "1hhcxirwvg58grlfr177b3awhbq8hlx1l3lh69ifl1ki7lfd1s1x";
257 };
257 };
258 meta = {
258 meta = {
259 license = [ pkgs.lib.licenses.bsdOriginal ];
259 license = [ pkgs.lib.licenses.bsdOriginal ];
260 };
260 };
261 };
261 };
262 "configparser" = super.buildPythonPackage {
262 "configparser" = super.buildPythonPackage {
263 name = "configparser-3.5.0";
263 name = "configparser-3.5.0";
264 doCheck = false;
264 doCheck = false;
265 src = fetchurl {
265 src = fetchurl {
266 url = "https://files.pythonhosted.org/packages/7c/69/c2ce7e91c89dc073eb1aa74c0621c3eefbffe8216b3f9af9d3885265c01c/configparser-3.5.0.tar.gz";
266 url = "https://files.pythonhosted.org/packages/7c/69/c2ce7e91c89dc073eb1aa74c0621c3eefbffe8216b3f9af9d3885265c01c/configparser-3.5.0.tar.gz";
267 sha256 = "0fi7vf09vi1588jd8f16a021m5y6ih2hy7rpbjb408xw45qb822k";
267 sha256 = "0fi7vf09vi1588jd8f16a021m5y6ih2hy7rpbjb408xw45qb822k";
268 };
268 };
269 meta = {
269 meta = {
270 license = [ pkgs.lib.licenses.mit ];
270 license = [ pkgs.lib.licenses.mit ];
271 };
271 };
272 };
272 };
273 "cov-core" = super.buildPythonPackage {
273 "cov-core" = super.buildPythonPackage {
274 name = "cov-core-1.15.0";
274 name = "cov-core-1.15.0";
275 doCheck = false;
275 doCheck = false;
276 propagatedBuildInputs = [
276 propagatedBuildInputs = [
277 self."coverage"
277 self."coverage"
278 ];
278 ];
279 src = fetchurl {
279 src = fetchurl {
280 url = "https://files.pythonhosted.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
280 url = "https://files.pythonhosted.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
281 sha256 = "0k3np9ymh06yv1ib96sb6wfsxjkqhmik8qfsn119vnhga9ywc52a";
281 sha256 = "0k3np9ymh06yv1ib96sb6wfsxjkqhmik8qfsn119vnhga9ywc52a";
282 };
282 };
283 meta = {
283 meta = {
284 license = [ pkgs.lib.licenses.mit ];
284 license = [ pkgs.lib.licenses.mit ];
285 };
285 };
286 };
286 };
287 "coverage" = super.buildPythonPackage {
287 "coverage" = super.buildPythonPackage {
288 name = "coverage-4.5.1";
288 name = "coverage-4.5.1";
289 doCheck = false;
289 doCheck = false;
290 src = fetchurl {
290 src = fetchurl {
291 url = "https://files.pythonhosted.org/packages/35/fe/e7df7289d717426093c68d156e0fd9117c8f4872b6588e8a8928a0f68424/coverage-4.5.1.tar.gz";
291 url = "https://files.pythonhosted.org/packages/35/fe/e7df7289d717426093c68d156e0fd9117c8f4872b6588e8a8928a0f68424/coverage-4.5.1.tar.gz";
292 sha256 = "1wbrzpxka3xd4nmmkc6q0ir343d91kymwsm8pbmwa0d2a7q4ir2n";
292 sha256 = "1wbrzpxka3xd4nmmkc6q0ir343d91kymwsm8pbmwa0d2a7q4ir2n";
293 };
293 };
294 meta = {
294 meta = {
295 license = [ pkgs.lib.licenses.asl20 ];
295 license = [ pkgs.lib.licenses.asl20 ];
296 };
296 };
297 };
297 };
298 "cssselect" = super.buildPythonPackage {
298 "cssselect" = super.buildPythonPackage {
299 name = "cssselect-1.0.3";
299 name = "cssselect-1.0.3";
300 doCheck = false;
300 doCheck = false;
301 src = fetchurl {
301 src = fetchurl {
302 url = "https://files.pythonhosted.org/packages/52/ea/f31e1d2e9eb130fda2a631e22eac369dc644e8807345fbed5113f2d6f92b/cssselect-1.0.3.tar.gz";
302 url = "https://files.pythonhosted.org/packages/52/ea/f31e1d2e9eb130fda2a631e22eac369dc644e8807345fbed5113f2d6f92b/cssselect-1.0.3.tar.gz";
303 sha256 = "011jqa2jhmydhi0iz4v1w3cr540z5zas8g2bw8brdw4s4b2qnv86";
303 sha256 = "011jqa2jhmydhi0iz4v1w3cr540z5zas8g2bw8brdw4s4b2qnv86";
304 };
304 };
305 meta = {
305 meta = {
306 license = [ pkgs.lib.licenses.bsdOriginal ];
306 license = [ pkgs.lib.licenses.bsdOriginal ];
307 };
307 };
308 };
308 };
309 "decorator" = super.buildPythonPackage {
309 "decorator" = super.buildPythonPackage {
310 name = "decorator-4.1.2";
310 name = "decorator-4.1.2";
311 doCheck = false;
311 doCheck = false;
312 src = fetchurl {
312 src = fetchurl {
313 url = "https://files.pythonhosted.org/packages/bb/e0/f6e41e9091e130bf16d4437dabbac3993908e4d6485ecbc985ef1352db94/decorator-4.1.2.tar.gz";
313 url = "https://files.pythonhosted.org/packages/bb/e0/f6e41e9091e130bf16d4437dabbac3993908e4d6485ecbc985ef1352db94/decorator-4.1.2.tar.gz";
314 sha256 = "1d8npb11kxyi36mrvjdpcjij76l5zfyrz2f820brf0l0rcw4vdkw";
314 sha256 = "1d8npb11kxyi36mrvjdpcjij76l5zfyrz2f820brf0l0rcw4vdkw";
315 };
315 };
316 meta = {
316 meta = {
317 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
317 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "new BSD License"; } ];
318 };
318 };
319 };
319 };
320 "deform" = super.buildPythonPackage {
320 "deform" = super.buildPythonPackage {
321 name = "deform-2.0.7";
321 name = "deform-2.0.7";
322 doCheck = false;
322 doCheck = false;
323 propagatedBuildInputs = [
323 propagatedBuildInputs = [
324 self."chameleon"
324 self."chameleon"
325 self."colander"
325 self."colander"
326 self."iso8601"
326 self."iso8601"
327 self."peppercorn"
327 self."peppercorn"
328 self."translationstring"
328 self."translationstring"
329 self."zope.deprecation"
329 self."zope.deprecation"
330 ];
330 ];
331 src = fetchurl {
331 src = fetchurl {
332 url = "https://files.pythonhosted.org/packages/cf/a1/bc234527b8f181de9acd80e796483c00007658d1e32b7de78f1c2e004d9a/deform-2.0.7.tar.gz";
332 url = "https://files.pythonhosted.org/packages/cf/a1/bc234527b8f181de9acd80e796483c00007658d1e32b7de78f1c2e004d9a/deform-2.0.7.tar.gz";
333 sha256 = "0jnpi0zr2hjvbmiz6nm33yqv976dn9lf51vhlzqc0i75xcr9rwig";
333 sha256 = "0jnpi0zr2hjvbmiz6nm33yqv976dn9lf51vhlzqc0i75xcr9rwig";
334 };
334 };
335 meta = {
335 meta = {
336 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
336 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
337 };
337 };
338 };
338 };
339 "defusedxml" = super.buildPythonPackage {
339 "defusedxml" = super.buildPythonPackage {
340 name = "defusedxml-0.5.0";
340 name = "defusedxml-0.5.0";
341 doCheck = false;
341 doCheck = false;
342 src = fetchurl {
342 src = fetchurl {
343 url = "https://files.pythonhosted.org/packages/74/ba/4ba4e89e21b5a2e267d80736ea674609a0a33cc4435a6d748ef04f1f9374/defusedxml-0.5.0.tar.gz";
343 url = "https://files.pythonhosted.org/packages/74/ba/4ba4e89e21b5a2e267d80736ea674609a0a33cc4435a6d748ef04f1f9374/defusedxml-0.5.0.tar.gz";
344 sha256 = "1x54n0h8hl92vvwyymx883fbqpqjwn2mc8fb383bcg3z9zwz5mr4";
344 sha256 = "1x54n0h8hl92vvwyymx883fbqpqjwn2mc8fb383bcg3z9zwz5mr4";
345 };
345 };
346 meta = {
346 meta = {
347 license = [ pkgs.lib.licenses.psfl ];
347 license = [ pkgs.lib.licenses.psfl ];
348 };
348 };
349 };
349 };
350 "dm.xmlsec.binding" = super.buildPythonPackage {
350 "dm.xmlsec.binding" = super.buildPythonPackage {
351 name = "dm.xmlsec.binding-1.3.7";
351 name = "dm.xmlsec.binding-1.3.7";
352 doCheck = false;
352 doCheck = false;
353 propagatedBuildInputs = [
353 propagatedBuildInputs = [
354 self."setuptools"
354 self."setuptools"
355 self."lxml"
355 self."lxml"
356 ];
356 ];
357 src = fetchurl {
357 src = fetchurl {
358 url = "https://files.pythonhosted.org/packages/2c/9e/7651982d50252692991acdae614af821fd6c79bc8dcd598ad71d55be8fc7/dm.xmlsec.binding-1.3.7.tar.gz";
358 url = "https://files.pythonhosted.org/packages/2c/9e/7651982d50252692991acdae614af821fd6c79bc8dcd598ad71d55be8fc7/dm.xmlsec.binding-1.3.7.tar.gz";
359 sha256 = "03jjjscx1pz2nc0dwiw9nia02qbz1c6f0f9zkyr8fmvys2n5jkb3";
359 sha256 = "03jjjscx1pz2nc0dwiw9nia02qbz1c6f0f9zkyr8fmvys2n5jkb3";
360 };
360 };
361 meta = {
361 meta = {
362 license = [ pkgs.lib.licenses.bsdOriginal ];
362 license = [ pkgs.lib.licenses.bsdOriginal ];
363 };
363 };
364 };
364 };
365 "docutils" = super.buildPythonPackage {
365 "docutils" = super.buildPythonPackage {
366 name = "docutils-0.14";
366 name = "docutils-0.14";
367 doCheck = false;
367 doCheck = false;
368 src = fetchurl {
368 src = fetchurl {
369 url = "https://files.pythonhosted.org/packages/84/f4/5771e41fdf52aabebbadecc9381d11dea0fa34e4759b4071244fa094804c/docutils-0.14.tar.gz";
369 url = "https://files.pythonhosted.org/packages/84/f4/5771e41fdf52aabebbadecc9381d11dea0fa34e4759b4071244fa094804c/docutils-0.14.tar.gz";
370 sha256 = "0x22fs3pdmr42kvz6c654756wja305qv6cx1zbhwlagvxgr4xrji";
370 sha256 = "0x22fs3pdmr42kvz6c654756wja305qv6cx1zbhwlagvxgr4xrji";
371 };
371 };
372 meta = {
372 meta = {
373 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
373 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.publicDomain pkgs.lib.licenses.gpl1 { fullName = "public domain, Python, 2-Clause BSD, GPL 3 (see COPYING.txt)"; } pkgs.lib.licenses.psfl ];
374 };
374 };
375 };
375 };
376 "dogpile.cache" = super.buildPythonPackage {
376 "dogpile.cache" = super.buildPythonPackage {
377 name = "dogpile.cache-0.6.7";
377 name = "dogpile.cache-0.6.7";
378 doCheck = false;
378 doCheck = false;
379 src = fetchurl {
379 src = fetchurl {
380 url = "https://files.pythonhosted.org/packages/ee/bd/440da735a11c6087eed7cc8747fc4b995cbac2464168682f8ee1c8e43844/dogpile.cache-0.6.7.tar.gz";
380 url = "https://files.pythonhosted.org/packages/ee/bd/440da735a11c6087eed7cc8747fc4b995cbac2464168682f8ee1c8e43844/dogpile.cache-0.6.7.tar.gz";
381 sha256 = "1aw8rx8vhb75y7zc6gi67g21sw057jdx7i8m3jq7kf3nqavxx9zw";
381 sha256 = "1aw8rx8vhb75y7zc6gi67g21sw057jdx7i8m3jq7kf3nqavxx9zw";
382 };
382 };
383 meta = {
383 meta = {
384 license = [ pkgs.lib.licenses.bsdOriginal ];
384 license = [ pkgs.lib.licenses.bsdOriginal ];
385 };
385 };
386 };
386 };
387 "dogpile.core" = super.buildPythonPackage {
387 "dogpile.core" = super.buildPythonPackage {
388 name = "dogpile.core-0.4.1";
388 name = "dogpile.core-0.4.1";
389 doCheck = false;
389 doCheck = false;
390 src = fetchurl {
390 src = fetchurl {
391 url = "https://files.pythonhosted.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
391 url = "https://files.pythonhosted.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
392 sha256 = "0xpdvg4kr1isfkrh1rfsh7za4q5a5s6l2kf9wpvndbwf3aqjyrdy";
392 sha256 = "0xpdvg4kr1isfkrh1rfsh7za4q5a5s6l2kf9wpvndbwf3aqjyrdy";
393 };
393 };
394 meta = {
394 meta = {
395 license = [ pkgs.lib.licenses.bsdOriginal ];
395 license = [ pkgs.lib.licenses.bsdOriginal ];
396 };
396 };
397 };
397 };
398 "ecdsa" = super.buildPythonPackage {
398 "ecdsa" = super.buildPythonPackage {
399 name = "ecdsa-0.13";
399 name = "ecdsa-0.13";
400 doCheck = false;
400 doCheck = false;
401 src = fetchurl {
401 src = fetchurl {
402 url = "https://files.pythonhosted.org/packages/f9/e5/99ebb176e47f150ac115ffeda5fedb6a3dbb3c00c74a59fd84ddf12f5857/ecdsa-0.13.tar.gz";
402 url = "https://files.pythonhosted.org/packages/f9/e5/99ebb176e47f150ac115ffeda5fedb6a3dbb3c00c74a59fd84ddf12f5857/ecdsa-0.13.tar.gz";
403 sha256 = "1yj31j0asmrx4an9xvsaj2icdmzy6pw0glfpqrrkrphwdpi1xkv4";
403 sha256 = "1yj31j0asmrx4an9xvsaj2icdmzy6pw0glfpqrrkrphwdpi1xkv4";
404 };
404 };
405 meta = {
405 meta = {
406 license = [ pkgs.lib.licenses.mit ];
406 license = [ pkgs.lib.licenses.mit ];
407 };
407 };
408 };
408 };
409 "elasticsearch" = super.buildPythonPackage {
409 "elasticsearch" = super.buildPythonPackage {
410 name = "elasticsearch-2.3.0";
410 name = "elasticsearch-6.3.1";
411 doCheck = false;
411 doCheck = false;
412 propagatedBuildInputs = [
412 propagatedBuildInputs = [
413 self."urllib3"
413 self."urllib3"
414 ];
414 ];
415 src = fetchurl {
415 src = fetchurl {
416 url = "https://files.pythonhosted.org/packages/10/35/5fd52c5f0b0ee405ed4b5195e8bce44c5e041787680dc7b94b8071cac600/elasticsearch-2.3.0.tar.gz";
416 url = "https://files.pythonhosted.org/packages/9d/ce/c4664e8380e379a9402ecfbaf158e56396da90d520daba21cfa840e0eb71/elasticsearch-6.3.1.tar.gz";
417 sha256 = "10ad2dk73xsys9vajwsncibs69asa63w1hgwz6lz1prjpyi80c5y";
417 sha256 = "12y93v0yn7a4xmf969239g8gb3l4cdkclfpbk1qc8hx5qkymrnma";
418 };
418 };
419 meta = {
419 meta = {
420 license = [ pkgs.lib.licenses.asl20 ];
420 license = [ pkgs.lib.licenses.asl20 ];
421 };
421 };
422 };
422 };
423 "elasticsearch-dsl" = super.buildPythonPackage {
423 "elasticsearch-dsl" = super.buildPythonPackage {
424 name = "elasticsearch-dsl-2.2.0";
424 name = "elasticsearch-dsl-6.3.1";
425 doCheck = false;
425 doCheck = false;
426 propagatedBuildInputs = [
426 propagatedBuildInputs = [
427 self."six"
427 self."six"
428 self."python-dateutil"
428 self."python-dateutil"
429 self."elasticsearch"
429 self."elasticsearch"
430 self."ipaddress"
431 ];
432 src = fetchurl {
433 url = "https://files.pythonhosted.org/packages/4c/0d/1549f50c591db6bb4e66cbcc8d34a6e537c3d89aa426b167c244fd46420a/elasticsearch-dsl-6.3.1.tar.gz";
434 sha256 = "1gh8a0shqi105k325hgwb9avrpdjh0mc6mxwfg9ba7g6lssb702z";
435 };
436 meta = {
437 license = [ pkgs.lib.licenses.asl20 ];
438 };
439 };
440 "elasticsearch1" = super.buildPythonPackage {
441 name = "elasticsearch1-1.10.0";
442 doCheck = false;
443 propagatedBuildInputs = [
444 self."urllib3"
430 ];
445 ];
431 src = fetchurl {
446 src = fetchurl {
432 url = "https://files.pythonhosted.org/packages/66/2f/52a086968788e58461641570f45c3207a52d46ebbe9b77dc22b6a8ffda66/elasticsearch-dsl-2.2.0.tar.gz";
447 url = "https://files.pythonhosted.org/packages/a6/eb/73e75f9681fa71e3157b8ee878534235d57f24ee64f0e77f8d995fb57076/elasticsearch1-1.10.0.tar.gz";
433 sha256 = "1g4kxzxsdwlsl2a9kscmx11pafgimhj7y8wrfksv8pgvpkfb9fwr";
448 sha256 = "0g89444kd5zwql4vbvyrmi2m6l6dcj6ga98j4hqxyyyz6z20aki2";
449 };
450 meta = {
451 license = [ pkgs.lib.licenses.asl20 ];
452 };
453 };
454 "elasticsearch1-dsl" = super.buildPythonPackage {
455 name = "elasticsearch1-dsl-0.0.12";
456 doCheck = false;
457 propagatedBuildInputs = [
458 self."six"
459 self."python-dateutil"
460 self."elasticsearch1"
461 ];
462 src = fetchurl {
463 url = "https://files.pythonhosted.org/packages/eb/9d/785342775cb10eddc9b8d7457d618a423b4f0b89d8b2b2d1bc27190d71db/elasticsearch1-dsl-0.0.12.tar.gz";
464 sha256 = "0ig1ly39v93hba0z975wnhbmzwj28w6w1sqlr2g7cn5spp732bhk";
465 };
466 meta = {
467 license = [ pkgs.lib.licenses.asl20 ];
468 };
469 };
470 "elasticsearch2" = super.buildPythonPackage {
471 name = "elasticsearch2-2.5.0";
472 doCheck = false;
473 propagatedBuildInputs = [
474 self."urllib3"
475 ];
476 src = fetchurl {
477 url = "https://files.pythonhosted.org/packages/84/77/63cf63d4ba11d913b5278406f2a37b0712bec6fc85edfb6151a33eaeba25/elasticsearch2-2.5.0.tar.gz";
478 sha256 = "0ky0q16lbvz022yv6q3pix7aamf026p1y994537ccjf0p0dxnbxr";
434 };
479 };
435 meta = {
480 meta = {
436 license = [ pkgs.lib.licenses.asl20 ];
481 license = [ pkgs.lib.licenses.asl20 ];
437 };
482 };
438 };
483 };
439 "entrypoints" = super.buildPythonPackage {
484 "entrypoints" = super.buildPythonPackage {
440 name = "entrypoints-0.2.2";
485 name = "entrypoints-0.2.2";
441 doCheck = false;
486 doCheck = false;
442 propagatedBuildInputs = [
487 propagatedBuildInputs = [
443 self."configparser"
488 self."configparser"
444 ];
489 ];
445 src = fetchurl {
490 src = fetchurl {
446 url = "https://code.rhodecode.com/upstream/entrypoints/archive/96e6d645684e1af3d7df5b5272f3fe85a546b233.tar.gz?md5=7db37771aea9ac9fefe093e5d6987313";
491 url = "https://code.rhodecode.com/upstream/entrypoints/archive/96e6d645684e1af3d7df5b5272f3fe85a546b233.tar.gz?md5=7db37771aea9ac9fefe093e5d6987313";
447 sha256 = "0bihrdp8ahsys437kxdhk52gz6kib8rxjv71i93wkw7594fcaxll";
492 sha256 = "0bihrdp8ahsys437kxdhk52gz6kib8rxjv71i93wkw7594fcaxll";
448 };
493 };
449 meta = {
494 meta = {
450 license = [ pkgs.lib.licenses.mit ];
495 license = [ pkgs.lib.licenses.mit ];
451 };
496 };
452 };
497 };
453 "enum34" = super.buildPythonPackage {
498 "enum34" = super.buildPythonPackage {
454 name = "enum34-1.1.6";
499 name = "enum34-1.1.6";
455 doCheck = false;
500 doCheck = false;
456 src = fetchurl {
501 src = fetchurl {
457 url = "https://files.pythonhosted.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz";
502 url = "https://files.pythonhosted.org/packages/bf/3e/31d502c25302814a7c2f1d3959d2a3b3f78e509002ba91aea64993936876/enum34-1.1.6.tar.gz";
458 sha256 = "1cgm5ng2gcfrkrm3hc22brl6chdmv67b9zvva9sfs7gn7dwc9n4a";
503 sha256 = "1cgm5ng2gcfrkrm3hc22brl6chdmv67b9zvva9sfs7gn7dwc9n4a";
459 };
504 };
460 meta = {
505 meta = {
461 license = [ pkgs.lib.licenses.bsdOriginal ];
506 license = [ pkgs.lib.licenses.bsdOriginal ];
462 };
507 };
463 };
508 };
464 "formencode" = super.buildPythonPackage {
509 "formencode" = super.buildPythonPackage {
465 name = "formencode-1.2.4";
510 name = "formencode-1.2.4";
466 doCheck = false;
511 doCheck = false;
467 src = fetchurl {
512 src = fetchurl {
468 url = "https://files.pythonhosted.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
513 url = "https://files.pythonhosted.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
469 sha256 = "1fgy04sdy4yry5xcjls3x3xy30dqwj58ycnkndim819jx0788w42";
514 sha256 = "1fgy04sdy4yry5xcjls3x3xy30dqwj58ycnkndim819jx0788w42";
470 };
515 };
471 meta = {
516 meta = {
472 license = [ pkgs.lib.licenses.psfl ];
517 license = [ pkgs.lib.licenses.psfl ];
473 };
518 };
474 };
519 };
475 "funcsigs" = super.buildPythonPackage {
520 "funcsigs" = super.buildPythonPackage {
476 name = "funcsigs-1.0.2";
521 name = "funcsigs-1.0.2";
477 doCheck = false;
522 doCheck = false;
478 src = fetchurl {
523 src = fetchurl {
479 url = "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
524 url = "https://files.pythonhosted.org/packages/94/4a/db842e7a0545de1cdb0439bb80e6e42dfe82aaeaadd4072f2263a4fbed23/funcsigs-1.0.2.tar.gz";
480 sha256 = "0l4g5818ffyfmfs1a924811azhjj8ax9xd1cffr1mzd3ycn0zfx7";
525 sha256 = "0l4g5818ffyfmfs1a924811azhjj8ax9xd1cffr1mzd3ycn0zfx7";
481 };
526 };
482 meta = {
527 meta = {
483 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
528 license = [ { fullName = "ASL"; } pkgs.lib.licenses.asl20 ];
484 };
529 };
485 };
530 };
486 "functools32" = super.buildPythonPackage {
531 "functools32" = super.buildPythonPackage {
487 name = "functools32-3.2.3.post2";
532 name = "functools32-3.2.3.post2";
488 doCheck = false;
533 doCheck = false;
489 src = fetchurl {
534 src = fetchurl {
490 url = "https://files.pythonhosted.org/packages/c5/60/6ac26ad05857c601308d8fb9e87fa36d0ebf889423f47c3502ef034365db/functools32-3.2.3-2.tar.gz";
535 url = "https://files.pythonhosted.org/packages/c5/60/6ac26ad05857c601308d8fb9e87fa36d0ebf889423f47c3502ef034365db/functools32-3.2.3-2.tar.gz";
491 sha256 = "0v8ya0b58x47wp216n1zamimv4iw57cxz3xxhzix52jkw3xks9gn";
536 sha256 = "0v8ya0b58x47wp216n1zamimv4iw57cxz3xxhzix52jkw3xks9gn";
492 };
537 };
493 meta = {
538 meta = {
494 license = [ pkgs.lib.licenses.psfl ];
539 license = [ pkgs.lib.licenses.psfl ];
495 };
540 };
496 };
541 };
497 "future" = super.buildPythonPackage {
542 "future" = super.buildPythonPackage {
498 name = "future-0.14.3";
543 name = "future-0.14.3";
499 doCheck = false;
544 doCheck = false;
500 src = fetchurl {
545 src = fetchurl {
501 url = "https://files.pythonhosted.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
546 url = "https://files.pythonhosted.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
502 sha256 = "1savk7jx7hal032f522c5ajhh8fra6gmnadrj9adv5qxi18pv1b2";
547 sha256 = "1savk7jx7hal032f522c5ajhh8fra6gmnadrj9adv5qxi18pv1b2";
503 };
548 };
504 meta = {
549 meta = {
505 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
550 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
506 };
551 };
507 };
552 };
508 "futures" = super.buildPythonPackage {
553 "futures" = super.buildPythonPackage {
509 name = "futures-3.0.2";
554 name = "futures-3.0.2";
510 doCheck = false;
555 doCheck = false;
511 src = fetchurl {
556 src = fetchurl {
512 url = "https://files.pythonhosted.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
557 url = "https://files.pythonhosted.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
513 sha256 = "0mz2pbgxbc2nbib1szifi07whjbfs4r02pv2z390z7p410awjgyw";
558 sha256 = "0mz2pbgxbc2nbib1szifi07whjbfs4r02pv2z390z7p410awjgyw";
514 };
559 };
515 meta = {
560 meta = {
516 license = [ pkgs.lib.licenses.bsdOriginal ];
561 license = [ pkgs.lib.licenses.bsdOriginal ];
517 };
562 };
518 };
563 };
519 "gevent" = super.buildPythonPackage {
564 "gevent" = super.buildPythonPackage {
520 name = "gevent-1.3.7";
565 name = "gevent-1.3.7";
521 doCheck = false;
566 doCheck = false;
522 propagatedBuildInputs = [
567 propagatedBuildInputs = [
523 self."greenlet"
568 self."greenlet"
524 ];
569 ];
525 src = fetchurl {
570 src = fetchurl {
526 url = "https://files.pythonhosted.org/packages/10/c1/9499b146bfa43aa4f1e0ed1bab1bd3209a4861d25650c11725036c731cf5/gevent-1.3.7.tar.gz";
571 url = "https://files.pythonhosted.org/packages/10/c1/9499b146bfa43aa4f1e0ed1bab1bd3209a4861d25650c11725036c731cf5/gevent-1.3.7.tar.gz";
527 sha256 = "0b0fr04qdk1p4sniv87fh8z5psac60x01pv054kpgi94520g81iz";
572 sha256 = "0b0fr04qdk1p4sniv87fh8z5psac60x01pv054kpgi94520g81iz";
528 };
573 };
529 meta = {
574 meta = {
530 license = [ pkgs.lib.licenses.mit ];
575 license = [ pkgs.lib.licenses.mit ];
531 };
576 };
532 };
577 };
533 "gnureadline" = super.buildPythonPackage {
578 "gnureadline" = super.buildPythonPackage {
534 name = "gnureadline-6.3.8";
579 name = "gnureadline-6.3.8";
535 doCheck = false;
580 doCheck = false;
536 src = fetchurl {
581 src = fetchurl {
537 url = "https://files.pythonhosted.org/packages/50/64/86085c823cd78f9df9d8e33dce0baa71618016f8860460b82cf6610e1eb3/gnureadline-6.3.8.tar.gz";
582 url = "https://files.pythonhosted.org/packages/50/64/86085c823cd78f9df9d8e33dce0baa71618016f8860460b82cf6610e1eb3/gnureadline-6.3.8.tar.gz";
538 sha256 = "0ddhj98x2nv45iz4aadk4b9m0b1kpsn1xhcbypn5cd556knhiqjq";
583 sha256 = "0ddhj98x2nv45iz4aadk4b9m0b1kpsn1xhcbypn5cd556knhiqjq";
539 };
584 };
540 meta = {
585 meta = {
541 license = [ { fullName = "GNU General Public License v3 (GPLv3)"; } pkgs.lib.licenses.gpl1 ];
586 license = [ { fullName = "GNU General Public License v3 (GPLv3)"; } pkgs.lib.licenses.gpl1 ];
542 };
587 };
543 };
588 };
544 "gprof2dot" = super.buildPythonPackage {
589 "gprof2dot" = super.buildPythonPackage {
545 name = "gprof2dot-2017.9.19";
590 name = "gprof2dot-2017.9.19";
546 doCheck = false;
591 doCheck = false;
547 src = fetchurl {
592 src = fetchurl {
548 url = "https://files.pythonhosted.org/packages/9d/36/f977122502979f3dfb50704979c9ed70e6b620787942b089bf1af15f5aba/gprof2dot-2017.9.19.tar.gz";
593 url = "https://files.pythonhosted.org/packages/9d/36/f977122502979f3dfb50704979c9ed70e6b620787942b089bf1af15f5aba/gprof2dot-2017.9.19.tar.gz";
549 sha256 = "17ih23ld2nzgc3xwgbay911l6lh96jp1zshmskm17n1gg2i7mg6f";
594 sha256 = "17ih23ld2nzgc3xwgbay911l6lh96jp1zshmskm17n1gg2i7mg6f";
550 };
595 };
551 meta = {
596 meta = {
552 license = [ { fullName = "GNU Lesser General Public License v3 or later (LGPLv3+)"; } { fullName = "LGPL"; } ];
597 license = [ { fullName = "GNU Lesser General Public License v3 or later (LGPLv3+)"; } { fullName = "LGPL"; } ];
553 };
598 };
554 };
599 };
555 "greenlet" = super.buildPythonPackage {
600 "greenlet" = super.buildPythonPackage {
556 name = "greenlet-0.4.15";
601 name = "greenlet-0.4.15";
557 doCheck = false;
602 doCheck = false;
558 src = fetchurl {
603 src = fetchurl {
559 url = "https://files.pythonhosted.org/packages/f8/e8/b30ae23b45f69aa3f024b46064c0ac8e5fcb4f22ace0dca8d6f9c8bbe5e7/greenlet-0.4.15.tar.gz";
604 url = "https://files.pythonhosted.org/packages/f8/e8/b30ae23b45f69aa3f024b46064c0ac8e5fcb4f22ace0dca8d6f9c8bbe5e7/greenlet-0.4.15.tar.gz";
560 sha256 = "1g4g1wwc472ds89zmqlpyan3fbnzpa8qm48z3z1y6mlk44z485ll";
605 sha256 = "1g4g1wwc472ds89zmqlpyan3fbnzpa8qm48z3z1y6mlk44z485ll";
561 };
606 };
562 meta = {
607 meta = {
563 license = [ pkgs.lib.licenses.mit ];
608 license = [ pkgs.lib.licenses.mit ];
564 };
609 };
565 };
610 };
566 "gunicorn" = super.buildPythonPackage {
611 "gunicorn" = super.buildPythonPackage {
567 name = "gunicorn-19.9.0";
612 name = "gunicorn-19.9.0";
568 doCheck = false;
613 doCheck = false;
569 src = fetchurl {
614 src = fetchurl {
570 url = "https://files.pythonhosted.org/packages/47/52/68ba8e5e8ba251e54006a49441f7ccabca83b6bef5aedacb4890596c7911/gunicorn-19.9.0.tar.gz";
615 url = "https://files.pythonhosted.org/packages/47/52/68ba8e5e8ba251e54006a49441f7ccabca83b6bef5aedacb4890596c7911/gunicorn-19.9.0.tar.gz";
571 sha256 = "1wzlf4xmn6qjirh5w81l6i6kqjnab1n1qqkh7zsj1yb6gh4n49ps";
616 sha256 = "1wzlf4xmn6qjirh5w81l6i6kqjnab1n1qqkh7zsj1yb6gh4n49ps";
572 };
617 };
573 meta = {
618 meta = {
574 license = [ pkgs.lib.licenses.mit ];
619 license = [ pkgs.lib.licenses.mit ];
575 };
620 };
576 };
621 };
577 "hupper" = super.buildPythonPackage {
622 "hupper" = super.buildPythonPackage {
578 name = "hupper-1.4.2";
623 name = "hupper-1.4.2";
579 doCheck = false;
624 doCheck = false;
580 src = fetchurl {
625 src = fetchurl {
581 url = "https://files.pythonhosted.org/packages/f1/75/1915dc7650b4867fa3049256e24ca8eddb5989998fcec788cf52b9812dfc/hupper-1.4.2.tar.gz";
626 url = "https://files.pythonhosted.org/packages/f1/75/1915dc7650b4867fa3049256e24ca8eddb5989998fcec788cf52b9812dfc/hupper-1.4.2.tar.gz";
582 sha256 = "16vb9fkiaakdpcp6pn56h3w0dwvm67bxq2k2dv4i382qhqwphdzb";
627 sha256 = "16vb9fkiaakdpcp6pn56h3w0dwvm67bxq2k2dv4i382qhqwphdzb";
583 };
628 };
584 meta = {
629 meta = {
585 license = [ pkgs.lib.licenses.mit ];
630 license = [ pkgs.lib.licenses.mit ];
586 };
631 };
587 };
632 };
588 "infrae.cache" = super.buildPythonPackage {
633 "infrae.cache" = super.buildPythonPackage {
589 name = "infrae.cache-1.0.1";
634 name = "infrae.cache-1.0.1";
590 doCheck = false;
635 doCheck = false;
591 propagatedBuildInputs = [
636 propagatedBuildInputs = [
592 self."beaker"
637 self."beaker"
593 self."repoze.lru"
638 self."repoze.lru"
594 ];
639 ];
595 src = fetchurl {
640 src = fetchurl {
596 url = "https://files.pythonhosted.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
641 url = "https://files.pythonhosted.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
597 sha256 = "1dvqsjn8vw253wz9d1pz17j79mf4bs53dvp2qxck2qdp1am1njw4";
642 sha256 = "1dvqsjn8vw253wz9d1pz17j79mf4bs53dvp2qxck2qdp1am1njw4";
598 };
643 };
599 meta = {
644 meta = {
600 license = [ pkgs.lib.licenses.zpl21 ];
645 license = [ pkgs.lib.licenses.zpl21 ];
601 };
646 };
602 };
647 };
603 "invoke" = super.buildPythonPackage {
648 "invoke" = super.buildPythonPackage {
604 name = "invoke-0.13.0";
649 name = "invoke-0.13.0";
605 doCheck = false;
650 doCheck = false;
606 src = fetchurl {
651 src = fetchurl {
607 url = "https://files.pythonhosted.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
652 url = "https://files.pythonhosted.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
608 sha256 = "0794vhgxfmkh0vzkkg5cfv1w82g3jc3xr18wim29far9qpx9468s";
653 sha256 = "0794vhgxfmkh0vzkkg5cfv1w82g3jc3xr18wim29far9qpx9468s";
609 };
654 };
610 meta = {
655 meta = {
611 license = [ pkgs.lib.licenses.bsdOriginal ];
656 license = [ pkgs.lib.licenses.bsdOriginal ];
612 };
657 };
613 };
658 };
614 "ipaddress" = super.buildPythonPackage {
659 "ipaddress" = super.buildPythonPackage {
615 name = "ipaddress-1.0.22";
660 name = "ipaddress-1.0.22";
616 doCheck = false;
661 doCheck = false;
617 src = fetchurl {
662 src = fetchurl {
618 url = "https://files.pythonhosted.org/packages/97/8d/77b8cedcfbf93676148518036c6b1ce7f8e14bf07e95d7fd4ddcb8cc052f/ipaddress-1.0.22.tar.gz";
663 url = "https://files.pythonhosted.org/packages/97/8d/77b8cedcfbf93676148518036c6b1ce7f8e14bf07e95d7fd4ddcb8cc052f/ipaddress-1.0.22.tar.gz";
619 sha256 = "0b570bm6xqpjwqis15pvdy6lyvvzfndjvkynilcddjj5x98wfimi";
664 sha256 = "0b570bm6xqpjwqis15pvdy6lyvvzfndjvkynilcddjj5x98wfimi";
620 };
665 };
621 meta = {
666 meta = {
622 license = [ pkgs.lib.licenses.psfl ];
667 license = [ pkgs.lib.licenses.psfl ];
623 };
668 };
624 };
669 };
625 "ipdb" = super.buildPythonPackage {
670 "ipdb" = super.buildPythonPackage {
626 name = "ipdb-0.11";
671 name = "ipdb-0.11";
627 doCheck = false;
672 doCheck = false;
628 propagatedBuildInputs = [
673 propagatedBuildInputs = [
629 self."setuptools"
674 self."setuptools"
630 self."ipython"
675 self."ipython"
631 ];
676 ];
632 src = fetchurl {
677 src = fetchurl {
633 url = "https://files.pythonhosted.org/packages/80/fe/4564de08f174f3846364b3add8426d14cebee228f741c27e702b2877e85b/ipdb-0.11.tar.gz";
678 url = "https://files.pythonhosted.org/packages/80/fe/4564de08f174f3846364b3add8426d14cebee228f741c27e702b2877e85b/ipdb-0.11.tar.gz";
634 sha256 = "02m0l8wrhhd3z7dg3czn5ys1g5pxib516hpshdzp7rxzsxgcd0bh";
679 sha256 = "02m0l8wrhhd3z7dg3czn5ys1g5pxib516hpshdzp7rxzsxgcd0bh";
635 };
680 };
636 meta = {
681 meta = {
637 license = [ pkgs.lib.licenses.bsdOriginal ];
682 license = [ pkgs.lib.licenses.bsdOriginal ];
638 };
683 };
639 };
684 };
640 "ipython" = super.buildPythonPackage {
685 "ipython" = super.buildPythonPackage {
641 name = "ipython-5.1.0";
686 name = "ipython-5.1.0";
642 doCheck = false;
687 doCheck = false;
643 propagatedBuildInputs = [
688 propagatedBuildInputs = [
644 self."setuptools"
689 self."setuptools"
645 self."decorator"
690 self."decorator"
646 self."pickleshare"
691 self."pickleshare"
647 self."simplegeneric"
692 self."simplegeneric"
648 self."traitlets"
693 self."traitlets"
649 self."prompt-toolkit"
694 self."prompt-toolkit"
650 self."pygments"
695 self."pygments"
651 self."pexpect"
696 self."pexpect"
652 self."backports.shutil-get-terminal-size"
697 self."backports.shutil-get-terminal-size"
653 self."pathlib2"
698 self."pathlib2"
654 self."pexpect"
699 self."pexpect"
655 ];
700 ];
656 src = fetchurl {
701 src = fetchurl {
657 url = "https://files.pythonhosted.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
702 url = "https://files.pythonhosted.org/packages/89/63/a9292f7cd9d0090a0f995e1167f3f17d5889dcbc9a175261719c513b9848/ipython-5.1.0.tar.gz";
658 sha256 = "0qdrf6aj9kvjczd5chj1my8y2iq09am9l8bb2a1334a52d76kx3y";
703 sha256 = "0qdrf6aj9kvjczd5chj1my8y2iq09am9l8bb2a1334a52d76kx3y";
659 };
704 };
660 meta = {
705 meta = {
661 license = [ pkgs.lib.licenses.bsdOriginal ];
706 license = [ pkgs.lib.licenses.bsdOriginal ];
662 };
707 };
663 };
708 };
664 "ipython-genutils" = super.buildPythonPackage {
709 "ipython-genutils" = super.buildPythonPackage {
665 name = "ipython-genutils-0.2.0";
710 name = "ipython-genutils-0.2.0";
666 doCheck = false;
711 doCheck = false;
667 src = fetchurl {
712 src = fetchurl {
668 url = "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz";
713 url = "https://files.pythonhosted.org/packages/e8/69/fbeffffc05236398ebfcfb512b6d2511c622871dca1746361006da310399/ipython_genutils-0.2.0.tar.gz";
669 sha256 = "1a4bc9y8hnvq6cp08qs4mckgm6i6ajpndp4g496rvvzcfmp12bpb";
714 sha256 = "1a4bc9y8hnvq6cp08qs4mckgm6i6ajpndp4g496rvvzcfmp12bpb";
670 };
715 };
671 meta = {
716 meta = {
672 license = [ pkgs.lib.licenses.bsdOriginal ];
717 license = [ pkgs.lib.licenses.bsdOriginal ];
673 };
718 };
674 };
719 };
675 "iso8601" = super.buildPythonPackage {
720 "iso8601" = super.buildPythonPackage {
676 name = "iso8601-0.1.11";
721 name = "iso8601-0.1.11";
677 doCheck = false;
722 doCheck = false;
678 src = fetchurl {
723 src = fetchurl {
679 url = "https://files.pythonhosted.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
724 url = "https://files.pythonhosted.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
680 sha256 = "0c7gh3lsdjds262h0v1sqc66l7hqgfwbakn96qrhdbl0i3vm5yz8";
725 sha256 = "0c7gh3lsdjds262h0v1sqc66l7hqgfwbakn96qrhdbl0i3vm5yz8";
681 };
726 };
682 meta = {
727 meta = {
683 license = [ pkgs.lib.licenses.mit ];
728 license = [ pkgs.lib.licenses.mit ];
684 };
729 };
685 };
730 };
686 "isodate" = super.buildPythonPackage {
731 "isodate" = super.buildPythonPackage {
687 name = "isodate-0.6.0";
732 name = "isodate-0.6.0";
688 doCheck = false;
733 doCheck = false;
689 propagatedBuildInputs = [
734 propagatedBuildInputs = [
690 self."six"
735 self."six"
691 ];
736 ];
692 src = fetchurl {
737 src = fetchurl {
693 url = "https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz";
738 url = "https://files.pythonhosted.org/packages/b1/80/fb8c13a4cd38eb5021dc3741a9e588e4d1de88d895c1910c6fc8a08b7a70/isodate-0.6.0.tar.gz";
694 sha256 = "1n7jkz68kk5pwni540pr5zdh99bf6ywydk1p5pdrqisrawylldif";
739 sha256 = "1n7jkz68kk5pwni540pr5zdh99bf6ywydk1p5pdrqisrawylldif";
695 };
740 };
696 meta = {
741 meta = {
697 license = [ pkgs.lib.licenses.bsdOriginal ];
742 license = [ pkgs.lib.licenses.bsdOriginal ];
698 };
743 };
699 };
744 };
700 "itsdangerous" = super.buildPythonPackage {
745 "itsdangerous" = super.buildPythonPackage {
701 name = "itsdangerous-0.24";
746 name = "itsdangerous-0.24";
702 doCheck = false;
747 doCheck = false;
703 src = fetchurl {
748 src = fetchurl {
704 url = "https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
749 url = "https://files.pythonhosted.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
705 sha256 = "06856q6x675ly542ig0plbqcyab6ksfzijlyf1hzhgg3sgwgrcyb";
750 sha256 = "06856q6x675ly542ig0plbqcyab6ksfzijlyf1hzhgg3sgwgrcyb";
706 };
751 };
707 meta = {
752 meta = {
708 license = [ pkgs.lib.licenses.bsdOriginal ];
753 license = [ pkgs.lib.licenses.bsdOriginal ];
709 };
754 };
710 };
755 };
711 "jinja2" = super.buildPythonPackage {
756 "jinja2" = super.buildPythonPackage {
712 name = "jinja2-2.9.6";
757 name = "jinja2-2.9.6";
713 doCheck = false;
758 doCheck = false;
714 propagatedBuildInputs = [
759 propagatedBuildInputs = [
715 self."markupsafe"
760 self."markupsafe"
716 ];
761 ];
717 src = fetchurl {
762 src = fetchurl {
718 url = "https://files.pythonhosted.org/packages/90/61/f820ff0076a2599dd39406dcb858ecb239438c02ce706c8e91131ab9c7f1/Jinja2-2.9.6.tar.gz";
763 url = "https://files.pythonhosted.org/packages/90/61/f820ff0076a2599dd39406dcb858ecb239438c02ce706c8e91131ab9c7f1/Jinja2-2.9.6.tar.gz";
719 sha256 = "1zzrkywhziqffrzks14kzixz7nd4yh2vc0fb04a68vfd2ai03anx";
764 sha256 = "1zzrkywhziqffrzks14kzixz7nd4yh2vc0fb04a68vfd2ai03anx";
720 };
765 };
721 meta = {
766 meta = {
722 license = [ pkgs.lib.licenses.bsdOriginal ];
767 license = [ pkgs.lib.licenses.bsdOriginal ];
723 };
768 };
724 };
769 };
725 "jsonschema" = super.buildPythonPackage {
770 "jsonschema" = super.buildPythonPackage {
726 name = "jsonschema-2.6.0";
771 name = "jsonschema-2.6.0";
727 doCheck = false;
772 doCheck = false;
728 propagatedBuildInputs = [
773 propagatedBuildInputs = [
729 self."functools32"
774 self."functools32"
730 ];
775 ];
731 src = fetchurl {
776 src = fetchurl {
732 url = "https://files.pythonhosted.org/packages/58/b9/171dbb07e18c6346090a37f03c7e74410a1a56123f847efed59af260a298/jsonschema-2.6.0.tar.gz";
777 url = "https://files.pythonhosted.org/packages/58/b9/171dbb07e18c6346090a37f03c7e74410a1a56123f847efed59af260a298/jsonschema-2.6.0.tar.gz";
733 sha256 = "00kf3zmpp9ya4sydffpifn0j0mzm342a2vzh82p6r0vh10cg7xbg";
778 sha256 = "00kf3zmpp9ya4sydffpifn0j0mzm342a2vzh82p6r0vh10cg7xbg";
734 };
779 };
735 meta = {
780 meta = {
736 license = [ pkgs.lib.licenses.mit ];
781 license = [ pkgs.lib.licenses.mit ];
737 };
782 };
738 };
783 };
739 "jupyter-client" = super.buildPythonPackage {
784 "jupyter-client" = super.buildPythonPackage {
740 name = "jupyter-client-5.0.0";
785 name = "jupyter-client-5.0.0";
741 doCheck = false;
786 doCheck = false;
742 propagatedBuildInputs = [
787 propagatedBuildInputs = [
743 self."traitlets"
788 self."traitlets"
744 self."jupyter-core"
789 self."jupyter-core"
745 self."pyzmq"
790 self."pyzmq"
746 self."python-dateutil"
791 self."python-dateutil"
747 ];
792 ];
748 src = fetchurl {
793 src = fetchurl {
749 url = "https://files.pythonhosted.org/packages/e5/6f/65412ed462202b90134b7e761b0b7e7f949e07a549c1755475333727b3d0/jupyter_client-5.0.0.tar.gz";
794 url = "https://files.pythonhosted.org/packages/e5/6f/65412ed462202b90134b7e761b0b7e7f949e07a549c1755475333727b3d0/jupyter_client-5.0.0.tar.gz";
750 sha256 = "0nxw4rqk4wsjhc87gjqd7pv89cb9dnimcfnmcmp85bmrvv1gjri7";
795 sha256 = "0nxw4rqk4wsjhc87gjqd7pv89cb9dnimcfnmcmp85bmrvv1gjri7";
751 };
796 };
752 meta = {
797 meta = {
753 license = [ pkgs.lib.licenses.bsdOriginal ];
798 license = [ pkgs.lib.licenses.bsdOriginal ];
754 };
799 };
755 };
800 };
756 "jupyter-core" = super.buildPythonPackage {
801 "jupyter-core" = super.buildPythonPackage {
757 name = "jupyter-core-4.4.0";
802 name = "jupyter-core-4.4.0";
758 doCheck = false;
803 doCheck = false;
759 propagatedBuildInputs = [
804 propagatedBuildInputs = [
760 self."traitlets"
805 self."traitlets"
761 ];
806 ];
762 src = fetchurl {
807 src = fetchurl {
763 url = "https://files.pythonhosted.org/packages/b6/2d/2804f4de3a95583f65e5dcb4d7c8c7183124882323758996e867f47e72af/jupyter_core-4.4.0.tar.gz";
808 url = "https://files.pythonhosted.org/packages/b6/2d/2804f4de3a95583f65e5dcb4d7c8c7183124882323758996e867f47e72af/jupyter_core-4.4.0.tar.gz";
764 sha256 = "1dy083rarba8prn9f9srxq3c7n7vyql02ycrqq306c40lr57aw5s";
809 sha256 = "1dy083rarba8prn9f9srxq3c7n7vyql02ycrqq306c40lr57aw5s";
765 };
810 };
766 meta = {
811 meta = {
767 license = [ pkgs.lib.licenses.bsdOriginal ];
812 license = [ pkgs.lib.licenses.bsdOriginal ];
768 };
813 };
769 };
814 };
770 "kombu" = super.buildPythonPackage {
815 "kombu" = super.buildPythonPackage {
771 name = "kombu-4.2.0";
816 name = "kombu-4.2.0";
772 doCheck = false;
817 doCheck = false;
773 propagatedBuildInputs = [
818 propagatedBuildInputs = [
774 self."amqp"
819 self."amqp"
775 ];
820 ];
776 src = fetchurl {
821 src = fetchurl {
777 url = "https://files.pythonhosted.org/packages/ab/b1/46a7a8babf5e60f3b2ca081a100af8edfcf132078a726375f52a054e70cf/kombu-4.2.0.tar.gz";
822 url = "https://files.pythonhosted.org/packages/ab/b1/46a7a8babf5e60f3b2ca081a100af8edfcf132078a726375f52a054e70cf/kombu-4.2.0.tar.gz";
778 sha256 = "1yz19qlqf0inl1mnwlpq9j6kj9r67clpy0xg99phyg4329rw80fn";
823 sha256 = "1yz19qlqf0inl1mnwlpq9j6kj9r67clpy0xg99phyg4329rw80fn";
779 };
824 };
780 meta = {
825 meta = {
781 license = [ pkgs.lib.licenses.bsdOriginal ];
826 license = [ pkgs.lib.licenses.bsdOriginal ];
782 };
827 };
783 };
828 };
784 "lxml" = super.buildPythonPackage {
829 "lxml" = super.buildPythonPackage {
785 name = "lxml-4.2.5";
830 name = "lxml-4.2.5";
786 doCheck = false;
831 doCheck = false;
787 src = fetchurl {
832 src = fetchurl {
788 url = "https://files.pythonhosted.org/packages/4b/20/ddf5eb3bd5c57582d2b4652b4bbcf8da301bdfe5d805cb94e805f4d7464d/lxml-4.2.5.tar.gz";
833 url = "https://files.pythonhosted.org/packages/4b/20/ddf5eb3bd5c57582d2b4652b4bbcf8da301bdfe5d805cb94e805f4d7464d/lxml-4.2.5.tar.gz";
789 sha256 = "0zw0y9hs0nflxhl9cs6ipwwh53szi3w2x06wl0k9cylyqac0cwin";
834 sha256 = "0zw0y9hs0nflxhl9cs6ipwwh53szi3w2x06wl0k9cylyqac0cwin";
790 };
835 };
791 meta = {
836 meta = {
792 license = [ pkgs.lib.licenses.bsdOriginal ];
837 license = [ pkgs.lib.licenses.bsdOriginal ];
793 };
838 };
794 };
839 };
795 "mako" = super.buildPythonPackage {
840 "mako" = super.buildPythonPackage {
796 name = "mako-1.0.7";
841 name = "mako-1.0.7";
797 doCheck = false;
842 doCheck = false;
798 propagatedBuildInputs = [
843 propagatedBuildInputs = [
799 self."markupsafe"
844 self."markupsafe"
800 ];
845 ];
801 src = fetchurl {
846 src = fetchurl {
802 url = "https://files.pythonhosted.org/packages/eb/f3/67579bb486517c0d49547f9697e36582cd19dafb5df9e687ed8e22de57fa/Mako-1.0.7.tar.gz";
847 url = "https://files.pythonhosted.org/packages/eb/f3/67579bb486517c0d49547f9697e36582cd19dafb5df9e687ed8e22de57fa/Mako-1.0.7.tar.gz";
803 sha256 = "1bi5gnr8r8dva06qpyx4kgjc6spm2k1y908183nbbaylggjzs0jf";
848 sha256 = "1bi5gnr8r8dva06qpyx4kgjc6spm2k1y908183nbbaylggjzs0jf";
804 };
849 };
805 meta = {
850 meta = {
806 license = [ pkgs.lib.licenses.mit ];
851 license = [ pkgs.lib.licenses.mit ];
807 };
852 };
808 };
853 };
809 "markdown" = super.buildPythonPackage {
854 "markdown" = super.buildPythonPackage {
810 name = "markdown-2.6.11";
855 name = "markdown-2.6.11";
811 doCheck = false;
856 doCheck = false;
812 src = fetchurl {
857 src = fetchurl {
813 url = "https://files.pythonhosted.org/packages/b3/73/fc5c850f44af5889192dff783b7b0d8f3fe8d30b65c8e3f78f8f0265fecf/Markdown-2.6.11.tar.gz";
858 url = "https://files.pythonhosted.org/packages/b3/73/fc5c850f44af5889192dff783b7b0d8f3fe8d30b65c8e3f78f8f0265fecf/Markdown-2.6.11.tar.gz";
814 sha256 = "108g80ryzykh8bj0i7jfp71510wrcixdi771lf2asyghgyf8cmm8";
859 sha256 = "108g80ryzykh8bj0i7jfp71510wrcixdi771lf2asyghgyf8cmm8";
815 };
860 };
816 meta = {
861 meta = {
817 license = [ pkgs.lib.licenses.bsdOriginal ];
862 license = [ pkgs.lib.licenses.bsdOriginal ];
818 };
863 };
819 };
864 };
820 "markupsafe" = super.buildPythonPackage {
865 "markupsafe" = super.buildPythonPackage {
821 name = "markupsafe-1.0";
866 name = "markupsafe-1.1.0";
822 doCheck = false;
867 doCheck = false;
823 src = fetchurl {
868 src = fetchurl {
824 url = "https://files.pythonhosted.org/packages/4d/de/32d741db316d8fdb7680822dd37001ef7a448255de9699ab4bfcbdf4172b/MarkupSafe-1.0.tar.gz";
869 url = "https://files.pythonhosted.org/packages/ac/7e/1b4c2e05809a4414ebce0892fe1e32c14ace86ca7d50c70f00979ca9b3a3/MarkupSafe-1.1.0.tar.gz";
825 sha256 = "0rdn1s8x9ni7ss8rfiacj7x1085lx8mh2zdwqslnw8xc3l4nkgm6";
870 sha256 = "1lxirjypbdd3l9jl4vliilhfnhy7c7f2vlldqg1b0i74khn375sf";
826 };
871 };
827 meta = {
872 meta = {
828 license = [ pkgs.lib.licenses.bsdOriginal ];
873 license = [ pkgs.lib.licenses.bsdOriginal ];
829 };
874 };
830 };
875 };
831 "meld3" = super.buildPythonPackage {
876 "meld3" = super.buildPythonPackage {
832 name = "meld3-1.0.2";
877 name = "meld3-1.0.2";
833 doCheck = false;
878 doCheck = false;
834 src = fetchurl {
879 src = fetchurl {
835 url = "https://files.pythonhosted.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
880 url = "https://files.pythonhosted.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
836 sha256 = "0n4mkwlpsqnmn0dm0wm5hn9nkda0nafl0jdy5sdl5977znh59dzp";
881 sha256 = "0n4mkwlpsqnmn0dm0wm5hn9nkda0nafl0jdy5sdl5977znh59dzp";
837 };
882 };
838 meta = {
883 meta = {
839 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
884 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
840 };
885 };
841 };
886 };
842 "mistune" = super.buildPythonPackage {
887 "mistune" = super.buildPythonPackage {
843 name = "mistune-0.8.4";
888 name = "mistune-0.8.4";
844 doCheck = false;
889 doCheck = false;
845 src = fetchurl {
890 src = fetchurl {
846 url = "https://files.pythonhosted.org/packages/2d/a4/509f6e7783ddd35482feda27bc7f72e65b5e7dc910eca4ab2164daf9c577/mistune-0.8.4.tar.gz";
891 url = "https://files.pythonhosted.org/packages/2d/a4/509f6e7783ddd35482feda27bc7f72e65b5e7dc910eca4ab2164daf9c577/mistune-0.8.4.tar.gz";
847 sha256 = "0vkmsh0x480rni51lhyvigfdf06b9247z868pk3bal1wnnfl58sr";
892 sha256 = "0vkmsh0x480rni51lhyvigfdf06b9247z868pk3bal1wnnfl58sr";
848 };
893 };
849 meta = {
894 meta = {
850 license = [ pkgs.lib.licenses.bsdOriginal ];
895 license = [ pkgs.lib.licenses.bsdOriginal ];
851 };
896 };
852 };
897 };
853 "mock" = super.buildPythonPackage {
898 "mock" = super.buildPythonPackage {
854 name = "mock-1.0.1";
899 name = "mock-1.0.1";
855 doCheck = false;
900 doCheck = false;
856 src = fetchurl {
901 src = fetchurl {
857 url = "https://files.pythonhosted.org/packages/a2/52/7edcd94f0afb721a2d559a5b9aae8af4f8f2c79bc63fdbe8a8a6c9b23bbe/mock-1.0.1.tar.gz";
902 url = "https://files.pythonhosted.org/packages/a2/52/7edcd94f0afb721a2d559a5b9aae8af4f8f2c79bc63fdbe8a8a6c9b23bbe/mock-1.0.1.tar.gz";
858 sha256 = "0kzlsbki6q0awf89rc287f3aj8x431lrajf160a70z0ikhnxsfdq";
903 sha256 = "0kzlsbki6q0awf89rc287f3aj8x431lrajf160a70z0ikhnxsfdq";
859 };
904 };
860 meta = {
905 meta = {
861 license = [ pkgs.lib.licenses.bsdOriginal ];
906 license = [ pkgs.lib.licenses.bsdOriginal ];
862 };
907 };
863 };
908 };
864 "more-itertools" = super.buildPythonPackage {
909 "more-itertools" = super.buildPythonPackage {
865 name = "more-itertools-4.3.0";
910 name = "more-itertools-4.3.0";
866 doCheck = false;
911 doCheck = false;
867 propagatedBuildInputs = [
912 propagatedBuildInputs = [
868 self."six"
913 self."six"
869 ];
914 ];
870 src = fetchurl {
915 src = fetchurl {
871 url = "https://files.pythonhosted.org/packages/88/ff/6d485d7362f39880810278bdc906c13300db05485d9c65971dec1142da6a/more-itertools-4.3.0.tar.gz";
916 url = "https://files.pythonhosted.org/packages/88/ff/6d485d7362f39880810278bdc906c13300db05485d9c65971dec1142da6a/more-itertools-4.3.0.tar.gz";
872 sha256 = "17h3na0rdh8xq30w4b9pizgkdxmm51896bxw600x84jflg9vaxn4";
917 sha256 = "17h3na0rdh8xq30w4b9pizgkdxmm51896bxw600x84jflg9vaxn4";
873 };
918 };
874 meta = {
919 meta = {
875 license = [ pkgs.lib.licenses.mit ];
920 license = [ pkgs.lib.licenses.mit ];
876 };
921 };
877 };
922 };
878 "msgpack-python" = super.buildPythonPackage {
923 "msgpack-python" = super.buildPythonPackage {
879 name = "msgpack-python-0.5.6";
924 name = "msgpack-python-0.5.6";
880 doCheck = false;
925 doCheck = false;
881 src = fetchurl {
926 src = fetchurl {
882 url = "https://files.pythonhosted.org/packages/8a/20/6eca772d1a5830336f84aca1d8198e5a3f4715cd1c7fc36d3cc7f7185091/msgpack-python-0.5.6.tar.gz";
927 url = "https://files.pythonhosted.org/packages/8a/20/6eca772d1a5830336f84aca1d8198e5a3f4715cd1c7fc36d3cc7f7185091/msgpack-python-0.5.6.tar.gz";
883 sha256 = "16wh8qgybmfh4pjp8vfv78mdlkxfmcasg78lzlnm6nslsfkci31p";
928 sha256 = "16wh8qgybmfh4pjp8vfv78mdlkxfmcasg78lzlnm6nslsfkci31p";
884 };
929 };
885 meta = {
930 meta = {
886 license = [ pkgs.lib.licenses.asl20 ];
931 license = [ pkgs.lib.licenses.asl20 ];
887 };
932 };
888 };
933 };
889 "mysql-python" = super.buildPythonPackage {
934 "mysql-python" = super.buildPythonPackage {
890 name = "mysql-python-1.2.5";
935 name = "mysql-python-1.2.5";
891 doCheck = false;
936 doCheck = false;
892 src = fetchurl {
937 src = fetchurl {
893 url = "https://files.pythonhosted.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
938 url = "https://files.pythonhosted.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
894 sha256 = "0x0c2jg0bb3pp84njaqiic050qkyd7ymwhfvhipnimg58yv40441";
939 sha256 = "0x0c2jg0bb3pp84njaqiic050qkyd7ymwhfvhipnimg58yv40441";
895 };
940 };
896 meta = {
941 meta = {
897 license = [ pkgs.lib.licenses.gpl1 ];
942 license = [ pkgs.lib.licenses.gpl1 ];
898 };
943 };
899 };
944 };
900 "nbconvert" = super.buildPythonPackage {
945 "nbconvert" = super.buildPythonPackage {
901 name = "nbconvert-5.3.1";
946 name = "nbconvert-5.3.1";
902 doCheck = false;
947 doCheck = false;
903 propagatedBuildInputs = [
948 propagatedBuildInputs = [
904 self."mistune"
949 self."mistune"
905 self."jinja2"
950 self."jinja2"
906 self."pygments"
951 self."pygments"
907 self."traitlets"
952 self."traitlets"
908 self."jupyter-core"
953 self."jupyter-core"
909 self."nbformat"
954 self."nbformat"
910 self."entrypoints"
955 self."entrypoints"
911 self."bleach"
956 self."bleach"
912 self."pandocfilters"
957 self."pandocfilters"
913 self."testpath"
958 self."testpath"
914 ];
959 ];
915 src = fetchurl {
960 src = fetchurl {
916 url = "https://files.pythonhosted.org/packages/b9/a4/d0a0938ad6f5eeb4dea4e73d255c617ef94b0b2849d51194c9bbdb838412/nbconvert-5.3.1.tar.gz";
961 url = "https://files.pythonhosted.org/packages/b9/a4/d0a0938ad6f5eeb4dea4e73d255c617ef94b0b2849d51194c9bbdb838412/nbconvert-5.3.1.tar.gz";
917 sha256 = "1f9dkvpx186xjm4xab0qbph588mncp4vqk3fmxrsnqs43mks9c8j";
962 sha256 = "1f9dkvpx186xjm4xab0qbph588mncp4vqk3fmxrsnqs43mks9c8j";
918 };
963 };
919 meta = {
964 meta = {
920 license = [ pkgs.lib.licenses.bsdOriginal ];
965 license = [ pkgs.lib.licenses.bsdOriginal ];
921 };
966 };
922 };
967 };
923 "nbformat" = super.buildPythonPackage {
968 "nbformat" = super.buildPythonPackage {
924 name = "nbformat-4.4.0";
969 name = "nbformat-4.4.0";
925 doCheck = false;
970 doCheck = false;
926 propagatedBuildInputs = [
971 propagatedBuildInputs = [
927 self."ipython-genutils"
972 self."ipython-genutils"
928 self."traitlets"
973 self."traitlets"
929 self."jsonschema"
974 self."jsonschema"
930 self."jupyter-core"
975 self."jupyter-core"
931 ];
976 ];
932 src = fetchurl {
977 src = fetchurl {
933 url = "https://files.pythonhosted.org/packages/6e/0e/160754f7ae3e984863f585a3743b0ed1702043a81245907c8fae2d537155/nbformat-4.4.0.tar.gz";
978 url = "https://files.pythonhosted.org/packages/6e/0e/160754f7ae3e984863f585a3743b0ed1702043a81245907c8fae2d537155/nbformat-4.4.0.tar.gz";
934 sha256 = "00nlf08h8yc4q73nphfvfhxrcnilaqanb8z0mdy6nxk0vzq4wjgp";
979 sha256 = "00nlf08h8yc4q73nphfvfhxrcnilaqanb8z0mdy6nxk0vzq4wjgp";
935 };
980 };
936 meta = {
981 meta = {
937 license = [ pkgs.lib.licenses.bsdOriginal ];
982 license = [ pkgs.lib.licenses.bsdOriginal ];
938 };
983 };
939 };
984 };
940 "packaging" = super.buildPythonPackage {
985 "packaging" = super.buildPythonPackage {
941 name = "packaging-15.2";
986 name = "packaging-15.2";
942 doCheck = false;
987 doCheck = false;
943 src = fetchurl {
988 src = fetchurl {
944 url = "https://files.pythonhosted.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
989 url = "https://files.pythonhosted.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
945 sha256 = "1zn60w84bxvw6wypffka18ca66pa1k2cfrq3cq8fnsfja5m3k4ng";
990 sha256 = "1zn60w84bxvw6wypffka18ca66pa1k2cfrq3cq8fnsfja5m3k4ng";
946 };
991 };
947 meta = {
992 meta = {
948 license = [ pkgs.lib.licenses.asl20 ];
993 license = [ pkgs.lib.licenses.asl20 ];
949 };
994 };
950 };
995 };
951 "pandocfilters" = super.buildPythonPackage {
996 "pandocfilters" = super.buildPythonPackage {
952 name = "pandocfilters-1.4.2";
997 name = "pandocfilters-1.4.2";
953 doCheck = false;
998 doCheck = false;
954 src = fetchurl {
999 src = fetchurl {
955 url = "https://files.pythonhosted.org/packages/4c/ea/236e2584af67bb6df960832731a6e5325fd4441de001767da328c33368ce/pandocfilters-1.4.2.tar.gz";
1000 url = "https://files.pythonhosted.org/packages/4c/ea/236e2584af67bb6df960832731a6e5325fd4441de001767da328c33368ce/pandocfilters-1.4.2.tar.gz";
956 sha256 = "1a8d9b7s48gmq9zj0pmbyv2sivn5i7m6mybgpkk4jm5vd7hp1pdk";
1001 sha256 = "1a8d9b7s48gmq9zj0pmbyv2sivn5i7m6mybgpkk4jm5vd7hp1pdk";
957 };
1002 };
958 meta = {
1003 meta = {
959 license = [ pkgs.lib.licenses.bsdOriginal ];
1004 license = [ pkgs.lib.licenses.bsdOriginal ];
960 };
1005 };
961 };
1006 };
962 "paste" = super.buildPythonPackage {
1007 "paste" = super.buildPythonPackage {
963 name = "paste-3.0.5";
1008 name = "paste-3.0.5";
964 doCheck = false;
1009 doCheck = false;
965 propagatedBuildInputs = [
1010 propagatedBuildInputs = [
966 self."six"
1011 self."six"
967 ];
1012 ];
968 src = fetchurl {
1013 src = fetchurl {
969 url = "https://files.pythonhosted.org/packages/d4/41/91bde422400786b1b06357c1e6e3a5379f54dc3002aeb337cb767233304e/Paste-3.0.5.tar.gz";
1014 url = "https://files.pythonhosted.org/packages/d4/41/91bde422400786b1b06357c1e6e3a5379f54dc3002aeb337cb767233304e/Paste-3.0.5.tar.gz";
970 sha256 = "1a6i8fh1fg8r4x800fvy9r82m15clwjim6yf2g9r4dff0y40dchv";
1015 sha256 = "1a6i8fh1fg8r4x800fvy9r82m15clwjim6yf2g9r4dff0y40dchv";
971 };
1016 };
972 meta = {
1017 meta = {
973 license = [ pkgs.lib.licenses.mit ];
1018 license = [ pkgs.lib.licenses.mit ];
974 };
1019 };
975 };
1020 };
976 "pastedeploy" = super.buildPythonPackage {
1021 "pastedeploy" = super.buildPythonPackage {
977 name = "pastedeploy-2.0.1";
1022 name = "pastedeploy-2.0.1";
978 doCheck = false;
1023 doCheck = false;
979 src = fetchurl {
1024 src = fetchurl {
980 url = "https://files.pythonhosted.org/packages/19/a0/5623701df7e2478a68a1b685d1a84518024eef994cde7e4da8449a31616f/PasteDeploy-2.0.1.tar.gz";
1025 url = "https://files.pythonhosted.org/packages/19/a0/5623701df7e2478a68a1b685d1a84518024eef994cde7e4da8449a31616f/PasteDeploy-2.0.1.tar.gz";
981 sha256 = "02imfbbx1mi2h546f3sr37m47dk9qizaqhzzlhx8bkzxa6fzn8yl";
1026 sha256 = "02imfbbx1mi2h546f3sr37m47dk9qizaqhzzlhx8bkzxa6fzn8yl";
982 };
1027 };
983 meta = {
1028 meta = {
984 license = [ pkgs.lib.licenses.mit ];
1029 license = [ pkgs.lib.licenses.mit ];
985 };
1030 };
986 };
1031 };
987 "pastescript" = super.buildPythonPackage {
1032 "pastescript" = super.buildPythonPackage {
988 name = "pastescript-3.0.0";
1033 name = "pastescript-3.0.0";
989 doCheck = false;
1034 doCheck = false;
990 propagatedBuildInputs = [
1035 propagatedBuildInputs = [
991 self."paste"
1036 self."paste"
992 self."pastedeploy"
1037 self."pastedeploy"
993 self."six"
1038 self."six"
994 ];
1039 ];
995 src = fetchurl {
1040 src = fetchurl {
996 url = "https://files.pythonhosted.org/packages/08/2a/3797377a884ab9a064ad4d564ed612e54d26d7997caa8229c9c9df4eac31/PasteScript-3.0.0.tar.gz";
1041 url = "https://files.pythonhosted.org/packages/08/2a/3797377a884ab9a064ad4d564ed612e54d26d7997caa8229c9c9df4eac31/PasteScript-3.0.0.tar.gz";
997 sha256 = "1hvmyz1sbn7ws1syw567ph7km9fi0wi75r3vlyzx6sk0z26xkm6r";
1042 sha256 = "1hvmyz1sbn7ws1syw567ph7km9fi0wi75r3vlyzx6sk0z26xkm6r";
998 };
1043 };
999 meta = {
1044 meta = {
1000 license = [ pkgs.lib.licenses.mit ];
1045 license = [ pkgs.lib.licenses.mit ];
1001 };
1046 };
1002 };
1047 };
1003 "pathlib2" = super.buildPythonPackage {
1048 "pathlib2" = super.buildPythonPackage {
1004 name = "pathlib2-2.3.3";
1049 name = "pathlib2-2.3.3";
1005 doCheck = false;
1050 doCheck = false;
1006 propagatedBuildInputs = [
1051 propagatedBuildInputs = [
1007 self."six"
1052 self."six"
1008 self."scandir"
1053 self."scandir"
1009 ];
1054 ];
1010 src = fetchurl {
1055 src = fetchurl {
1011 url = "https://files.pythonhosted.org/packages/bf/d7/a2568f4596b75d2c6e2b4094a7e64f620decc7887f69a1f2811931ea15b9/pathlib2-2.3.3.tar.gz";
1056 url = "https://files.pythonhosted.org/packages/bf/d7/a2568f4596b75d2c6e2b4094a7e64f620decc7887f69a1f2811931ea15b9/pathlib2-2.3.3.tar.gz";
1012 sha256 = "0hpp92vqqgcd8h92msm9slv161b1q160igjwnkf2ag6cx0c96695";
1057 sha256 = "0hpp92vqqgcd8h92msm9slv161b1q160igjwnkf2ag6cx0c96695";
1013 };
1058 };
1014 meta = {
1059 meta = {
1015 license = [ pkgs.lib.licenses.mit ];
1060 license = [ pkgs.lib.licenses.mit ];
1016 };
1061 };
1017 };
1062 };
1018 "peppercorn" = super.buildPythonPackage {
1063 "peppercorn" = super.buildPythonPackage {
1019 name = "peppercorn-0.6";
1064 name = "peppercorn-0.6";
1020 doCheck = false;
1065 doCheck = false;
1021 src = fetchurl {
1066 src = fetchurl {
1022 url = "https://files.pythonhosted.org/packages/e4/77/93085de7108cdf1a0b092ff443872a8f9442c736d7ddebdf2f27627935f4/peppercorn-0.6.tar.gz";
1067 url = "https://files.pythonhosted.org/packages/e4/77/93085de7108cdf1a0b092ff443872a8f9442c736d7ddebdf2f27627935f4/peppercorn-0.6.tar.gz";
1023 sha256 = "1ip4bfwcpwkq9hz2dai14k2cyabvwrnvcvrcmzxmqm04g8fnimwn";
1068 sha256 = "1ip4bfwcpwkq9hz2dai14k2cyabvwrnvcvrcmzxmqm04g8fnimwn";
1024 };
1069 };
1025 meta = {
1070 meta = {
1026 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1071 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1027 };
1072 };
1028 };
1073 };
1029 "pexpect" = super.buildPythonPackage {
1074 "pexpect" = super.buildPythonPackage {
1030 name = "pexpect-4.6.0";
1075 name = "pexpect-4.6.0";
1031 doCheck = false;
1076 doCheck = false;
1032 propagatedBuildInputs = [
1077 propagatedBuildInputs = [
1033 self."ptyprocess"
1078 self."ptyprocess"
1034 ];
1079 ];
1035 src = fetchurl {
1080 src = fetchurl {
1036 url = "https://files.pythonhosted.org/packages/89/43/07d07654ee3e25235d8cea4164cdee0ec39d1fda8e9203156ebe403ffda4/pexpect-4.6.0.tar.gz";
1081 url = "https://files.pythonhosted.org/packages/89/43/07d07654ee3e25235d8cea4164cdee0ec39d1fda8e9203156ebe403ffda4/pexpect-4.6.0.tar.gz";
1037 sha256 = "1fla85g47iaxxpjhp9vkxdnv4pgc7rplfy6ja491smrrk0jqi3ia";
1082 sha256 = "1fla85g47iaxxpjhp9vkxdnv4pgc7rplfy6ja491smrrk0jqi3ia";
1038 };
1083 };
1039 meta = {
1084 meta = {
1040 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1085 license = [ pkgs.lib.licenses.isc { fullName = "ISC License (ISCL)"; } ];
1041 };
1086 };
1042 };
1087 };
1043 "pickleshare" = super.buildPythonPackage {
1088 "pickleshare" = super.buildPythonPackage {
1044 name = "pickleshare-0.7.5";
1089 name = "pickleshare-0.7.5";
1045 doCheck = false;
1090 doCheck = false;
1046 propagatedBuildInputs = [
1091 propagatedBuildInputs = [
1047 self."pathlib2"
1092 self."pathlib2"
1048 ];
1093 ];
1049 src = fetchurl {
1094 src = fetchurl {
1050 url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz";
1095 url = "https://files.pythonhosted.org/packages/d8/b6/df3c1c9b616e9c0edbc4fbab6ddd09df9535849c64ba51fcb6531c32d4d8/pickleshare-0.7.5.tar.gz";
1051 sha256 = "1jmghg3c53yp1i8cm6pcrm280ayi8621rwyav9fac7awjr3kss47";
1096 sha256 = "1jmghg3c53yp1i8cm6pcrm280ayi8621rwyav9fac7awjr3kss47";
1052 };
1097 };
1053 meta = {
1098 meta = {
1054 license = [ pkgs.lib.licenses.mit ];
1099 license = [ pkgs.lib.licenses.mit ];
1055 };
1100 };
1056 };
1101 };
1057 "plaster" = super.buildPythonPackage {
1102 "plaster" = super.buildPythonPackage {
1058 name = "plaster-1.0";
1103 name = "plaster-1.0";
1059 doCheck = false;
1104 doCheck = false;
1060 propagatedBuildInputs = [
1105 propagatedBuildInputs = [
1061 self."setuptools"
1106 self."setuptools"
1062 ];
1107 ];
1063 src = fetchurl {
1108 src = fetchurl {
1064 url = "https://files.pythonhosted.org/packages/37/e1/56d04382d718d32751017d32f351214384e529b794084eee20bb52405563/plaster-1.0.tar.gz";
1109 url = "https://files.pythonhosted.org/packages/37/e1/56d04382d718d32751017d32f351214384e529b794084eee20bb52405563/plaster-1.0.tar.gz";
1065 sha256 = "1hy8k0nv2mxq94y5aysk6hjk9ryb4bsd13g83m60hcyzxz3wflc3";
1110 sha256 = "1hy8k0nv2mxq94y5aysk6hjk9ryb4bsd13g83m60hcyzxz3wflc3";
1066 };
1111 };
1067 meta = {
1112 meta = {
1068 license = [ pkgs.lib.licenses.mit ];
1113 license = [ pkgs.lib.licenses.mit ];
1069 };
1114 };
1070 };
1115 };
1071 "plaster-pastedeploy" = super.buildPythonPackage {
1116 "plaster-pastedeploy" = super.buildPythonPackage {
1072 name = "plaster-pastedeploy-0.6";
1117 name = "plaster-pastedeploy-0.6";
1073 doCheck = false;
1118 doCheck = false;
1074 propagatedBuildInputs = [
1119 propagatedBuildInputs = [
1075 self."pastedeploy"
1120 self."pastedeploy"
1076 self."plaster"
1121 self."plaster"
1077 ];
1122 ];
1078 src = fetchurl {
1123 src = fetchurl {
1079 url = "https://files.pythonhosted.org/packages/3f/e7/6a6833158d2038ec40085433308a1e164fd1dac595513f6dd556d5669bb8/plaster_pastedeploy-0.6.tar.gz";
1124 url = "https://files.pythonhosted.org/packages/3f/e7/6a6833158d2038ec40085433308a1e164fd1dac595513f6dd556d5669bb8/plaster_pastedeploy-0.6.tar.gz";
1080 sha256 = "1bkggk18f4z2bmsmxyxabvf62znvjwbivzh880419r3ap0616cf2";
1125 sha256 = "1bkggk18f4z2bmsmxyxabvf62znvjwbivzh880419r3ap0616cf2";
1081 };
1126 };
1082 meta = {
1127 meta = {
1083 license = [ pkgs.lib.licenses.mit ];
1128 license = [ pkgs.lib.licenses.mit ];
1084 };
1129 };
1085 };
1130 };
1086 "pluggy" = super.buildPythonPackage {
1131 "pluggy" = super.buildPythonPackage {
1087 name = "pluggy-0.8.0";
1132 name = "pluggy-0.8.0";
1088 doCheck = false;
1133 doCheck = false;
1089 src = fetchurl {
1134 src = fetchurl {
1090 url = "https://files.pythonhosted.org/packages/65/25/81d0de17cd00f8ca994a4e74e3c4baf7cd25072c0b831dad5c7d9d6138f8/pluggy-0.8.0.tar.gz";
1135 url = "https://files.pythonhosted.org/packages/65/25/81d0de17cd00f8ca994a4e74e3c4baf7cd25072c0b831dad5c7d9d6138f8/pluggy-0.8.0.tar.gz";
1091 sha256 = "1580p47l2zqzsza8jcnw1h2wh3vvmygk6ly8bvi4w0g8j14sjys4";
1136 sha256 = "1580p47l2zqzsza8jcnw1h2wh3vvmygk6ly8bvi4w0g8j14sjys4";
1092 };
1137 };
1093 meta = {
1138 meta = {
1094 license = [ pkgs.lib.licenses.mit ];
1139 license = [ pkgs.lib.licenses.mit ];
1095 };
1140 };
1096 };
1141 };
1097 "prompt-toolkit" = super.buildPythonPackage {
1142 "prompt-toolkit" = super.buildPythonPackage {
1098 name = "prompt-toolkit-1.0.15";
1143 name = "prompt-toolkit-1.0.15";
1099 doCheck = false;
1144 doCheck = false;
1100 propagatedBuildInputs = [
1145 propagatedBuildInputs = [
1101 self."six"
1146 self."six"
1102 self."wcwidth"
1147 self."wcwidth"
1103 ];
1148 ];
1104 src = fetchurl {
1149 src = fetchurl {
1105 url = "https://files.pythonhosted.org/packages/8a/ad/cf6b128866e78ad6d7f1dc5b7f99885fb813393d9860778b2984582e81b5/prompt_toolkit-1.0.15.tar.gz";
1150 url = "https://files.pythonhosted.org/packages/8a/ad/cf6b128866e78ad6d7f1dc5b7f99885fb813393d9860778b2984582e81b5/prompt_toolkit-1.0.15.tar.gz";
1106 sha256 = "05v9h5nydljwpj5nm8n804ms0glajwfy1zagrzqrg91wk3qqi1c5";
1151 sha256 = "05v9h5nydljwpj5nm8n804ms0glajwfy1zagrzqrg91wk3qqi1c5";
1107 };
1152 };
1108 meta = {
1153 meta = {
1109 license = [ pkgs.lib.licenses.bsdOriginal ];
1154 license = [ pkgs.lib.licenses.bsdOriginal ];
1110 };
1155 };
1111 };
1156 };
1112 "psutil" = super.buildPythonPackage {
1157 "psutil" = super.buildPythonPackage {
1113 name = "psutil-5.4.7";
1158 name = "psutil-5.4.7";
1114 doCheck = false;
1159 doCheck = false;
1115 src = fetchurl {
1160 src = fetchurl {
1116 url = "https://files.pythonhosted.org/packages/7d/9a/1e93d41708f8ed2b564395edfa3389f0fd6d567597401c2e5e2775118d8b/psutil-5.4.7.tar.gz";
1161 url = "https://files.pythonhosted.org/packages/7d/9a/1e93d41708f8ed2b564395edfa3389f0fd6d567597401c2e5e2775118d8b/psutil-5.4.7.tar.gz";
1117 sha256 = "0fsgmvzwbdbszkwfnqhib8jcxm4w6zyhvlxlcda0rfm5cyqj4qsv";
1162 sha256 = "0fsgmvzwbdbszkwfnqhib8jcxm4w6zyhvlxlcda0rfm5cyqj4qsv";
1118 };
1163 };
1119 meta = {
1164 meta = {
1120 license = [ pkgs.lib.licenses.bsdOriginal ];
1165 license = [ pkgs.lib.licenses.bsdOriginal ];
1121 };
1166 };
1122 };
1167 };
1123 "psycopg2" = super.buildPythonPackage {
1168 "psycopg2" = super.buildPythonPackage {
1124 name = "psycopg2-2.7.5";
1169 name = "psycopg2-2.7.5";
1125 doCheck = false;
1170 doCheck = false;
1126 src = fetchurl {
1171 src = fetchurl {
1127 url = "https://files.pythonhosted.org/packages/b2/c1/7bf6c464e903ffc4f3f5907c389e5a4199666bf57f6cd6bf46c17912a1f9/psycopg2-2.7.5.tar.gz";
1172 url = "https://files.pythonhosted.org/packages/b2/c1/7bf6c464e903ffc4f3f5907c389e5a4199666bf57f6cd6bf46c17912a1f9/psycopg2-2.7.5.tar.gz";
1128 sha256 = "17klx964gw8z0znl0raz3by8vdc7cq5gxj4pdcrfcina84nrdkzc";
1173 sha256 = "17klx964gw8z0znl0raz3by8vdc7cq5gxj4pdcrfcina84nrdkzc";
1129 };
1174 };
1130 meta = {
1175 meta = {
1131 license = [ pkgs.lib.licenses.zpl21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1176 license = [ pkgs.lib.licenses.zpl21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
1132 };
1177 };
1133 };
1178 };
1134 "ptyprocess" = super.buildPythonPackage {
1179 "ptyprocess" = super.buildPythonPackage {
1135 name = "ptyprocess-0.6.0";
1180 name = "ptyprocess-0.6.0";
1136 doCheck = false;
1181 doCheck = false;
1137 src = fetchurl {
1182 src = fetchurl {
1138 url = "https://files.pythonhosted.org/packages/7d/2d/e4b8733cf79b7309d84c9081a4ab558c89d8c89da5961bf4ddb050ca1ce0/ptyprocess-0.6.0.tar.gz";
1183 url = "https://files.pythonhosted.org/packages/7d/2d/e4b8733cf79b7309d84c9081a4ab558c89d8c89da5961bf4ddb050ca1ce0/ptyprocess-0.6.0.tar.gz";
1139 sha256 = "1h4lcd3w5nrxnsk436ar7fwkiy5rfn5wj2xwy9l0r4mdqnf2jgwj";
1184 sha256 = "1h4lcd3w5nrxnsk436ar7fwkiy5rfn5wj2xwy9l0r4mdqnf2jgwj";
1140 };
1185 };
1141 meta = {
1186 meta = {
1142 license = [ ];
1187 license = [ ];
1143 };
1188 };
1144 };
1189 };
1145 "py" = super.buildPythonPackage {
1190 "py" = super.buildPythonPackage {
1146 name = "py-1.6.0";
1191 name = "py-1.6.0";
1147 doCheck = false;
1192 doCheck = false;
1148 src = fetchurl {
1193 src = fetchurl {
1149 url = "https://files.pythonhosted.org/packages/4f/38/5f427d1eedae73063ce4da680d2bae72014995f9fdeaa57809df61c968cd/py-1.6.0.tar.gz";
1194 url = "https://files.pythonhosted.org/packages/4f/38/5f427d1eedae73063ce4da680d2bae72014995f9fdeaa57809df61c968cd/py-1.6.0.tar.gz";
1150 sha256 = "1wcs3zv9wl5m5x7p16avqj2gsrviyb23yvc3pr330isqs0sh98q6";
1195 sha256 = "1wcs3zv9wl5m5x7p16avqj2gsrviyb23yvc3pr330isqs0sh98q6";
1151 };
1196 };
1152 meta = {
1197 meta = {
1153 license = [ pkgs.lib.licenses.mit ];
1198 license = [ pkgs.lib.licenses.mit ];
1154 };
1199 };
1155 };
1200 };
1156 "py-bcrypt" = super.buildPythonPackage {
1201 "py-bcrypt" = super.buildPythonPackage {
1157 name = "py-bcrypt-0.4";
1202 name = "py-bcrypt-0.4";
1158 doCheck = false;
1203 doCheck = false;
1159 src = fetchurl {
1204 src = fetchurl {
1160 url = "https://files.pythonhosted.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1205 url = "https://files.pythonhosted.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1161 sha256 = "0y6smdggwi5s72v6p1nn53dg6w05hna3d264cq6kas0lap73p8az";
1206 sha256 = "0y6smdggwi5s72v6p1nn53dg6w05hna3d264cq6kas0lap73p8az";
1162 };
1207 };
1163 meta = {
1208 meta = {
1164 license = [ pkgs.lib.licenses.bsdOriginal ];
1209 license = [ pkgs.lib.licenses.bsdOriginal ];
1165 };
1210 };
1166 };
1211 };
1167 "py-gfm" = super.buildPythonPackage {
1212 "py-gfm" = super.buildPythonPackage {
1168 name = "py-gfm-0.1.4";
1213 name = "py-gfm-0.1.4";
1169 doCheck = false;
1214 doCheck = false;
1170 propagatedBuildInputs = [
1215 propagatedBuildInputs = [
1171 self."setuptools"
1216 self."setuptools"
1172 self."markdown"
1217 self."markdown"
1173 ];
1218 ];
1174 src = fetchurl {
1219 src = fetchurl {
1175 url = "https://files.pythonhosted.org/packages/06/ee/004a03a1d92bb386dae44f6dd087db541bc5093374f1637d4d4ae5596cc2/py-gfm-0.1.4.tar.gz";
1220 url = "https://files.pythonhosted.org/packages/06/ee/004a03a1d92bb386dae44f6dd087db541bc5093374f1637d4d4ae5596cc2/py-gfm-0.1.4.tar.gz";
1176 sha256 = "0zip06g2isivx8fzgqd4n9qzsa22c25jas1rsb7m2rnjg72m0rzg";
1221 sha256 = "0zip06g2isivx8fzgqd4n9qzsa22c25jas1rsb7m2rnjg72m0rzg";
1177 };
1222 };
1178 meta = {
1223 meta = {
1179 license = [ pkgs.lib.licenses.bsdOriginal ];
1224 license = [ pkgs.lib.licenses.bsdOriginal ];
1180 };
1225 };
1181 };
1226 };
1182 "pyasn1" = super.buildPythonPackage {
1227 "pyasn1" = super.buildPythonPackage {
1183 name = "pyasn1-0.4.4";
1228 name = "pyasn1-0.4.4";
1184 doCheck = false;
1229 doCheck = false;
1185 src = fetchurl {
1230 src = fetchurl {
1186 url = "https://files.pythonhosted.org/packages/10/46/059775dc8e50f722d205452bced4b3cc965d27e8c3389156acd3b1123ae3/pyasn1-0.4.4.tar.gz";
1231 url = "https://files.pythonhosted.org/packages/10/46/059775dc8e50f722d205452bced4b3cc965d27e8c3389156acd3b1123ae3/pyasn1-0.4.4.tar.gz";
1187 sha256 = "0drilmx5j25aplfr5wrml0030cs5fgxp9yp94fhllxgx28yjm3zm";
1232 sha256 = "0drilmx5j25aplfr5wrml0030cs5fgxp9yp94fhllxgx28yjm3zm";
1188 };
1233 };
1189 meta = {
1234 meta = {
1190 license = [ pkgs.lib.licenses.bsdOriginal ];
1235 license = [ pkgs.lib.licenses.bsdOriginal ];
1191 };
1236 };
1192 };
1237 };
1193 "pyasn1-modules" = super.buildPythonPackage {
1238 "pyasn1-modules" = super.buildPythonPackage {
1194 name = "pyasn1-modules-0.2.2";
1239 name = "pyasn1-modules-0.2.2";
1195 doCheck = false;
1240 doCheck = false;
1196 propagatedBuildInputs = [
1241 propagatedBuildInputs = [
1197 self."pyasn1"
1242 self."pyasn1"
1198 ];
1243 ];
1199 src = fetchurl {
1244 src = fetchurl {
1200 url = "https://files.pythonhosted.org/packages/37/33/74ebdc52be534e683dc91faf263931bc00ae05c6073909fde53999088541/pyasn1-modules-0.2.2.tar.gz";
1245 url = "https://files.pythonhosted.org/packages/37/33/74ebdc52be534e683dc91faf263931bc00ae05c6073909fde53999088541/pyasn1-modules-0.2.2.tar.gz";
1201 sha256 = "0ivm850yi7ajjbi8j115qpsj95bgxdsx48nbjzg0zip788c3xkx0";
1246 sha256 = "0ivm850yi7ajjbi8j115qpsj95bgxdsx48nbjzg0zip788c3xkx0";
1202 };
1247 };
1203 meta = {
1248 meta = {
1204 license = [ pkgs.lib.licenses.bsdOriginal ];
1249 license = [ pkgs.lib.licenses.bsdOriginal ];
1205 };
1250 };
1206 };
1251 };
1207 "pycrypto" = super.buildPythonPackage {
1252 "pycrypto" = super.buildPythonPackage {
1208 name = "pycrypto-2.6.1";
1253 name = "pycrypto-2.6.1";
1209 doCheck = false;
1254 doCheck = false;
1210 src = fetchurl {
1255 src = fetchurl {
1211 url = "https://files.pythonhosted.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1256 url = "https://files.pythonhosted.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1212 sha256 = "0g0ayql5b9mkjam8hym6zyg6bv77lbh66rv1fyvgqb17kfc1xkpj";
1257 sha256 = "0g0ayql5b9mkjam8hym6zyg6bv77lbh66rv1fyvgqb17kfc1xkpj";
1213 };
1258 };
1214 meta = {
1259 meta = {
1215 license = [ pkgs.lib.licenses.publicDomain ];
1260 license = [ pkgs.lib.licenses.publicDomain ];
1216 };
1261 };
1217 };
1262 };
1218 "pycurl" = super.buildPythonPackage {
1263 "pycurl" = super.buildPythonPackage {
1219 name = "pycurl-7.43.0.2";
1264 name = "pycurl-7.43.0.2";
1220 doCheck = false;
1265 doCheck = false;
1221 src = fetchurl {
1266 src = fetchurl {
1222 url = "https://files.pythonhosted.org/packages/e8/e4/0dbb8735407189f00b33d84122b9be52c790c7c3b25286826f4e1bdb7bde/pycurl-7.43.0.2.tar.gz";
1267 url = "https://files.pythonhosted.org/packages/e8/e4/0dbb8735407189f00b33d84122b9be52c790c7c3b25286826f4e1bdb7bde/pycurl-7.43.0.2.tar.gz";
1223 sha256 = "1915kb04k1j4y6k1dx1sgnbddxrl9r1n4q928if2lkrdm73xy30g";
1268 sha256 = "1915kb04k1j4y6k1dx1sgnbddxrl9r1n4q928if2lkrdm73xy30g";
1224 };
1269 };
1225 meta = {
1270 meta = {
1226 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1271 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1227 };
1272 };
1228 };
1273 };
1229 "pyflakes" = super.buildPythonPackage {
1274 "pyflakes" = super.buildPythonPackage {
1230 name = "pyflakes-0.8.1";
1275 name = "pyflakes-0.8.1";
1231 doCheck = false;
1276 doCheck = false;
1232 src = fetchurl {
1277 src = fetchurl {
1233 url = "https://files.pythonhosted.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
1278 url = "https://files.pythonhosted.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
1234 sha256 = "0sbpq6pqm1i9wqi41mlfrsc5rk92jv4mskvlyxmnhlbdnc80ma1z";
1279 sha256 = "0sbpq6pqm1i9wqi41mlfrsc5rk92jv4mskvlyxmnhlbdnc80ma1z";
1235 };
1280 };
1236 meta = {
1281 meta = {
1237 license = [ pkgs.lib.licenses.mit ];
1282 license = [ pkgs.lib.licenses.mit ];
1238 };
1283 };
1239 };
1284 };
1240 "pygments" = super.buildPythonPackage {
1285 "pygments" = super.buildPythonPackage {
1241 name = "pygments-2.3.0";
1286 name = "pygments-2.3.0";
1242 doCheck = false;
1287 doCheck = false;
1243 src = fetchurl {
1288 src = fetchurl {
1244 url = "https://files.pythonhosted.org/packages/63/a2/91c31c4831853dedca2a08a0f94d788fc26a48f7281c99a303769ad2721b/Pygments-2.3.0.tar.gz";
1289 url = "https://files.pythonhosted.org/packages/63/a2/91c31c4831853dedca2a08a0f94d788fc26a48f7281c99a303769ad2721b/Pygments-2.3.0.tar.gz";
1245 sha256 = "1z34ms51dh4jq4h3cizp7vd1dmsxcbvffkjsd2xxfav22nn6lrl2";
1290 sha256 = "1z34ms51dh4jq4h3cizp7vd1dmsxcbvffkjsd2xxfav22nn6lrl2";
1246 };
1291 };
1247 meta = {
1292 meta = {
1248 license = [ pkgs.lib.licenses.bsdOriginal ];
1293 license = [ pkgs.lib.licenses.bsdOriginal ];
1249 };
1294 };
1250 };
1295 };
1251 "pymysql" = super.buildPythonPackage {
1296 "pymysql" = super.buildPythonPackage {
1252 name = "pymysql-0.8.1";
1297 name = "pymysql-0.8.1";
1253 doCheck = false;
1298 doCheck = false;
1254 src = fetchurl {
1299 src = fetchurl {
1255 url = "https://files.pythonhosted.org/packages/44/39/6bcb83cae0095a31b6be4511707fdf2009d3e29903a55a0494d3a9a2fac0/PyMySQL-0.8.1.tar.gz";
1300 url = "https://files.pythonhosted.org/packages/44/39/6bcb83cae0095a31b6be4511707fdf2009d3e29903a55a0494d3a9a2fac0/PyMySQL-0.8.1.tar.gz";
1256 sha256 = "0a96crz55bw4h6myh833skrli7b0ck89m3x673y2z2ryy7zrpq9l";
1301 sha256 = "0a96crz55bw4h6myh833skrli7b0ck89m3x673y2z2ryy7zrpq9l";
1257 };
1302 };
1258 meta = {
1303 meta = {
1259 license = [ pkgs.lib.licenses.mit ];
1304 license = [ pkgs.lib.licenses.mit ];
1260 };
1305 };
1261 };
1306 };
1262 "pyotp" = super.buildPythonPackage {
1307 "pyotp" = super.buildPythonPackage {
1263 name = "pyotp-2.2.7";
1308 name = "pyotp-2.2.7";
1264 doCheck = false;
1309 doCheck = false;
1265 src = fetchurl {
1310 src = fetchurl {
1266 url = "https://files.pythonhosted.org/packages/b1/ab/477cda97b6ca7baced5106471cb1ac1fe698d1b035983b9f8ee3422989eb/pyotp-2.2.7.tar.gz";
1311 url = "https://files.pythonhosted.org/packages/b1/ab/477cda97b6ca7baced5106471cb1ac1fe698d1b035983b9f8ee3422989eb/pyotp-2.2.7.tar.gz";
1267 sha256 = "00p69nw431f0s2ilg0hnd77p1l22m06p9rq4f8zfapmavnmzw3xy";
1312 sha256 = "00p69nw431f0s2ilg0hnd77p1l22m06p9rq4f8zfapmavnmzw3xy";
1268 };
1313 };
1269 meta = {
1314 meta = {
1270 license = [ pkgs.lib.licenses.mit ];
1315 license = [ pkgs.lib.licenses.mit ];
1271 };
1316 };
1272 };
1317 };
1273 "pyparsing" = super.buildPythonPackage {
1318 "pyparsing" = super.buildPythonPackage {
1274 name = "pyparsing-1.5.7";
1319 name = "pyparsing-2.3.0";
1275 doCheck = false;
1320 doCheck = false;
1276 src = fetchurl {
1321 src = fetchurl {
1277 url = "https://files.pythonhosted.org/packages/6f/2c/47457771c02a8ff0f302b695e094ec309e30452232bd79198ee94fda689f/pyparsing-1.5.7.tar.gz";
1322 url = "https://files.pythonhosted.org/packages/d0/09/3e6a5eeb6e04467b737d55f8bba15247ac0876f98fae659e58cd744430c6/pyparsing-2.3.0.tar.gz";
1278 sha256 = "17z7ws076z977sclj628fvwrp8y9j2rvdjcsq42v129n1gwi8vk4";
1323 sha256 = "14k5v7n3xqw8kzf42x06bzp184spnlkya2dpjyflax6l3yrallzk";
1279 };
1324 };
1280 meta = {
1325 meta = {
1281 license = [ pkgs.lib.licenses.mit ];
1326 license = [ pkgs.lib.licenses.mit ];
1282 };
1327 };
1283 };
1328 };
1284 "pyramid" = super.buildPythonPackage {
1329 "pyramid" = super.buildPythonPackage {
1285 name = "pyramid-1.10.1";
1330 name = "pyramid-1.10.1";
1286 doCheck = false;
1331 doCheck = false;
1287 propagatedBuildInputs = [
1332 propagatedBuildInputs = [
1288 self."hupper"
1333 self."hupper"
1289 self."plaster"
1334 self."plaster"
1290 self."plaster-pastedeploy"
1335 self."plaster-pastedeploy"
1291 self."setuptools"
1336 self."setuptools"
1292 self."translationstring"
1337 self."translationstring"
1293 self."venusian"
1338 self."venusian"
1294 self."webob"
1339 self."webob"
1295 self."zope.deprecation"
1340 self."zope.deprecation"
1296 self."zope.interface"
1341 self."zope.interface"
1297 self."repoze.lru"
1342 self."repoze.lru"
1298 ];
1343 ];
1299 src = fetchurl {
1344 src = fetchurl {
1300 url = "https://files.pythonhosted.org/packages/0a/3e/22e3ac9be1b70a01139adba8906ee4b8f628bb469fea3c52f6c97b73063c/pyramid-1.10.1.tar.gz";
1345 url = "https://files.pythonhosted.org/packages/0a/3e/22e3ac9be1b70a01139adba8906ee4b8f628bb469fea3c52f6c97b73063c/pyramid-1.10.1.tar.gz";
1301 sha256 = "1h5105nfh6rsrfjiyw20aavyibj36la3hajy6vh1fa77xb4y3hrp";
1346 sha256 = "1h5105nfh6rsrfjiyw20aavyibj36la3hajy6vh1fa77xb4y3hrp";
1302 };
1347 };
1303 meta = {
1348 meta = {
1304 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1349 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1305 };
1350 };
1306 };
1351 };
1307 "pyramid-beaker" = super.buildPythonPackage {
1352 "pyramid-beaker" = super.buildPythonPackage {
1308 name = "pyramid-beaker-0.8";
1353 name = "pyramid-beaker-0.8";
1309 doCheck = false;
1354 doCheck = false;
1310 propagatedBuildInputs = [
1355 propagatedBuildInputs = [
1311 self."pyramid"
1356 self."pyramid"
1312 self."beaker"
1357 self."beaker"
1313 ];
1358 ];
1314 src = fetchurl {
1359 src = fetchurl {
1315 url = "https://files.pythonhosted.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
1360 url = "https://files.pythonhosted.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
1316 sha256 = "0hflx3qkcdml1mwpq53sz46s7jickpfn0zy0ns2c7j445j66bp3p";
1361 sha256 = "0hflx3qkcdml1mwpq53sz46s7jickpfn0zy0ns2c7j445j66bp3p";
1317 };
1362 };
1318 meta = {
1363 meta = {
1319 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1364 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1320 };
1365 };
1321 };
1366 };
1322 "pyramid-debugtoolbar" = super.buildPythonPackage {
1367 "pyramid-debugtoolbar" = super.buildPythonPackage {
1323 name = "pyramid-debugtoolbar-4.4";
1368 name = "pyramid-debugtoolbar-4.4";
1324 doCheck = false;
1369 doCheck = false;
1325 propagatedBuildInputs = [
1370 propagatedBuildInputs = [
1326 self."pyramid"
1371 self."pyramid"
1327 self."pyramid-mako"
1372 self."pyramid-mako"
1328 self."repoze.lru"
1373 self."repoze.lru"
1329 self."pygments"
1374 self."pygments"
1330 self."ipaddress"
1375 self."ipaddress"
1331 ];
1376 ];
1332 src = fetchurl {
1377 src = fetchurl {
1333 url = "https://files.pythonhosted.org/packages/00/6f/c04eb4e715a7a5a4b24079ab7ffd1dceb1f70b2e24fc17686a2922dbac0a/pyramid_debugtoolbar-4.4.tar.gz";
1378 url = "https://files.pythonhosted.org/packages/00/6f/c04eb4e715a7a5a4b24079ab7ffd1dceb1f70b2e24fc17686a2922dbac0a/pyramid_debugtoolbar-4.4.tar.gz";
1334 sha256 = "17p7nxvapvy2hab1rah3ndq2kbs4v83pixj8x2n4m7008ai9lxsz";
1379 sha256 = "17p7nxvapvy2hab1rah3ndq2kbs4v83pixj8x2n4m7008ai9lxsz";
1335 };
1380 };
1336 meta = {
1381 meta = {
1337 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1382 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1338 };
1383 };
1339 };
1384 };
1340 "pyramid-jinja2" = super.buildPythonPackage {
1385 "pyramid-jinja2" = super.buildPythonPackage {
1341 name = "pyramid-jinja2-2.7";
1386 name = "pyramid-jinja2-2.7";
1342 doCheck = false;
1387 doCheck = false;
1343 propagatedBuildInputs = [
1388 propagatedBuildInputs = [
1344 self."pyramid"
1389 self."pyramid"
1345 self."zope.deprecation"
1390 self."zope.deprecation"
1346 self."jinja2"
1391 self."jinja2"
1347 self."markupsafe"
1392 self."markupsafe"
1348 ];
1393 ];
1349 src = fetchurl {
1394 src = fetchurl {
1350 url = "https://files.pythonhosted.org/packages/d8/80/d60a7233823de22ce77bd864a8a83736a1fe8b49884b08303a2e68b2c853/pyramid_jinja2-2.7.tar.gz";
1395 url = "https://files.pythonhosted.org/packages/d8/80/d60a7233823de22ce77bd864a8a83736a1fe8b49884b08303a2e68b2c853/pyramid_jinja2-2.7.tar.gz";
1351 sha256 = "1sz5s0pp5jqhf4w22w9527yz8hgdi4mhr6apd6vw1gm5clghh8aw";
1396 sha256 = "1sz5s0pp5jqhf4w22w9527yz8hgdi4mhr6apd6vw1gm5clghh8aw";
1352 };
1397 };
1353 meta = {
1398 meta = {
1354 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1399 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1355 };
1400 };
1356 };
1401 };
1357 "pyramid-mailer" = super.buildPythonPackage {
1402 "pyramid-mailer" = super.buildPythonPackage {
1358 name = "pyramid-mailer-0.15.1";
1403 name = "pyramid-mailer-0.15.1";
1359 doCheck = false;
1404 doCheck = false;
1360 propagatedBuildInputs = [
1405 propagatedBuildInputs = [
1361 self."pyramid"
1406 self."pyramid"
1362 self."repoze.sendmail"
1407 self."repoze.sendmail"
1363 self."transaction"
1408 self."transaction"
1364 ];
1409 ];
1365 src = fetchurl {
1410 src = fetchurl {
1366 url = "https://files.pythonhosted.org/packages/a0/f2/6febf5459dff4d7e653314d575469ad2e11b9d2af2c3606360e1c67202f2/pyramid_mailer-0.15.1.tar.gz";
1411 url = "https://files.pythonhosted.org/packages/a0/f2/6febf5459dff4d7e653314d575469ad2e11b9d2af2c3606360e1c67202f2/pyramid_mailer-0.15.1.tar.gz";
1367 sha256 = "16vg8jb203jgb7b0hd6wllfqvp542qh2ry1gjai2m6qpv5agy2pc";
1412 sha256 = "16vg8jb203jgb7b0hd6wllfqvp542qh2ry1gjai2m6qpv5agy2pc";
1368 };
1413 };
1369 meta = {
1414 meta = {
1370 license = [ pkgs.lib.licenses.bsdOriginal ];
1415 license = [ pkgs.lib.licenses.bsdOriginal ];
1371 };
1416 };
1372 };
1417 };
1373 "pyramid-mako" = super.buildPythonPackage {
1418 "pyramid-mako" = super.buildPythonPackage {
1374 name = "pyramid-mako-1.0.2";
1419 name = "pyramid-mako-1.0.2";
1375 doCheck = false;
1420 doCheck = false;
1376 propagatedBuildInputs = [
1421 propagatedBuildInputs = [
1377 self."pyramid"
1422 self."pyramid"
1378 self."mako"
1423 self."mako"
1379 ];
1424 ];
1380 src = fetchurl {
1425 src = fetchurl {
1381 url = "https://files.pythonhosted.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
1426 url = "https://files.pythonhosted.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
1382 sha256 = "18gk2vliq8z4acblsl6yzgbvnr9rlxjlcqir47km7kvlk1xri83d";
1427 sha256 = "18gk2vliq8z4acblsl6yzgbvnr9rlxjlcqir47km7kvlk1xri83d";
1383 };
1428 };
1384 meta = {
1429 meta = {
1385 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1430 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1386 };
1431 };
1387 };
1432 };
1388 "pysqlite" = super.buildPythonPackage {
1433 "pysqlite" = super.buildPythonPackage {
1389 name = "pysqlite-2.8.3";
1434 name = "pysqlite-2.8.3";
1390 doCheck = false;
1435 doCheck = false;
1391 src = fetchurl {
1436 src = fetchurl {
1392 url = "https://files.pythonhosted.org/packages/42/02/981b6703e3c83c5b25a829c6e77aad059f9481b0bbacb47e6e8ca12bd731/pysqlite-2.8.3.tar.gz";
1437 url = "https://files.pythonhosted.org/packages/42/02/981b6703e3c83c5b25a829c6e77aad059f9481b0bbacb47e6e8ca12bd731/pysqlite-2.8.3.tar.gz";
1393 sha256 = "1424gwq9sil2ffmnizk60q36vydkv8rxs6m7xs987kz8cdc37lqp";
1438 sha256 = "1424gwq9sil2ffmnizk60q36vydkv8rxs6m7xs987kz8cdc37lqp";
1394 };
1439 };
1395 meta = {
1440 meta = {
1396 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1441 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1397 };
1442 };
1398 };
1443 };
1399 "pytest" = super.buildPythonPackage {
1444 "pytest" = super.buildPythonPackage {
1400 name = "pytest-3.8.2";
1445 name = "pytest-3.8.2";
1401 doCheck = false;
1446 doCheck = false;
1402 propagatedBuildInputs = [
1447 propagatedBuildInputs = [
1403 self."py"
1448 self."py"
1404 self."six"
1449 self."six"
1405 self."setuptools"
1450 self."setuptools"
1406 self."attrs"
1451 self."attrs"
1407 self."more-itertools"
1452 self."more-itertools"
1408 self."atomicwrites"
1453 self."atomicwrites"
1409 self."pluggy"
1454 self."pluggy"
1410 self."funcsigs"
1455 self."funcsigs"
1411 self."pathlib2"
1456 self."pathlib2"
1412 ];
1457 ];
1413 src = fetchurl {
1458 src = fetchurl {
1414 url = "https://files.pythonhosted.org/packages/5f/d2/7f77f406ac505abda02ab4afb50d06ebf304f6ea42fca34f8f37529106b2/pytest-3.8.2.tar.gz";
1459 url = "https://files.pythonhosted.org/packages/5f/d2/7f77f406ac505abda02ab4afb50d06ebf304f6ea42fca34f8f37529106b2/pytest-3.8.2.tar.gz";
1415 sha256 = "18nrwzn61kph2y6gxwfz9ms68rfvr9d4vcffsxng9p7jk9z18clk";
1460 sha256 = "18nrwzn61kph2y6gxwfz9ms68rfvr9d4vcffsxng9p7jk9z18clk";
1416 };
1461 };
1417 meta = {
1462 meta = {
1418 license = [ pkgs.lib.licenses.mit ];
1463 license = [ pkgs.lib.licenses.mit ];
1419 };
1464 };
1420 };
1465 };
1421 "pytest-cov" = super.buildPythonPackage {
1466 "pytest-cov" = super.buildPythonPackage {
1422 name = "pytest-cov-2.6.0";
1467 name = "pytest-cov-2.6.0";
1423 doCheck = false;
1468 doCheck = false;
1424 propagatedBuildInputs = [
1469 propagatedBuildInputs = [
1425 self."pytest"
1470 self."pytest"
1426 self."coverage"
1471 self."coverage"
1427 ];
1472 ];
1428 src = fetchurl {
1473 src = fetchurl {
1429 url = "https://files.pythonhosted.org/packages/d9/e2/58f90a316fbd94dd50bf5c826a23f3f5d079fb3cc448c1e9f0e3c33a3d2a/pytest-cov-2.6.0.tar.gz";
1474 url = "https://files.pythonhosted.org/packages/d9/e2/58f90a316fbd94dd50bf5c826a23f3f5d079fb3cc448c1e9f0e3c33a3d2a/pytest-cov-2.6.0.tar.gz";
1430 sha256 = "0qnpp9y3ygx4jk4pf5ad71fh2skbvnr6gl54m7rg5qysnx4g0q73";
1475 sha256 = "0qnpp9y3ygx4jk4pf5ad71fh2skbvnr6gl54m7rg5qysnx4g0q73";
1431 };
1476 };
1432 meta = {
1477 meta = {
1433 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1478 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.mit ];
1434 };
1479 };
1435 };
1480 };
1436 "pytest-profiling" = super.buildPythonPackage {
1481 "pytest-profiling" = super.buildPythonPackage {
1437 name = "pytest-profiling-1.3.0";
1482 name = "pytest-profiling-1.3.0";
1438 doCheck = false;
1483 doCheck = false;
1439 propagatedBuildInputs = [
1484 propagatedBuildInputs = [
1440 self."six"
1485 self."six"
1441 self."pytest"
1486 self."pytest"
1442 self."gprof2dot"
1487 self."gprof2dot"
1443 ];
1488 ];
1444 src = fetchurl {
1489 src = fetchurl {
1445 url = "https://files.pythonhosted.org/packages/f5/34/4626126e041a51ef50a80d0619519b18d20aef249aac25b0d0fdd47e57ee/pytest-profiling-1.3.0.tar.gz";
1490 url = "https://files.pythonhosted.org/packages/f5/34/4626126e041a51ef50a80d0619519b18d20aef249aac25b0d0fdd47e57ee/pytest-profiling-1.3.0.tar.gz";
1446 sha256 = "08r5afx5z22yvpmsnl91l4amsy1yxn8qsmm61mhp06mz8zjs51kb";
1491 sha256 = "08r5afx5z22yvpmsnl91l4amsy1yxn8qsmm61mhp06mz8zjs51kb";
1447 };
1492 };
1448 meta = {
1493 meta = {
1449 license = [ pkgs.lib.licenses.mit ];
1494 license = [ pkgs.lib.licenses.mit ];
1450 };
1495 };
1451 };
1496 };
1452 "pytest-runner" = super.buildPythonPackage {
1497 "pytest-runner" = super.buildPythonPackage {
1453 name = "pytest-runner-4.2";
1498 name = "pytest-runner-4.2";
1454 doCheck = false;
1499 doCheck = false;
1455 src = fetchurl {
1500 src = fetchurl {
1456 url = "https://files.pythonhosted.org/packages/9e/b7/fe6e8f87f9a756fd06722216f1b6698ccba4d269eac6329d9f0c441d0f93/pytest-runner-4.2.tar.gz";
1501 url = "https://files.pythonhosted.org/packages/9e/b7/fe6e8f87f9a756fd06722216f1b6698ccba4d269eac6329d9f0c441d0f93/pytest-runner-4.2.tar.gz";
1457 sha256 = "1gkpyphawxz38ni1gdq1fmwyqcg02m7ypzqvv46z06crwdxi2gyj";
1502 sha256 = "1gkpyphawxz38ni1gdq1fmwyqcg02m7ypzqvv46z06crwdxi2gyj";
1458 };
1503 };
1459 meta = {
1504 meta = {
1460 license = [ pkgs.lib.licenses.mit ];
1505 license = [ pkgs.lib.licenses.mit ];
1461 };
1506 };
1462 };
1507 };
1463 "pytest-sugar" = super.buildPythonPackage {
1508 "pytest-sugar" = super.buildPythonPackage {
1464 name = "pytest-sugar-0.9.1";
1509 name = "pytest-sugar-0.9.1";
1465 doCheck = false;
1510 doCheck = false;
1466 propagatedBuildInputs = [
1511 propagatedBuildInputs = [
1467 self."pytest"
1512 self."pytest"
1468 self."termcolor"
1513 self."termcolor"
1469 ];
1514 ];
1470 src = fetchurl {
1515 src = fetchurl {
1471 url = "https://files.pythonhosted.org/packages/3e/6a/a3f909083079d03bde11d06ab23088886bbe25f2c97fbe4bb865e2bf05bc/pytest-sugar-0.9.1.tar.gz";
1516 url = "https://files.pythonhosted.org/packages/3e/6a/a3f909083079d03bde11d06ab23088886bbe25f2c97fbe4bb865e2bf05bc/pytest-sugar-0.9.1.tar.gz";
1472 sha256 = "0b4av40dv30727m54v211r0nzwjp2ajkjgxix6j484qjmwpw935b";
1517 sha256 = "0b4av40dv30727m54v211r0nzwjp2ajkjgxix6j484qjmwpw935b";
1473 };
1518 };
1474 meta = {
1519 meta = {
1475 license = [ pkgs.lib.licenses.bsdOriginal ];
1520 license = [ pkgs.lib.licenses.bsdOriginal ];
1476 };
1521 };
1477 };
1522 };
1478 "pytest-timeout" = super.buildPythonPackage {
1523 "pytest-timeout" = super.buildPythonPackage {
1479 name = "pytest-timeout-1.3.2";
1524 name = "pytest-timeout-1.3.2";
1480 doCheck = false;
1525 doCheck = false;
1481 propagatedBuildInputs = [
1526 propagatedBuildInputs = [
1482 self."pytest"
1527 self."pytest"
1483 ];
1528 ];
1484 src = fetchurl {
1529 src = fetchurl {
1485 url = "https://files.pythonhosted.org/packages/8c/3e/1b6a319d12ae7baa3acb7c18ff2c8630a09471a0319d43535c683b4d03eb/pytest-timeout-1.3.2.tar.gz";
1530 url = "https://files.pythonhosted.org/packages/8c/3e/1b6a319d12ae7baa3acb7c18ff2c8630a09471a0319d43535c683b4d03eb/pytest-timeout-1.3.2.tar.gz";
1486 sha256 = "09wnmzvnls2mnsdz7x3c3sk2zdp6jl4dryvyj5i8hqz16q2zq5qi";
1531 sha256 = "09wnmzvnls2mnsdz7x3c3sk2zdp6jl4dryvyj5i8hqz16q2zq5qi";
1487 };
1532 };
1488 meta = {
1533 meta = {
1489 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1534 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1490 };
1535 };
1491 };
1536 };
1492 "python-dateutil" = super.buildPythonPackage {
1537 "python-dateutil" = super.buildPythonPackage {
1493 name = "python-dateutil-2.7.5";
1538 name = "python-dateutil-2.7.5";
1494 doCheck = false;
1539 doCheck = false;
1495 propagatedBuildInputs = [
1540 propagatedBuildInputs = [
1496 self."six"
1541 self."six"
1497 ];
1542 ];
1498 src = fetchurl {
1543 src = fetchurl {
1499 url = "https://files.pythonhosted.org/packages/0e/01/68747933e8d12263d41ce08119620d9a7e5eb72c876a3442257f74490da0/python-dateutil-2.7.5.tar.gz";
1544 url = "https://files.pythonhosted.org/packages/0e/01/68747933e8d12263d41ce08119620d9a7e5eb72c876a3442257f74490da0/python-dateutil-2.7.5.tar.gz";
1500 sha256 = "00ngwcdw36w5b37b51mdwn3qxid9zdf3kpffv2q6n9kl05y2iyc8";
1545 sha256 = "00ngwcdw36w5b37b51mdwn3qxid9zdf3kpffv2q6n9kl05y2iyc8";
1501 };
1546 };
1502 meta = {
1547 meta = {
1503 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.asl20 { fullName = "Dual License"; } ];
1548 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.asl20 { fullName = "Dual License"; } ];
1504 };
1549 };
1505 };
1550 };
1506 "python-editor" = super.buildPythonPackage {
1551 "python-editor" = super.buildPythonPackage {
1507 name = "python-editor-1.0.3";
1552 name = "python-editor-1.0.3";
1508 doCheck = false;
1553 doCheck = false;
1509 src = fetchurl {
1554 src = fetchurl {
1510 url = "https://files.pythonhosted.org/packages/65/1e/adf6e000ea5dc909aa420352d6ba37f16434c8a3c2fa030445411a1ed545/python-editor-1.0.3.tar.gz";
1555 url = "https://files.pythonhosted.org/packages/65/1e/adf6e000ea5dc909aa420352d6ba37f16434c8a3c2fa030445411a1ed545/python-editor-1.0.3.tar.gz";
1511 sha256 = "0rf5xz8vw93v7mhdcvind7fkykipzga430wkcd7wk892xsn6dh53";
1556 sha256 = "0rf5xz8vw93v7mhdcvind7fkykipzga430wkcd7wk892xsn6dh53";
1512 };
1557 };
1513 meta = {
1558 meta = {
1514 license = [ pkgs.lib.licenses.asl20 { fullName = "Apache"; } ];
1559 license = [ pkgs.lib.licenses.asl20 { fullName = "Apache"; } ];
1515 };
1560 };
1516 };
1561 };
1517 "python-ldap" = super.buildPythonPackage {
1562 "python-ldap" = super.buildPythonPackage {
1518 name = "python-ldap-3.1.0";
1563 name = "python-ldap-3.1.0";
1519 doCheck = false;
1564 doCheck = false;
1520 propagatedBuildInputs = [
1565 propagatedBuildInputs = [
1521 self."pyasn1"
1566 self."pyasn1"
1522 self."pyasn1-modules"
1567 self."pyasn1-modules"
1523 ];
1568 ];
1524 src = fetchurl {
1569 src = fetchurl {
1525 url = "https://files.pythonhosted.org/packages/7f/1c/28d721dff2fcd2fef9d55b40df63a00be26ec8a11e8c6fc612ae642f9cfd/python-ldap-3.1.0.tar.gz";
1570 url = "https://files.pythonhosted.org/packages/7f/1c/28d721dff2fcd2fef9d55b40df63a00be26ec8a11e8c6fc612ae642f9cfd/python-ldap-3.1.0.tar.gz";
1526 sha256 = "1i97nwfnraylyn0myxlf3vciicrf5h6fymrcff9c00k581wmx5s1";
1571 sha256 = "1i97nwfnraylyn0myxlf3vciicrf5h6fymrcff9c00k581wmx5s1";
1527 };
1572 };
1528 meta = {
1573 meta = {
1529 license = [ pkgs.lib.licenses.psfl ];
1574 license = [ pkgs.lib.licenses.psfl ];
1530 };
1575 };
1531 };
1576 };
1532 "python-memcached" = super.buildPythonPackage {
1577 "python-memcached" = super.buildPythonPackage {
1533 name = "python-memcached-1.59";
1578 name = "python-memcached-1.59";
1534 doCheck = false;
1579 doCheck = false;
1535 propagatedBuildInputs = [
1580 propagatedBuildInputs = [
1536 self."six"
1581 self."six"
1537 ];
1582 ];
1538 src = fetchurl {
1583 src = fetchurl {
1539 url = "https://files.pythonhosted.org/packages/90/59/5faf6e3cd8a568dd4f737ddae4f2e54204fd8c51f90bf8df99aca6c22318/python-memcached-1.59.tar.gz";
1584 url = "https://files.pythonhosted.org/packages/90/59/5faf6e3cd8a568dd4f737ddae4f2e54204fd8c51f90bf8df99aca6c22318/python-memcached-1.59.tar.gz";
1540 sha256 = "0kvyapavbirk2x3n1jx4yb9nyigrj1s3x15nm3qhpvhkpqvqdqm2";
1585 sha256 = "0kvyapavbirk2x3n1jx4yb9nyigrj1s3x15nm3qhpvhkpqvqdqm2";
1541 };
1586 };
1542 meta = {
1587 meta = {
1543 license = [ pkgs.lib.licenses.psfl ];
1588 license = [ pkgs.lib.licenses.psfl ];
1544 };
1589 };
1545 };
1590 };
1546 "python-pam" = super.buildPythonPackage {
1591 "python-pam" = super.buildPythonPackage {
1547 name = "python-pam-1.8.4";
1592 name = "python-pam-1.8.4";
1548 doCheck = false;
1593 doCheck = false;
1549 src = fetchurl {
1594 src = fetchurl {
1550 url = "https://files.pythonhosted.org/packages/01/16/544d01cae9f28e0292dbd092b6b8b0bf222b528f362ee768a5bed2140111/python-pam-1.8.4.tar.gz";
1595 url = "https://files.pythonhosted.org/packages/01/16/544d01cae9f28e0292dbd092b6b8b0bf222b528f362ee768a5bed2140111/python-pam-1.8.4.tar.gz";
1551 sha256 = "16whhc0vr7gxsbzvsnq65nq8fs3wwmx755cavm8kkczdkz4djmn8";
1596 sha256 = "16whhc0vr7gxsbzvsnq65nq8fs3wwmx755cavm8kkczdkz4djmn8";
1552 };
1597 };
1553 meta = {
1598 meta = {
1554 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1599 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1555 };
1600 };
1556 };
1601 };
1557 "python-saml" = super.buildPythonPackage {
1602 "python-saml" = super.buildPythonPackage {
1558 name = "python-saml-2.4.2";
1603 name = "python-saml-2.4.2";
1559 doCheck = false;
1604 doCheck = false;
1560 propagatedBuildInputs = [
1605 propagatedBuildInputs = [
1561 self."dm.xmlsec.binding"
1606 self."dm.xmlsec.binding"
1562 self."isodate"
1607 self."isodate"
1563 self."defusedxml"
1608 self."defusedxml"
1564 ];
1609 ];
1565 src = fetchurl {
1610 src = fetchurl {
1566 url = "https://files.pythonhosted.org/packages/79/a8/a6611017e0883102fd5e2b73c9d90691b8134e38247c04ee1531d3dc647c/python-saml-2.4.2.tar.gz";
1611 url = "https://files.pythonhosted.org/packages/79/a8/a6611017e0883102fd5e2b73c9d90691b8134e38247c04ee1531d3dc647c/python-saml-2.4.2.tar.gz";
1567 sha256 = "0dls4hwvf13yg7x5yfjrghbywg8g38vn5vr0rsf70hli3ydbfm43";
1612 sha256 = "0dls4hwvf13yg7x5yfjrghbywg8g38vn5vr0rsf70hli3ydbfm43";
1568 };
1613 };
1569 meta = {
1614 meta = {
1570 license = [ pkgs.lib.licenses.mit ];
1615 license = [ pkgs.lib.licenses.mit ];
1571 };
1616 };
1572 };
1617 };
1573 "pytz" = super.buildPythonPackage {
1618 "pytz" = super.buildPythonPackage {
1574 name = "pytz-2018.4";
1619 name = "pytz-2018.4";
1575 doCheck = false;
1620 doCheck = false;
1576 src = fetchurl {
1621 src = fetchurl {
1577 url = "https://files.pythonhosted.org/packages/10/76/52efda4ef98e7544321fd8d5d512e11739c1df18b0649551aeccfb1c8376/pytz-2018.4.tar.gz";
1622 url = "https://files.pythonhosted.org/packages/10/76/52efda4ef98e7544321fd8d5d512e11739c1df18b0649551aeccfb1c8376/pytz-2018.4.tar.gz";
1578 sha256 = "0jgpqx3kk2rhv81j1izjxvmx8d0x7hzs1857pgqnixic5wq2ar60";
1623 sha256 = "0jgpqx3kk2rhv81j1izjxvmx8d0x7hzs1857pgqnixic5wq2ar60";
1579 };
1624 };
1580 meta = {
1625 meta = {
1581 license = [ pkgs.lib.licenses.mit ];
1626 license = [ pkgs.lib.licenses.mit ];
1582 };
1627 };
1583 };
1628 };
1584 "pyzmq" = super.buildPythonPackage {
1629 "pyzmq" = super.buildPythonPackage {
1585 name = "pyzmq-14.6.0";
1630 name = "pyzmq-14.6.0";
1586 doCheck = false;
1631 doCheck = false;
1587 src = fetchurl {
1632 src = fetchurl {
1588 url = "https://files.pythonhosted.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1633 url = "https://files.pythonhosted.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1589 sha256 = "1frmbjykvhmdg64g7sn20c9fpamrsfxwci1nhhg8q7jgz5pq0ikp";
1634 sha256 = "1frmbjykvhmdg64g7sn20c9fpamrsfxwci1nhhg8q7jgz5pq0ikp";
1590 };
1635 };
1591 meta = {
1636 meta = {
1592 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1637 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1593 };
1638 };
1594 };
1639 };
1595 "redis" = super.buildPythonPackage {
1640 "redis" = super.buildPythonPackage {
1596 name = "redis-2.10.6";
1641 name = "redis-2.10.6";
1597 doCheck = false;
1642 doCheck = false;
1598 src = fetchurl {
1643 src = fetchurl {
1599 url = "https://files.pythonhosted.org/packages/09/8d/6d34b75326bf96d4139a2ddd8e74b80840f800a0a79f9294399e212cb9a7/redis-2.10.6.tar.gz";
1644 url = "https://files.pythonhosted.org/packages/09/8d/6d34b75326bf96d4139a2ddd8e74b80840f800a0a79f9294399e212cb9a7/redis-2.10.6.tar.gz";
1600 sha256 = "03vcgklykny0g0wpvqmy8p6azi2s078317wgb2xjv5m2rs9sjb52";
1645 sha256 = "03vcgklykny0g0wpvqmy8p6azi2s078317wgb2xjv5m2rs9sjb52";
1601 };
1646 };
1602 meta = {
1647 meta = {
1603 license = [ pkgs.lib.licenses.mit ];
1648 license = [ pkgs.lib.licenses.mit ];
1604 };
1649 };
1605 };
1650 };
1606 "repoze.lru" = super.buildPythonPackage {
1651 "repoze.lru" = super.buildPythonPackage {
1607 name = "repoze.lru-0.7";
1652 name = "repoze.lru-0.7";
1608 doCheck = false;
1653 doCheck = false;
1609 src = fetchurl {
1654 src = fetchurl {
1610 url = "https://files.pythonhosted.org/packages/12/bc/595a77c4b5e204847fdf19268314ef59c85193a9dc9f83630fc459c0fee5/repoze.lru-0.7.tar.gz";
1655 url = "https://files.pythonhosted.org/packages/12/bc/595a77c4b5e204847fdf19268314ef59c85193a9dc9f83630fc459c0fee5/repoze.lru-0.7.tar.gz";
1611 sha256 = "0xzz1aw2smy8hdszrq8yhnklx6w1r1mf55061kalw3iq35gafa84";
1656 sha256 = "0xzz1aw2smy8hdszrq8yhnklx6w1r1mf55061kalw3iq35gafa84";
1612 };
1657 };
1613 meta = {
1658 meta = {
1614 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1659 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1615 };
1660 };
1616 };
1661 };
1617 "repoze.sendmail" = super.buildPythonPackage {
1662 "repoze.sendmail" = super.buildPythonPackage {
1618 name = "repoze.sendmail-4.4.1";
1663 name = "repoze.sendmail-4.4.1";
1619 doCheck = false;
1664 doCheck = false;
1620 propagatedBuildInputs = [
1665 propagatedBuildInputs = [
1621 self."setuptools"
1666 self."setuptools"
1622 self."zope.interface"
1667 self."zope.interface"
1623 self."transaction"
1668 self."transaction"
1624 ];
1669 ];
1625 src = fetchurl {
1670 src = fetchurl {
1626 url = "https://files.pythonhosted.org/packages/12/4e/8ef1fd5c42765d712427b9c391419a77bd48877886d2cbc5e9f23c8cad9b/repoze.sendmail-4.4.1.tar.gz";
1671 url = "https://files.pythonhosted.org/packages/12/4e/8ef1fd5c42765d712427b9c391419a77bd48877886d2cbc5e9f23c8cad9b/repoze.sendmail-4.4.1.tar.gz";
1627 sha256 = "096ln02jr2afk7ab9j2czxqv2ryqq7m86ah572nqplx52iws73ks";
1672 sha256 = "096ln02jr2afk7ab9j2czxqv2ryqq7m86ah572nqplx52iws73ks";
1628 };
1673 };
1629 meta = {
1674 meta = {
1630 license = [ pkgs.lib.licenses.zpl21 ];
1675 license = [ pkgs.lib.licenses.zpl21 ];
1631 };
1676 };
1632 };
1677 };
1633 "requests" = super.buildPythonPackage {
1678 "requests" = super.buildPythonPackage {
1634 name = "requests-2.9.1";
1679 name = "requests-2.9.1";
1635 doCheck = false;
1680 doCheck = false;
1636 src = fetchurl {
1681 src = fetchurl {
1637 url = "https://files.pythonhosted.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1682 url = "https://files.pythonhosted.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1638 sha256 = "0zsqrzlybf25xscgi7ja4s48y2abf9wvjkn47wh984qgs1fq2xy5";
1683 sha256 = "0zsqrzlybf25xscgi7ja4s48y2abf9wvjkn47wh984qgs1fq2xy5";
1639 };
1684 };
1640 meta = {
1685 meta = {
1641 license = [ pkgs.lib.licenses.asl20 ];
1686 license = [ pkgs.lib.licenses.asl20 ];
1642 };
1687 };
1643 };
1688 };
1644 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1689 "rhodecode-enterprise-ce" = super.buildPythonPackage {
1645 name = "rhodecode-enterprise-ce-4.15.0";
1690 name = "rhodecode-enterprise-ce-4.16.0";
1646 buildInputs = [
1691 buildInputs = [
1647 self."pytest"
1692 self."pytest"
1648 self."py"
1693 self."py"
1649 self."pytest-cov"
1694 self."pytest-cov"
1650 self."pytest-sugar"
1695 self."pytest-sugar"
1651 self."pytest-runner"
1696 self."pytest-runner"
1652 self."pytest-profiling"
1697 self."pytest-profiling"
1653 self."pytest-timeout"
1698 self."pytest-timeout"
1654 self."gprof2dot"
1699 self."gprof2dot"
1655 self."mock"
1700 self."mock"
1656 self."cov-core"
1701 self."cov-core"
1657 self."coverage"
1702 self."coverage"
1658 self."webtest"
1703 self."webtest"
1659 self."configobj"
1704 self."configobj"
1660 ];
1705 ];
1661 doCheck = true;
1706 doCheck = true;
1662 propagatedBuildInputs = [
1707 propagatedBuildInputs = [
1663 self."setuptools-scm"
1708 self."setuptools-scm"
1664 self."amqp"
1709 self."amqp"
1665 self."authomatic"
1710 self."authomatic"
1666 self."atomicwrites"
1711 self."atomicwrites"
1667 self."attrs"
1712 self."attrs"
1668 self."babel"
1713 self."babel"
1669 self."beaker"
1714 self."beaker"
1670 self."bleach"
1715 self."bleach"
1671 self."celery"
1716 self."celery"
1672 self."chameleon"
1717 self."chameleon"
1673 self."channelstream"
1718 self."channelstream"
1674 self."click"
1719 self."click"
1675 self."colander"
1720 self."colander"
1676 self."configobj"
1721 self."configobj"
1677 self."cssselect"
1722 self."cssselect"
1678 self."decorator"
1723 self."decorator"
1679 self."deform"
1724 self."deform"
1680 self."docutils"
1725 self."docutils"
1681 self."dogpile.cache"
1726 self."dogpile.cache"
1682 self."dogpile.core"
1727 self."dogpile.core"
1683 self."ecdsa"
1728 self."ecdsa"
1684 self."formencode"
1729 self."formencode"
1685 self."future"
1730 self."future"
1686 self."futures"
1731 self."futures"
1687 self."gnureadline"
1732 self."gnureadline"
1688 self."infrae.cache"
1733 self."infrae.cache"
1689 self."iso8601"
1734 self."iso8601"
1690 self."itsdangerous"
1735 self."itsdangerous"
1691 self."jinja2"
1736 self."jinja2"
1692 self."billiard"
1737 self."billiard"
1693 self."kombu"
1738 self."kombu"
1694 self."lxml"
1739 self."lxml"
1695 self."mako"
1740 self."mako"
1696 self."markdown"
1741 self."markdown"
1697 self."markupsafe"
1742 self."markupsafe"
1698 self."msgpack-python"
1743 self."msgpack-python"
1699 self."pyotp"
1744 self."pyotp"
1700 self."packaging"
1745 self."packaging"
1701 self."paste"
1746 self."paste"
1702 self."pastedeploy"
1747 self."pastedeploy"
1703 self."pastescript"
1748 self."pastescript"
1704 self."pathlib2"
1749 self."pathlib2"
1705 self."peppercorn"
1750 self."peppercorn"
1706 self."psutil"
1751 self."psutil"
1707 self."py-bcrypt"
1752 self."py-bcrypt"
1708 self."pycrypto"
1753 self."pycrypto"
1709 self."pycurl"
1754 self."pycurl"
1710 self."pyflakes"
1755 self."pyflakes"
1711 self."pygments"
1756 self."pygments"
1712 self."pyparsing"
1757 self."pyparsing"
1713 self."pyramid-beaker"
1758 self."pyramid-beaker"
1714 self."pyramid-debugtoolbar"
1759 self."pyramid-debugtoolbar"
1715 self."pyramid-jinja2"
1760 self."pyramid-jinja2"
1716 self."pyramid-mako"
1761 self."pyramid-mako"
1717 self."pyramid"
1762 self."pyramid"
1718 self."pyramid-mailer"
1763 self."pyramid-mailer"
1719 self."python-dateutil"
1764 self."python-dateutil"
1720 self."python-ldap"
1765 self."python-ldap"
1721 self."python-memcached"
1766 self."python-memcached"
1722 self."python-pam"
1767 self."python-pam"
1723 self."python-saml"
1768 self."python-saml"
1724 self."pytz"
1769 self."pytz"
1725 self."tzlocal"
1770 self."tzlocal"
1726 self."pyzmq"
1771 self."pyzmq"
1727 self."py-gfm"
1772 self."py-gfm"
1728 self."redis"
1773 self."redis"
1729 self."repoze.lru"
1774 self."repoze.lru"
1730 self."requests"
1775 self."requests"
1731 self."routes"
1776 self."routes"
1732 self."simplejson"
1777 self."simplejson"
1733 self."six"
1778 self."six"
1734 self."sqlalchemy"
1779 self."sqlalchemy"
1735 self."sshpubkeys"
1780 self."sshpubkeys"
1736 self."subprocess32"
1781 self."subprocess32"
1737 self."supervisor"
1782 self."supervisor"
1738 self."tempita"
1783 self."tempita"
1739 self."translationstring"
1784 self."translationstring"
1740 self."urllib3"
1785 self."urllib3"
1741 self."urlobject"
1786 self."urlobject"
1742 self."venusian"
1787 self."venusian"
1743 self."weberror"
1788 self."weberror"
1744 self."webhelpers2"
1789 self."webhelpers2"
1745 self."webhelpers"
1790 self."webhelpers"
1746 self."webob"
1791 self."webob"
1747 self."whoosh"
1792 self."whoosh"
1748 self."wsgiref"
1793 self."wsgiref"
1749 self."zope.cachedescriptors"
1794 self."zope.cachedescriptors"
1750 self."zope.deprecation"
1795 self."zope.deprecation"
1751 self."zope.event"
1796 self."zope.event"
1752 self."zope.interface"
1797 self."zope.interface"
1753 self."mysql-python"
1798 self."mysql-python"
1754 self."pymysql"
1799 self."pymysql"
1755 self."pysqlite"
1800 self."pysqlite"
1756 self."psycopg2"
1801 self."psycopg2"
1757 self."nbconvert"
1802 self."nbconvert"
1758 self."nbformat"
1803 self."nbformat"
1759 self."jupyter-client"
1804 self."jupyter-client"
1760 self."alembic"
1805 self."alembic"
1761 self."invoke"
1806 self."invoke"
1762 self."bumpversion"
1807 self."bumpversion"
1763 self."gevent"
1808 self."gevent"
1764 self."greenlet"
1809 self."greenlet"
1765 self."gunicorn"
1810 self."gunicorn"
1766 self."waitress"
1811 self."waitress"
1767 self."setproctitle"
1812 self."setproctitle"
1768 self."ipdb"
1813 self."ipdb"
1769 self."ipython"
1814 self."ipython"
1770 self."rhodecode-tools"
1815 self."rhodecode-tools"
1771 self."appenlight-client"
1816 self."appenlight-client"
1772 self."pytest"
1817 self."pytest"
1773 self."py"
1818 self."py"
1774 self."pytest-cov"
1819 self."pytest-cov"
1775 self."pytest-sugar"
1820 self."pytest-sugar"
1776 self."pytest-runner"
1821 self."pytest-runner"
1777 self."pytest-profiling"
1822 self."pytest-profiling"
1778 self."pytest-timeout"
1823 self."pytest-timeout"
1779 self."gprof2dot"
1824 self."gprof2dot"
1780 self."mock"
1825 self."mock"
1781 self."cov-core"
1826 self."cov-core"
1782 self."coverage"
1827 self."coverage"
1783 self."webtest"
1828 self."webtest"
1784 ];
1829 ];
1785 src = ./.;
1830 src = ./.;
1786 meta = {
1831 meta = {
1787 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
1832 license = [ { fullName = "Affero GNU General Public License v3 or later (AGPLv3+)"; } { fullName = "AGPLv3, and Commercial License"; } ];
1788 };
1833 };
1789 };
1834 };
1790 "rhodecode-tools" = super.buildPythonPackage {
1835 "rhodecode-tools" = super.buildPythonPackage {
1791 name = "rhodecode-tools-1.0.1";
1836 name = "rhodecode-tools-1.1.0";
1792 doCheck = false;
1837 doCheck = false;
1793 propagatedBuildInputs = [
1838 propagatedBuildInputs = [
1794 self."click"
1839 self."click"
1795 self."future"
1840 self."future"
1796 self."six"
1841 self."six"
1797 self."mako"
1842 self."mako"
1798 self."markupsafe"
1843 self."markupsafe"
1799 self."requests"
1844 self."requests"
1800 self."elasticsearch"
1801 self."elasticsearch-dsl"
1802 self."urllib3"
1845 self."urllib3"
1803 self."whoosh"
1846 self."whoosh"
1847 self."elasticsearch"
1848 self."elasticsearch-dsl"
1849 self."elasticsearch2"
1850 self."elasticsearch1-dsl"
1804 ];
1851 ];
1805 src = fetchurl {
1852 src = fetchurl {
1806 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v1.0.1.tar.gz?md5=ffb5d6bcb855305b93cfe23ad42e500b";
1853 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v1.1.0.tar.gz?md5=cc320c277cb2add546220290ac9be626";
1807 sha256 = "0nr300s4sg685qs4wgbwlplwriawrwi6jq79z37frcnpyc89gpvm";
1854 sha256 = "1wbnnfrzyp0d4ys55vj5vnfrzfhwlqgdhc8yv8i6kwinizf8hfrn";
1808 };
1855 };
1809 meta = {
1856 meta = {
1810 license = [ { fullName = "Apache 2.0 and Proprietary"; } ];
1857 license = [ { fullName = "Apache 2.0 and Proprietary"; } ];
1811 };
1858 };
1812 };
1859 };
1813 "routes" = super.buildPythonPackage {
1860 "routes" = super.buildPythonPackage {
1814 name = "routes-2.4.1";
1861 name = "routes-2.4.1";
1815 doCheck = false;
1862 doCheck = false;
1816 propagatedBuildInputs = [
1863 propagatedBuildInputs = [
1817 self."six"
1864 self."six"
1818 self."repoze.lru"
1865 self."repoze.lru"
1819 ];
1866 ];
1820 src = fetchurl {
1867 src = fetchurl {
1821 url = "https://files.pythonhosted.org/packages/33/38/ea827837e68d9c7dde4cff7ec122a93c319f0effc08ce92a17095576603f/Routes-2.4.1.tar.gz";
1868 url = "https://files.pythonhosted.org/packages/33/38/ea827837e68d9c7dde4cff7ec122a93c319f0effc08ce92a17095576603f/Routes-2.4.1.tar.gz";
1822 sha256 = "1zamff3m0kc4vyfniyhxpkkcqv1rrgnmh37ykxv34nna1ws47vi6";
1869 sha256 = "1zamff3m0kc4vyfniyhxpkkcqv1rrgnmh37ykxv34nna1ws47vi6";
1823 };
1870 };
1824 meta = {
1871 meta = {
1825 license = [ pkgs.lib.licenses.mit ];
1872 license = [ pkgs.lib.licenses.mit ];
1826 };
1873 };
1827 };
1874 };
1828 "scandir" = super.buildPythonPackage {
1875 "scandir" = super.buildPythonPackage {
1829 name = "scandir-1.9.0";
1876 name = "scandir-1.9.0";
1830 doCheck = false;
1877 doCheck = false;
1831 src = fetchurl {
1878 src = fetchurl {
1832 url = "https://files.pythonhosted.org/packages/16/2a/557af1181e6b4e30254d5a6163b18f5053791ca66e251e77ab08887e8fe3/scandir-1.9.0.tar.gz";
1879 url = "https://files.pythonhosted.org/packages/16/2a/557af1181e6b4e30254d5a6163b18f5053791ca66e251e77ab08887e8fe3/scandir-1.9.0.tar.gz";
1833 sha256 = "0r3hvf1a9jm1rkqgx40gxkmccknkaiqjavs8lccgq9s8khh5x5s4";
1880 sha256 = "0r3hvf1a9jm1rkqgx40gxkmccknkaiqjavs8lccgq9s8khh5x5s4";
1834 };
1881 };
1835 meta = {
1882 meta = {
1836 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
1883 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "New BSD License"; } ];
1837 };
1884 };
1838 };
1885 };
1839 "setproctitle" = super.buildPythonPackage {
1886 "setproctitle" = super.buildPythonPackage {
1840 name = "setproctitle-1.1.10";
1887 name = "setproctitle-1.1.10";
1841 doCheck = false;
1888 doCheck = false;
1842 src = fetchurl {
1889 src = fetchurl {
1843 url = "https://files.pythonhosted.org/packages/5a/0d/dc0d2234aacba6cf1a729964383e3452c52096dc695581248b548786f2b3/setproctitle-1.1.10.tar.gz";
1890 url = "https://files.pythonhosted.org/packages/5a/0d/dc0d2234aacba6cf1a729964383e3452c52096dc695581248b548786f2b3/setproctitle-1.1.10.tar.gz";
1844 sha256 = "163kplw9dcrw0lffq1bvli5yws3rngpnvrxrzdw89pbphjjvg0v2";
1891 sha256 = "163kplw9dcrw0lffq1bvli5yws3rngpnvrxrzdw89pbphjjvg0v2";
1845 };
1892 };
1846 meta = {
1893 meta = {
1847 license = [ pkgs.lib.licenses.bsdOriginal ];
1894 license = [ pkgs.lib.licenses.bsdOriginal ];
1848 };
1895 };
1849 };
1896 };
1850 "setuptools" = super.buildPythonPackage {
1897 "setuptools" = super.buildPythonPackage {
1851 name = "setuptools-40.6.2";
1898 name = "setuptools-40.6.3";
1852 doCheck = false;
1899 doCheck = false;
1853 src = fetchurl {
1900 src = fetchurl {
1854 url = "https://files.pythonhosted.org/packages/b0/d1/8acb42f391cba52e35b131e442e80deffbb8d0676b93261d761b1f0ef8fb/setuptools-40.6.2.zip";
1901 url = "https://files.pythonhosted.org/packages/37/1b/b25507861991beeade31473868463dad0e58b1978c209de27384ae541b0b/setuptools-40.6.3.zip";
1855 sha256 = "0r2c5hapirlzm34h7pl1lgkm6gk7bcrlrdj28qgsvaqg3f74vfw6";
1902 sha256 = "1y085dnk574sxw9aymdng9gijvrsbw86hsv9hqnhv7y4d6nlsirv";
1856 };
1903 };
1857 meta = {
1904 meta = {
1858 license = [ pkgs.lib.licenses.mit ];
1905 license = [ pkgs.lib.licenses.mit ];
1859 };
1906 };
1860 };
1907 };
1861 "setuptools-scm" = super.buildPythonPackage {
1908 "setuptools-scm" = super.buildPythonPackage {
1862 name = "setuptools-scm-2.1.0";
1909 name = "setuptools-scm-2.1.0";
1863 doCheck = false;
1910 doCheck = false;
1864 src = fetchurl {
1911 src = fetchurl {
1865 url = "https://files.pythonhosted.org/packages/e5/62/f9e1ac314464eb5945c97542acb6bf6f3381dfa5d7a658de7730c36f31a1/setuptools_scm-2.1.0.tar.gz";
1912 url = "https://files.pythonhosted.org/packages/e5/62/f9e1ac314464eb5945c97542acb6bf6f3381dfa5d7a658de7730c36f31a1/setuptools_scm-2.1.0.tar.gz";
1866 sha256 = "0yb364cgk15sfw3x8ln4ssh98z1dj6n8iiz4r2rw1cfsxhgi8rx7";
1913 sha256 = "0yb364cgk15sfw3x8ln4ssh98z1dj6n8iiz4r2rw1cfsxhgi8rx7";
1867 };
1914 };
1868 meta = {
1915 meta = {
1869 license = [ pkgs.lib.licenses.mit ];
1916 license = [ pkgs.lib.licenses.mit ];
1870 };
1917 };
1871 };
1918 };
1872 "simplegeneric" = super.buildPythonPackage {
1919 "simplegeneric" = super.buildPythonPackage {
1873 name = "simplegeneric-0.8.1";
1920 name = "simplegeneric-0.8.1";
1874 doCheck = false;
1921 doCheck = false;
1875 src = fetchurl {
1922 src = fetchurl {
1876 url = "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
1923 url = "https://files.pythonhosted.org/packages/3d/57/4d9c9e3ae9a255cd4e1106bb57e24056d3d0709fc01b2e3e345898e49d5b/simplegeneric-0.8.1.zip";
1877 sha256 = "0wwi1c6md4vkbcsfsf8dklf3vr4mcdj4mpxkanwgb6jb1432x5yw";
1924 sha256 = "0wwi1c6md4vkbcsfsf8dklf3vr4mcdj4mpxkanwgb6jb1432x5yw";
1878 };
1925 };
1879 meta = {
1926 meta = {
1880 license = [ pkgs.lib.licenses.zpl21 ];
1927 license = [ pkgs.lib.licenses.zpl21 ];
1881 };
1928 };
1882 };
1929 };
1883 "simplejson" = super.buildPythonPackage {
1930 "simplejson" = super.buildPythonPackage {
1884 name = "simplejson-3.11.1";
1931 name = "simplejson-3.11.1";
1885 doCheck = false;
1932 doCheck = false;
1886 src = fetchurl {
1933 src = fetchurl {
1887 url = "https://files.pythonhosted.org/packages/08/48/c97b668d6da7d7bebe7ea1817a6f76394b0ec959cb04214ca833c34359df/simplejson-3.11.1.tar.gz";
1934 url = "https://files.pythonhosted.org/packages/08/48/c97b668d6da7d7bebe7ea1817a6f76394b0ec959cb04214ca833c34359df/simplejson-3.11.1.tar.gz";
1888 sha256 = "1rr58dppsq73p0qcd9bsw066cdd3v63sqv7j6sqni8frvm4jv8h1";
1935 sha256 = "1rr58dppsq73p0qcd9bsw066cdd3v63sqv7j6sqni8frvm4jv8h1";
1889 };
1936 };
1890 meta = {
1937 meta = {
1891 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
1938 license = [ { fullName = "Academic Free License (AFL)"; } pkgs.lib.licenses.mit ];
1892 };
1939 };
1893 };
1940 };
1894 "six" = super.buildPythonPackage {
1941 "six" = super.buildPythonPackage {
1895 name = "six-1.11.0";
1942 name = "six-1.11.0";
1896 doCheck = false;
1943 doCheck = false;
1897 src = fetchurl {
1944 src = fetchurl {
1898 url = "https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
1945 url = "https://files.pythonhosted.org/packages/16/d8/bc6316cf98419719bd59c91742194c111b6f2e85abac88e496adefaf7afe/six-1.11.0.tar.gz";
1899 sha256 = "1scqzwc51c875z23phj48gircqjgnn3af8zy2izjwmnlxrxsgs3h";
1946 sha256 = "1scqzwc51c875z23phj48gircqjgnn3af8zy2izjwmnlxrxsgs3h";
1900 };
1947 };
1901 meta = {
1948 meta = {
1902 license = [ pkgs.lib.licenses.mit ];
1949 license = [ pkgs.lib.licenses.mit ];
1903 };
1950 };
1904 };
1951 };
1905 "sqlalchemy" = super.buildPythonPackage {
1952 "sqlalchemy" = super.buildPythonPackage {
1906 name = "sqlalchemy-1.1.18";
1953 name = "sqlalchemy-1.1.18";
1907 doCheck = false;
1954 doCheck = false;
1908 src = fetchurl {
1955 src = fetchurl {
1909 url = "https://files.pythonhosted.org/packages/cc/4d/96d93ff77cd67aca7618e402191eee3490d8f5f245d6ab7622d35fe504f4/SQLAlchemy-1.1.18.tar.gz";
1956 url = "https://files.pythonhosted.org/packages/cc/4d/96d93ff77cd67aca7618e402191eee3490d8f5f245d6ab7622d35fe504f4/SQLAlchemy-1.1.18.tar.gz";
1910 sha256 = "1ab4ysip6irajfbxl9wy27kv76miaz8h6759hfx92499z4dcf3lb";
1957 sha256 = "1ab4ysip6irajfbxl9wy27kv76miaz8h6759hfx92499z4dcf3lb";
1911 };
1958 };
1912 meta = {
1959 meta = {
1913 license = [ pkgs.lib.licenses.mit ];
1960 license = [ pkgs.lib.licenses.mit ];
1914 };
1961 };
1915 };
1962 };
1916 "sshpubkeys" = super.buildPythonPackage {
1963 "sshpubkeys" = super.buildPythonPackage {
1917 name = "sshpubkeys-2.2.0";
1964 name = "sshpubkeys-2.2.0";
1918 doCheck = false;
1965 doCheck = false;
1919 propagatedBuildInputs = [
1966 propagatedBuildInputs = [
1920 self."pycrypto"
1967 self."pycrypto"
1921 self."ecdsa"
1968 self."ecdsa"
1922 ];
1969 ];
1923 src = fetchurl {
1970 src = fetchurl {
1924 url = "https://files.pythonhosted.org/packages/27/da/337fabeb3dca6b62039a93ceaa636f25065e0ae92b575b1235342076cf0a/sshpubkeys-2.2.0.tar.gz";
1971 url = "https://files.pythonhosted.org/packages/27/da/337fabeb3dca6b62039a93ceaa636f25065e0ae92b575b1235342076cf0a/sshpubkeys-2.2.0.tar.gz";
1925 sha256 = "0r4kpwzmg96a2x56pllik7dmc3fnqk189v3sfgsi07q2ryrhr6xm";
1972 sha256 = "0r4kpwzmg96a2x56pllik7dmc3fnqk189v3sfgsi07q2ryrhr6xm";
1926 };
1973 };
1927 meta = {
1974 meta = {
1928 license = [ pkgs.lib.licenses.bsdOriginal ];
1975 license = [ pkgs.lib.licenses.bsdOriginal ];
1929 };
1976 };
1930 };
1977 };
1931 "subprocess32" = super.buildPythonPackage {
1978 "subprocess32" = super.buildPythonPackage {
1932 name = "subprocess32-3.5.2";
1979 name = "subprocess32-3.5.2";
1933 doCheck = false;
1980 doCheck = false;
1934 src = fetchurl {
1981 src = fetchurl {
1935 url = "https://files.pythonhosted.org/packages/c3/5f/7117737fc7114061837a4f51670d863dd7f7f9c762a6546fa8a0dcfe61c8/subprocess32-3.5.2.tar.gz";
1982 url = "https://files.pythonhosted.org/packages/c3/5f/7117737fc7114061837a4f51670d863dd7f7f9c762a6546fa8a0dcfe61c8/subprocess32-3.5.2.tar.gz";
1936 sha256 = "11v62shwmdys48g7ncs3a8jwwnkcl8d4zcwy6dk73z1zy2f9hazb";
1983 sha256 = "11v62shwmdys48g7ncs3a8jwwnkcl8d4zcwy6dk73z1zy2f9hazb";
1937 };
1984 };
1938 meta = {
1985 meta = {
1939 license = [ pkgs.lib.licenses.psfl ];
1986 license = [ pkgs.lib.licenses.psfl ];
1940 };
1987 };
1941 };
1988 };
1942 "supervisor" = super.buildPythonPackage {
1989 "supervisor" = super.buildPythonPackage {
1943 name = "supervisor-3.3.4";
1990 name = "supervisor-3.3.4";
1944 doCheck = false;
1991 doCheck = false;
1945 propagatedBuildInputs = [
1992 propagatedBuildInputs = [
1946 self."meld3"
1993 self."meld3"
1947 ];
1994 ];
1948 src = fetchurl {
1995 src = fetchurl {
1949 url = "https://files.pythonhosted.org/packages/44/60/698e54b4a4a9b956b2d709b4b7b676119c833d811d53ee2500f1b5e96dc3/supervisor-3.3.4.tar.gz";
1996 url = "https://files.pythonhosted.org/packages/44/60/698e54b4a4a9b956b2d709b4b7b676119c833d811d53ee2500f1b5e96dc3/supervisor-3.3.4.tar.gz";
1950 sha256 = "0wp62z9xprvz2krg02xnbwcnq6pxfq3byd8cxx8c2d8xznih28i1";
1997 sha256 = "0wp62z9xprvz2krg02xnbwcnq6pxfq3byd8cxx8c2d8xznih28i1";
1951 };
1998 };
1952 meta = {
1999 meta = {
1953 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2000 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1954 };
2001 };
1955 };
2002 };
1956 "tempita" = super.buildPythonPackage {
2003 "tempita" = super.buildPythonPackage {
1957 name = "tempita-0.5.2";
2004 name = "tempita-0.5.2";
1958 doCheck = false;
2005 doCheck = false;
1959 src = fetchurl {
2006 src = fetchurl {
1960 url = "https://files.pythonhosted.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
2007 url = "https://files.pythonhosted.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
1961 sha256 = "177wwq45slfyajd8csy477bmdmzipyw0dm7i85k3akb7m85wzkna";
2008 sha256 = "177wwq45slfyajd8csy477bmdmzipyw0dm7i85k3akb7m85wzkna";
1962 };
2009 };
1963 meta = {
2010 meta = {
1964 license = [ pkgs.lib.licenses.mit ];
2011 license = [ pkgs.lib.licenses.mit ];
1965 };
2012 };
1966 };
2013 };
1967 "termcolor" = super.buildPythonPackage {
2014 "termcolor" = super.buildPythonPackage {
1968 name = "termcolor-1.1.0";
2015 name = "termcolor-1.1.0";
1969 doCheck = false;
2016 doCheck = false;
1970 src = fetchurl {
2017 src = fetchurl {
1971 url = "https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
2018 url = "https://files.pythonhosted.org/packages/8a/48/a76be51647d0eb9f10e2a4511bf3ffb8cc1e6b14e9e4fab46173aa79f981/termcolor-1.1.0.tar.gz";
1972 sha256 = "0fv1vq14rpqwgazxg4981904lfyp84mnammw7y046491cv76jv8x";
2019 sha256 = "0fv1vq14rpqwgazxg4981904lfyp84mnammw7y046491cv76jv8x";
1973 };
2020 };
1974 meta = {
2021 meta = {
1975 license = [ pkgs.lib.licenses.mit ];
2022 license = [ pkgs.lib.licenses.mit ];
1976 };
2023 };
1977 };
2024 };
1978 "testpath" = super.buildPythonPackage {
2025 "testpath" = super.buildPythonPackage {
1979 name = "testpath-0.4.2";
2026 name = "testpath-0.4.2";
1980 doCheck = false;
2027 doCheck = false;
1981 src = fetchurl {
2028 src = fetchurl {
1982 url = "https://files.pythonhosted.org/packages/06/30/9a7e917066d851d8b4117e85794b5f14516419ea714a8a2681ec6aa8a981/testpath-0.4.2.tar.gz";
2029 url = "https://files.pythonhosted.org/packages/06/30/9a7e917066d851d8b4117e85794b5f14516419ea714a8a2681ec6aa8a981/testpath-0.4.2.tar.gz";
1983 sha256 = "1y40hywscnnyb734pnzm55nd8r8kp1072bjxbil83gcd53cv755n";
2030 sha256 = "1y40hywscnnyb734pnzm55nd8r8kp1072bjxbil83gcd53cv755n";
1984 };
2031 };
1985 meta = {
2032 meta = {
1986 license = [ ];
2033 license = [ ];
1987 };
2034 };
1988 };
2035 };
1989 "traitlets" = super.buildPythonPackage {
2036 "traitlets" = super.buildPythonPackage {
1990 name = "traitlets-4.3.2";
2037 name = "traitlets-4.3.2";
1991 doCheck = false;
2038 doCheck = false;
1992 propagatedBuildInputs = [
2039 propagatedBuildInputs = [
1993 self."ipython-genutils"
2040 self."ipython-genutils"
1994 self."six"
2041 self."six"
1995 self."decorator"
2042 self."decorator"
1996 self."enum34"
2043 self."enum34"
1997 ];
2044 ];
1998 src = fetchurl {
2045 src = fetchurl {
1999 url = "https://files.pythonhosted.org/packages/a5/98/7f5ef2fe9e9e071813aaf9cb91d1a732e0a68b6c44a32b38cb8e14c3f069/traitlets-4.3.2.tar.gz";
2046 url = "https://files.pythonhosted.org/packages/a5/98/7f5ef2fe9e9e071813aaf9cb91d1a732e0a68b6c44a32b38cb8e14c3f069/traitlets-4.3.2.tar.gz";
2000 sha256 = "0dbq7sx26xqz5ixs711k5nc88p8a0nqyz6162pwks5dpcz9d4jww";
2047 sha256 = "0dbq7sx26xqz5ixs711k5nc88p8a0nqyz6162pwks5dpcz9d4jww";
2001 };
2048 };
2002 meta = {
2049 meta = {
2003 license = [ pkgs.lib.licenses.bsdOriginal ];
2050 license = [ pkgs.lib.licenses.bsdOriginal ];
2004 };
2051 };
2005 };
2052 };
2006 "transaction" = super.buildPythonPackage {
2053 "transaction" = super.buildPythonPackage {
2007 name = "transaction-2.4.0";
2054 name = "transaction-2.4.0";
2008 doCheck = false;
2055 doCheck = false;
2009 propagatedBuildInputs = [
2056 propagatedBuildInputs = [
2010 self."zope.interface"
2057 self."zope.interface"
2011 ];
2058 ];
2012 src = fetchurl {
2059 src = fetchurl {
2013 url = "https://files.pythonhosted.org/packages/9d/7d/0e8af0d059e052b9dcf2bb5a08aad20ae3e238746bdd3f8701a60969b363/transaction-2.4.0.tar.gz";
2060 url = "https://files.pythonhosted.org/packages/9d/7d/0e8af0d059e052b9dcf2bb5a08aad20ae3e238746bdd3f8701a60969b363/transaction-2.4.0.tar.gz";
2014 sha256 = "17wz1y524ca07vr03yddy8dv0gbscs06dbdywmllxv5rc725jq3j";
2061 sha256 = "17wz1y524ca07vr03yddy8dv0gbscs06dbdywmllxv5rc725jq3j";
2015 };
2062 };
2016 meta = {
2063 meta = {
2017 license = [ pkgs.lib.licenses.zpl21 ];
2064 license = [ pkgs.lib.licenses.zpl21 ];
2018 };
2065 };
2019 };
2066 };
2020 "translationstring" = super.buildPythonPackage {
2067 "translationstring" = super.buildPythonPackage {
2021 name = "translationstring-1.3";
2068 name = "translationstring-1.3";
2022 doCheck = false;
2069 doCheck = false;
2023 src = fetchurl {
2070 src = fetchurl {
2024 url = "https://files.pythonhosted.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
2071 url = "https://files.pythonhosted.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
2025 sha256 = "0bdpcnd9pv0131dl08h4zbcwmgc45lyvq3pa224xwan5b3x4rr2f";
2072 sha256 = "0bdpcnd9pv0131dl08h4zbcwmgc45lyvq3pa224xwan5b3x4rr2f";
2026 };
2073 };
2027 meta = {
2074 meta = {
2028 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
2075 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
2029 };
2076 };
2030 };
2077 };
2031 "tzlocal" = super.buildPythonPackage {
2078 "tzlocal" = super.buildPythonPackage {
2032 name = "tzlocal-1.5.1";
2079 name = "tzlocal-1.5.1";
2033 doCheck = false;
2080 doCheck = false;
2034 propagatedBuildInputs = [
2081 propagatedBuildInputs = [
2035 self."pytz"
2082 self."pytz"
2036 ];
2083 ];
2037 src = fetchurl {
2084 src = fetchurl {
2038 url = "https://files.pythonhosted.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5.1.tar.gz";
2085 url = "https://files.pythonhosted.org/packages/cb/89/e3687d3ed99bc882793f82634e9824e62499fdfdc4b1ae39e211c5b05017/tzlocal-1.5.1.tar.gz";
2039 sha256 = "0kiciwiqx0bv0fbc913idxibc4ygg4cb7f8rcpd9ij2shi4bigjf";
2086 sha256 = "0kiciwiqx0bv0fbc913idxibc4ygg4cb7f8rcpd9ij2shi4bigjf";
2040 };
2087 };
2041 meta = {
2088 meta = {
2042 license = [ pkgs.lib.licenses.mit ];
2089 license = [ pkgs.lib.licenses.mit ];
2043 };
2090 };
2044 };
2091 };
2045 "urllib3" = super.buildPythonPackage {
2092 "urllib3" = super.buildPythonPackage {
2046 name = "urllib3-1.21";
2093 name = "urllib3-1.24.1";
2047 doCheck = false;
2094 doCheck = false;
2048 src = fetchurl {
2095 src = fetchurl {
2049 url = "https://files.pythonhosted.org/packages/34/95/7b28259d0006ed681c424cd71a668363265eac92b67dddd018eb9a22bff8/urllib3-1.21.tar.gz";
2096 url = "https://files.pythonhosted.org/packages/b1/53/37d82ab391393565f2f831b8eedbffd57db5a718216f82f1a8b4d381a1c1/urllib3-1.24.1.tar.gz";
2050 sha256 = "0irnj4wvh2y36s4q3l2vas9qr9m766w6w418nb490j3mf8a8zw6h";
2097 sha256 = "08lwd9f3hqznyf32vnzwvp87pchx062nkbgyrf67rwlkgj0jk5fy";
2051 };
2098 };
2052 meta = {
2099 meta = {
2053 license = [ pkgs.lib.licenses.mit ];
2100 license = [ pkgs.lib.licenses.mit ];
2054 };
2101 };
2055 };
2102 };
2056 "urlobject" = super.buildPythonPackage {
2103 "urlobject" = super.buildPythonPackage {
2057 name = "urlobject-2.4.3";
2104 name = "urlobject-2.4.3";
2058 doCheck = false;
2105 doCheck = false;
2059 src = fetchurl {
2106 src = fetchurl {
2060 url = "https://files.pythonhosted.org/packages/e2/b8/1d0a916f4b34c4618846e6da0e4eeaa8fcb4a2f39e006434fe38acb74b34/URLObject-2.4.3.tar.gz";
2107 url = "https://files.pythonhosted.org/packages/e2/b8/1d0a916f4b34c4618846e6da0e4eeaa8fcb4a2f39e006434fe38acb74b34/URLObject-2.4.3.tar.gz";
2061 sha256 = "1ahc8ficzfvr2avln71immfh4ls0zyv6cdaa5xmkdj5rd87f5cj7";
2108 sha256 = "1ahc8ficzfvr2avln71immfh4ls0zyv6cdaa5xmkdj5rd87f5cj7";
2062 };
2109 };
2063 meta = {
2110 meta = {
2064 license = [ pkgs.lib.licenses.publicDomain ];
2111 license = [ pkgs.lib.licenses.publicDomain ];
2065 };
2112 };
2066 };
2113 };
2067 "venusian" = super.buildPythonPackage {
2114 "venusian" = super.buildPythonPackage {
2068 name = "venusian-1.1.0";
2115 name = "venusian-1.1.0";
2069 doCheck = false;
2116 doCheck = false;
2070 src = fetchurl {
2117 src = fetchurl {
2071 url = "https://files.pythonhosted.org/packages/38/24/b4b470ab9e0a2e2e9b9030c7735828c8934b4c6b45befd1bb713ec2aeb2d/venusian-1.1.0.tar.gz";
2118 url = "https://files.pythonhosted.org/packages/38/24/b4b470ab9e0a2e2e9b9030c7735828c8934b4c6b45befd1bb713ec2aeb2d/venusian-1.1.0.tar.gz";
2072 sha256 = "0zapz131686qm0gazwy8bh11vr57pr89jbwbl50s528sqy9f80lr";
2119 sha256 = "0zapz131686qm0gazwy8bh11vr57pr89jbwbl50s528sqy9f80lr";
2073 };
2120 };
2074 meta = {
2121 meta = {
2075 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2122 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
2076 };
2123 };
2077 };
2124 };
2078 "vine" = super.buildPythonPackage {
2125 "vine" = super.buildPythonPackage {
2079 name = "vine-1.1.4";
2126 name = "vine-1.1.4";
2080 doCheck = false;
2127 doCheck = false;
2081 src = fetchurl {
2128 src = fetchurl {
2082 url = "https://files.pythonhosted.org/packages/32/23/36284986e011f3c130d802c3c66abd8f1aef371eae110ddf80c5ae22e1ff/vine-1.1.4.tar.gz";
2129 url = "https://files.pythonhosted.org/packages/32/23/36284986e011f3c130d802c3c66abd8f1aef371eae110ddf80c5ae22e1ff/vine-1.1.4.tar.gz";
2083 sha256 = "0wkskb2hb494v9gixqnf4bl972p4ibcmxdykzpwjlfa5picns4aj";
2130 sha256 = "0wkskb2hb494v9gixqnf4bl972p4ibcmxdykzpwjlfa5picns4aj";
2084 };
2131 };
2085 meta = {
2132 meta = {
2086 license = [ pkgs.lib.licenses.bsdOriginal ];
2133 license = [ pkgs.lib.licenses.bsdOriginal ];
2087 };
2134 };
2088 };
2135 };
2089 "waitress" = super.buildPythonPackage {
2136 "waitress" = super.buildPythonPackage {
2090 name = "waitress-1.1.0";
2137 name = "waitress-1.1.0";
2091 doCheck = false;
2138 doCheck = false;
2092 src = fetchurl {
2139 src = fetchurl {
2093 url = "https://files.pythonhosted.org/packages/3c/68/1c10dd5c556872ceebe88483b0436140048d39de83a84a06a8baa8136f4f/waitress-1.1.0.tar.gz";
2140 url = "https://files.pythonhosted.org/packages/3c/68/1c10dd5c556872ceebe88483b0436140048d39de83a84a06a8baa8136f4f/waitress-1.1.0.tar.gz";
2094 sha256 = "1a85gyji0kajc3p0s1pwwfm06w4wfxjkvvl4rnrz3h164kbd6g6k";
2141 sha256 = "1a85gyji0kajc3p0s1pwwfm06w4wfxjkvvl4rnrz3h164kbd6g6k";
2095 };
2142 };
2096 meta = {
2143 meta = {
2097 license = [ pkgs.lib.licenses.zpl21 ];
2144 license = [ pkgs.lib.licenses.zpl21 ];
2098 };
2145 };
2099 };
2146 };
2100 "wcwidth" = super.buildPythonPackage {
2147 "wcwidth" = super.buildPythonPackage {
2101 name = "wcwidth-0.1.7";
2148 name = "wcwidth-0.1.7";
2102 doCheck = false;
2149 doCheck = false;
2103 src = fetchurl {
2150 src = fetchurl {
2104 url = "https://files.pythonhosted.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz";
2151 url = "https://files.pythonhosted.org/packages/55/11/e4a2bb08bb450fdbd42cc709dd40de4ed2c472cf0ccb9e64af22279c5495/wcwidth-0.1.7.tar.gz";
2105 sha256 = "0pn6dflzm609m4r3i8ik5ni9ijjbb5fa3vg1n7hn6vkd49r77wrx";
2152 sha256 = "0pn6dflzm609m4r3i8ik5ni9ijjbb5fa3vg1n7hn6vkd49r77wrx";
2106 };
2153 };
2107 meta = {
2154 meta = {
2108 license = [ pkgs.lib.licenses.mit ];
2155 license = [ pkgs.lib.licenses.mit ];
2109 };
2156 };
2110 };
2157 };
2111 "webencodings" = super.buildPythonPackage {
2158 "webencodings" = super.buildPythonPackage {
2112 name = "webencodings-0.5.1";
2159 name = "webencodings-0.5.1";
2113 doCheck = false;
2160 doCheck = false;
2114 src = fetchurl {
2161 src = fetchurl {
2115 url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz";
2162 url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz";
2116 sha256 = "08qrgrc4hrximb2gqnl69g01s93rhf2842jfxdjljc1dbwj1qsmk";
2163 sha256 = "08qrgrc4hrximb2gqnl69g01s93rhf2842jfxdjljc1dbwj1qsmk";
2117 };
2164 };
2118 meta = {
2165 meta = {
2119 license = [ pkgs.lib.licenses.bsdOriginal ];
2166 license = [ pkgs.lib.licenses.bsdOriginal ];
2120 };
2167 };
2121 };
2168 };
2122 "weberror" = super.buildPythonPackage {
2169 "weberror" = super.buildPythonPackage {
2123 name = "weberror-0.10.3";
2170 name = "weberror-0.10.3";
2124 doCheck = false;
2171 doCheck = false;
2125 propagatedBuildInputs = [
2172 propagatedBuildInputs = [
2126 self."webob"
2173 self."webob"
2127 self."tempita"
2174 self."tempita"
2128 self."pygments"
2175 self."pygments"
2129 self."paste"
2176 self."paste"
2130 ];
2177 ];
2131 src = fetchurl {
2178 src = fetchurl {
2132 url = "https://files.pythonhosted.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
2179 url = "https://files.pythonhosted.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
2133 sha256 = "0frg4kvycqpj5bi8asfqfs6bxsr2cvjvb6b56c4d1ai1z57kbjx6";
2180 sha256 = "0frg4kvycqpj5bi8asfqfs6bxsr2cvjvb6b56c4d1ai1z57kbjx6";
2134 };
2181 };
2135 meta = {
2182 meta = {
2136 license = [ pkgs.lib.licenses.mit ];
2183 license = [ pkgs.lib.licenses.mit ];
2137 };
2184 };
2138 };
2185 };
2139 "webhelpers" = super.buildPythonPackage {
2186 "webhelpers" = super.buildPythonPackage {
2140 name = "webhelpers-1.3";
2187 name = "webhelpers-1.3";
2141 doCheck = false;
2188 doCheck = false;
2142 propagatedBuildInputs = [
2189 propagatedBuildInputs = [
2143 self."markupsafe"
2190 self."markupsafe"
2144 ];
2191 ];
2145 src = fetchurl {
2192 src = fetchurl {
2146 url = "https://files.pythonhosted.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
2193 url = "https://files.pythonhosted.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
2147 sha256 = "10x5i82qdkrvyw18gsybwggfhfpl869siaab89vnndi9x62g51pa";
2194 sha256 = "10x5i82qdkrvyw18gsybwggfhfpl869siaab89vnndi9x62g51pa";
2148 };
2195 };
2149 meta = {
2196 meta = {
2150 license = [ pkgs.lib.licenses.bsdOriginal ];
2197 license = [ pkgs.lib.licenses.bsdOriginal ];
2151 };
2198 };
2152 };
2199 };
2153 "webhelpers2" = super.buildPythonPackage {
2200 "webhelpers2" = super.buildPythonPackage {
2154 name = "webhelpers2-2.0";
2201 name = "webhelpers2-2.0";
2155 doCheck = false;
2202 doCheck = false;
2156 propagatedBuildInputs = [
2203 propagatedBuildInputs = [
2157 self."markupsafe"
2204 self."markupsafe"
2158 self."six"
2205 self."six"
2159 ];
2206 ];
2160 src = fetchurl {
2207 src = fetchurl {
2161 url = "https://files.pythonhosted.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
2208 url = "https://files.pythonhosted.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
2162 sha256 = "0aphva1qmxh83n01p53f5fd43m4srzbnfbz5ajvbx9aj2aipwmcs";
2209 sha256 = "0aphva1qmxh83n01p53f5fd43m4srzbnfbz5ajvbx9aj2aipwmcs";
2163 };
2210 };
2164 meta = {
2211 meta = {
2165 license = [ pkgs.lib.licenses.mit ];
2212 license = [ pkgs.lib.licenses.mit ];
2166 };
2213 };
2167 };
2214 };
2168 "webob" = super.buildPythonPackage {
2215 "webob" = super.buildPythonPackage {
2169 name = "webob-1.8.4";
2216 name = "webob-1.8.4";
2170 doCheck = false;
2217 doCheck = false;
2171 src = fetchurl {
2218 src = fetchurl {
2172 url = "https://files.pythonhosted.org/packages/e4/6c/99e322c3d4cc11d9060a67a9bf2f7c9c581f40988c11fffe89bb8c36bc5e/WebOb-1.8.4.tar.gz";
2219 url = "https://files.pythonhosted.org/packages/e4/6c/99e322c3d4cc11d9060a67a9bf2f7c9c581f40988c11fffe89bb8c36bc5e/WebOb-1.8.4.tar.gz";
2173 sha256 = "16cfg5y4n6sihz59vsmns2yqbfm0gfsn3l5xgz2g0pdhilaib0x4";
2220 sha256 = "16cfg5y4n6sihz59vsmns2yqbfm0gfsn3l5xgz2g0pdhilaib0x4";
2174 };
2221 };
2175 meta = {
2222 meta = {
2176 license = [ pkgs.lib.licenses.mit ];
2223 license = [ pkgs.lib.licenses.mit ];
2177 };
2224 };
2178 };
2225 };
2179 "webtest" = super.buildPythonPackage {
2226 "webtest" = super.buildPythonPackage {
2180 name = "webtest-2.0.32";
2227 name = "webtest-2.0.32";
2181 doCheck = false;
2228 doCheck = false;
2182 propagatedBuildInputs = [
2229 propagatedBuildInputs = [
2183 self."six"
2230 self."six"
2184 self."webob"
2231 self."webob"
2185 self."waitress"
2232 self."waitress"
2186 self."beautifulsoup4"
2233 self."beautifulsoup4"
2187 ];
2234 ];
2188 src = fetchurl {
2235 src = fetchurl {
2189 url = "https://files.pythonhosted.org/packages/27/9f/9e74449d272ffbef4fb3012e6dbc53c0b24822d545e7a33a342f80131e59/WebTest-2.0.32.tar.gz";
2236 url = "https://files.pythonhosted.org/packages/27/9f/9e74449d272ffbef4fb3012e6dbc53c0b24822d545e7a33a342f80131e59/WebTest-2.0.32.tar.gz";
2190 sha256 = "0qp0nnbazzm4ibjiyqfcn6f230svk09i4g58zg2i9x1ga06h48a2";
2237 sha256 = "0qp0nnbazzm4ibjiyqfcn6f230svk09i4g58zg2i9x1ga06h48a2";
2191 };
2238 };
2192 meta = {
2239 meta = {
2193 license = [ pkgs.lib.licenses.mit ];
2240 license = [ pkgs.lib.licenses.mit ];
2194 };
2241 };
2195 };
2242 };
2196 "whoosh" = super.buildPythonPackage {
2243 "whoosh" = super.buildPythonPackage {
2197 name = "whoosh-2.7.4";
2244 name = "whoosh-2.7.4";
2198 doCheck = false;
2245 doCheck = false;
2199 src = fetchurl {
2246 src = fetchurl {
2200 url = "https://files.pythonhosted.org/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz";
2247 url = "https://files.pythonhosted.org/packages/25/2b/6beed2107b148edc1321da0d489afc4617b9ed317ef7b72d4993cad9b684/Whoosh-2.7.4.tar.gz";
2201 sha256 = "10qsqdjpbc85fykc1vgcs8xwbgn4l2l52c8d83xf1q59pwyn79bw";
2248 sha256 = "10qsqdjpbc85fykc1vgcs8xwbgn4l2l52c8d83xf1q59pwyn79bw";
2202 };
2249 };
2203 meta = {
2250 meta = {
2204 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
2251 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
2205 };
2252 };
2206 };
2253 };
2207 "ws4py" = super.buildPythonPackage {
2254 "ws4py" = super.buildPythonPackage {
2208 name = "ws4py-0.5.1";
2255 name = "ws4py-0.5.1";
2209 doCheck = false;
2256 doCheck = false;
2210 src = fetchurl {
2257 src = fetchurl {
2211 url = "https://files.pythonhosted.org/packages/53/20/4019a739b2eefe9282d3822ef6a225250af964b117356971bd55e274193c/ws4py-0.5.1.tar.gz";
2258 url = "https://files.pythonhosted.org/packages/53/20/4019a739b2eefe9282d3822ef6a225250af964b117356971bd55e274193c/ws4py-0.5.1.tar.gz";
2212 sha256 = "10slbbf2jm4hpr92jx7kh7mhf48sjl01v2w4d8z3f1p0ybbp7l19";
2259 sha256 = "10slbbf2jm4hpr92jx7kh7mhf48sjl01v2w4d8z3f1p0ybbp7l19";
2213 };
2260 };
2214 meta = {
2261 meta = {
2215 license = [ pkgs.lib.licenses.bsdOriginal ];
2262 license = [ pkgs.lib.licenses.bsdOriginal ];
2216 };
2263 };
2217 };
2264 };
2218 "wsgiref" = super.buildPythonPackage {
2265 "wsgiref" = super.buildPythonPackage {
2219 name = "wsgiref-0.1.2";
2266 name = "wsgiref-0.1.2";
2220 doCheck = false;
2267 doCheck = false;
2221 src = fetchurl {
2268 src = fetchurl {
2222 url = "https://files.pythonhosted.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
2269 url = "https://files.pythonhosted.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
2223 sha256 = "0y8fyjmpq7vwwm4x732w97qbkw78rjwal5409k04cw4m03411rn7";
2270 sha256 = "0y8fyjmpq7vwwm4x732w97qbkw78rjwal5409k04cw4m03411rn7";
2224 };
2271 };
2225 meta = {
2272 meta = {
2226 license = [ { fullName = "PSF or ZPL"; } ];
2273 license = [ { fullName = "PSF or ZPL"; } ];
2227 };
2274 };
2228 };
2275 };
2229 "zope.cachedescriptors" = super.buildPythonPackage {
2276 "zope.cachedescriptors" = super.buildPythonPackage {
2230 name = "zope.cachedescriptors-4.3.1";
2277 name = "zope.cachedescriptors-4.3.1";
2231 doCheck = false;
2278 doCheck = false;
2232 propagatedBuildInputs = [
2279 propagatedBuildInputs = [
2233 self."setuptools"
2280 self."setuptools"
2234 ];
2281 ];
2235 src = fetchurl {
2282 src = fetchurl {
2236 url = "https://files.pythonhosted.org/packages/2f/89/ebe1890cc6d3291ebc935558fa764d5fffe571018dbbee200e9db78762cb/zope.cachedescriptors-4.3.1.tar.gz";
2283 url = "https://files.pythonhosted.org/packages/2f/89/ebe1890cc6d3291ebc935558fa764d5fffe571018dbbee200e9db78762cb/zope.cachedescriptors-4.3.1.tar.gz";
2237 sha256 = "0jhr3m5p74c6r7k8iv0005b8bfsialih9d7zl5vx38rf5xq1lk8z";
2284 sha256 = "0jhr3m5p74c6r7k8iv0005b8bfsialih9d7zl5vx38rf5xq1lk8z";
2238 };
2285 };
2239 meta = {
2286 meta = {
2240 license = [ pkgs.lib.licenses.zpl21 ];
2287 license = [ pkgs.lib.licenses.zpl21 ];
2241 };
2288 };
2242 };
2289 };
2243 "zope.deprecation" = super.buildPythonPackage {
2290 "zope.deprecation" = super.buildPythonPackage {
2244 name = "zope.deprecation-4.3.0";
2291 name = "zope.deprecation-4.3.0";
2245 doCheck = false;
2292 doCheck = false;
2246 propagatedBuildInputs = [
2293 propagatedBuildInputs = [
2247 self."setuptools"
2294 self."setuptools"
2248 ];
2295 ];
2249 src = fetchurl {
2296 src = fetchurl {
2250 url = "https://files.pythonhosted.org/packages/a1/18/2dc5e6bfe64fdc3b79411b67464c55bb0b43b127051a20f7f492ab767758/zope.deprecation-4.3.0.tar.gz";
2297 url = "https://files.pythonhosted.org/packages/a1/18/2dc5e6bfe64fdc3b79411b67464c55bb0b43b127051a20f7f492ab767758/zope.deprecation-4.3.0.tar.gz";
2251 sha256 = "095jas41wbxgmw95kwdxqhbc3bgihw2hzj9b3qpdg85apcsf2lkx";
2298 sha256 = "095jas41wbxgmw95kwdxqhbc3bgihw2hzj9b3qpdg85apcsf2lkx";
2252 };
2299 };
2253 meta = {
2300 meta = {
2254 license = [ pkgs.lib.licenses.zpl21 ];
2301 license = [ pkgs.lib.licenses.zpl21 ];
2255 };
2302 };
2256 };
2303 };
2257 "zope.event" = super.buildPythonPackage {
2304 "zope.event" = super.buildPythonPackage {
2258 name = "zope.event-4.3.0";
2305 name = "zope.event-4.3.0";
2259 doCheck = false;
2306 doCheck = false;
2260 propagatedBuildInputs = [
2307 propagatedBuildInputs = [
2261 self."setuptools"
2308 self."setuptools"
2262 ];
2309 ];
2263 src = fetchurl {
2310 src = fetchurl {
2264 url = "https://files.pythonhosted.org/packages/9e/d0/54ba59f19a0635f6591b74be259cf6fbf67e73f4edda27b5cd0cf4d26efa/zope.event-4.3.0.tar.gz";
2311 url = "https://files.pythonhosted.org/packages/9e/d0/54ba59f19a0635f6591b74be259cf6fbf67e73f4edda27b5cd0cf4d26efa/zope.event-4.3.0.tar.gz";
2265 sha256 = "1rrkyx42bcq8dkpj23c2v99kczlrg8d39c06q5qpr0vs4hjfmv70";
2312 sha256 = "1rrkyx42bcq8dkpj23c2v99kczlrg8d39c06q5qpr0vs4hjfmv70";
2266 };
2313 };
2267 meta = {
2314 meta = {
2268 license = [ pkgs.lib.licenses.zpl21 ];
2315 license = [ pkgs.lib.licenses.zpl21 ];
2269 };
2316 };
2270 };
2317 };
2271 "zope.interface" = super.buildPythonPackage {
2318 "zope.interface" = super.buildPythonPackage {
2272 name = "zope.interface-4.5.0";
2319 name = "zope.interface-4.5.0";
2273 doCheck = false;
2320 doCheck = false;
2274 propagatedBuildInputs = [
2321 propagatedBuildInputs = [
2275 self."setuptools"
2322 self."setuptools"
2276 ];
2323 ];
2277 src = fetchurl {
2324 src = fetchurl {
2278 url = "https://files.pythonhosted.org/packages/ac/8a/657532df378c2cd2a1fe6b12be3b4097521570769d4852ec02c24bd3594e/zope.interface-4.5.0.tar.gz";
2325 url = "https://files.pythonhosted.org/packages/ac/8a/657532df378c2cd2a1fe6b12be3b4097521570769d4852ec02c24bd3594e/zope.interface-4.5.0.tar.gz";
2279 sha256 = "0k67m60ij06wkg82n15qgyn96waf4pmrkhv0njpkfzpmv5q89hsp";
2326 sha256 = "0k67m60ij06wkg82n15qgyn96waf4pmrkhv0njpkfzpmv5q89hsp";
2280 };
2327 };
2281 meta = {
2328 meta = {
2282 license = [ pkgs.lib.licenses.zpl21 ];
2329 license = [ pkgs.lib.licenses.zpl21 ];
2283 };
2330 };
2284 };
2331 };
2285
2332
2286 ### Test requirements
2333 ### Test requirements
2287
2334
2288
2335
2289 }
2336 }
@@ -1,135 +1,135 b''
1 ## dependencies
1 ## dependencies
2
2
3 setuptools-scm==2.1.0
3 setuptools-scm==2.1.0
4 amqp==2.3.1
4 amqp==2.3.1
5 # not released authomatic that has updated some oauth providers
5 # not released authomatic that has updated some oauth providers
6 https://code.rhodecode.com/upstream/authomatic/archive/90a9ce60cc405ae8a2bf5c3713acd5d78579a04e.tar.gz?md5=3c68720a1322b25254009518d1ff6801#egg=authomatic==0.1.0.post1
6 https://code.rhodecode.com/upstream/authomatic/archive/90a9ce60cc405ae8a2bf5c3713acd5d78579a04e.tar.gz?md5=3c68720a1322b25254009518d1ff6801#egg=authomatic==0.1.0.post1
7 atomicwrites==1.2.1
7 atomicwrites==1.2.1
8 attrs==18.2.0
8 attrs==18.2.0
9 babel==1.3
9 babel==1.3
10 beaker==1.9.1
10 beaker==1.9.1
11 bleach==3.0.2
11 bleach==3.0.2
12 celery==4.1.1
12 celery==4.1.1
13 chameleon==2.24
13 chameleon==2.24
14 channelstream==0.5.2
14 channelstream==0.5.2
15 click==6.6
15 click==6.6
16 colander==1.5.1
16 colander==1.5.1
17 # our custom configobj
17 # our custom configobj
18 https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c#egg=configobj==5.0.6
18 https://code.rhodecode.com/upstream/configobj/archive/a11ff0a0bd4fbda9e3a91267e720f88329efb4a6.tar.gz?md5=9916c524ea11a6c418217af6b28d4b3c#egg=configobj==5.0.6
19 cssselect==1.0.3
19 cssselect==1.0.3
20 decorator==4.1.2
20 decorator==4.1.2
21 deform==2.0.7
21 deform==2.0.7
22 docutils==0.14.0
22 docutils==0.14.0
23 dogpile.cache==0.6.7
23 dogpile.cache==0.6.7
24 dogpile.core==0.4.1
24 dogpile.core==0.4.1
25 ecdsa==0.13
25 ecdsa==0.13
26 formencode==1.2.4
26 formencode==1.2.4
27 future==0.14.3
27 future==0.14.3
28 futures==3.0.2
28 futures==3.0.2
29 gnureadline==6.3.8
29 gnureadline==6.3.8
30 infrae.cache==1.0.1
30 infrae.cache==1.0.1
31 iso8601==0.1.11
31 iso8601==0.1.11
32 itsdangerous==0.24
32 itsdangerous==0.24
33 jinja2==2.9.6
33 jinja2==2.9.6
34 billiard==3.5.0.3
34 billiard==3.5.0.3
35 kombu==4.2.0
35 kombu==4.2.0
36 lxml==4.2.5
36 lxml==4.2.5
37 mako==1.0.7
37 mako==1.0.7
38 markdown==2.6.11
38 markdown==2.6.11
39 markupsafe==1.0.0
39 markupsafe==1.1.0
40 msgpack-python==0.5.6
40 msgpack-python==0.5.6
41 pyotp==2.2.7
41 pyotp==2.2.7
42 packaging==15.2
42 packaging==15.2
43 paste==3.0.5
43 paste==3.0.5
44 pastedeploy==2.0.1
44 pastedeploy==2.0.1
45 pastescript==3.0.0
45 pastescript==3.0.0
46 pathlib2==2.3.3
46 pathlib2==2.3.3
47 peppercorn==0.6
47 peppercorn==0.6
48 psutil==5.4.7
48 psutil==5.4.7
49 py-bcrypt==0.4
49 py-bcrypt==0.4
50 pycrypto==2.6.1
50 pycrypto==2.6.1
51 pycurl==7.43.0.2
51 pycurl==7.43.0.2
52 pyflakes==0.8.1
52 pyflakes==0.8.1
53 pygments==2.3.0
53 pygments==2.3.0
54 pyparsing==1.5.7
54 pyparsing==2.3.0
55 pyramid-beaker==0.8
55 pyramid-beaker==0.8
56 pyramid-debugtoolbar==4.4.0
56 pyramid-debugtoolbar==4.4.0
57 pyramid-jinja2==2.7
57 pyramid-jinja2==2.7
58 pyramid-mako==1.0.2
58 pyramid-mako==1.0.2
59 pyramid==1.10.1
59 pyramid==1.10.1
60 pyramid_mailer==0.15.1
60 pyramid_mailer==0.15.1
61 python-dateutil
61 python-dateutil
62 python-ldap==3.1.0
62 python-ldap==3.1.0
63 python-memcached==1.59
63 python-memcached==1.59
64 python-pam==1.8.4
64 python-pam==1.8.4
65 python-saml==2.4.2
65 python-saml==2.4.2
66 pytz==2018.4
66 pytz==2018.4
67 tzlocal==1.5.1
67 tzlocal==1.5.1
68 pyzmq==14.6.0
68 pyzmq==14.6.0
69 py-gfm==0.1.4
69 py-gfm==0.1.4
70 redis==2.10.6
70 redis==2.10.6
71 repoze.lru==0.7
71 repoze.lru==0.7
72 requests==2.9.1
72 requests==2.9.1
73 routes==2.4.1
73 routes==2.4.1
74 simplejson==3.11.1
74 simplejson==3.11.1
75 six==1.11.0
75 six==1.11.0
76 sqlalchemy==1.1.18
76 sqlalchemy==1.1.18
77 sshpubkeys==2.2.0
77 sshpubkeys==2.2.0
78 subprocess32==3.5.2
78 subprocess32==3.5.2
79 supervisor==3.3.4
79 supervisor==3.3.4
80 tempita==0.5.2
80 tempita==0.5.2
81 translationstring==1.3
81 translationstring==1.3
82 urllib3==1.21
82 urllib3==1.24.1
83 urlobject==2.4.3
83 urlobject==2.4.3
84 venusian==1.1.0
84 venusian==1.1.0
85 weberror==0.10.3
85 weberror==0.10.3
86 webhelpers2==2.0
86 webhelpers2==2.0
87 webhelpers==1.3
87 webhelpers==1.3
88 webob==1.8.4
88 webob==1.8.4
89 whoosh==2.7.4
89 whoosh==2.7.4
90 wsgiref==0.1.2
90 wsgiref==0.1.2
91 zope.cachedescriptors==4.3.1
91 zope.cachedescriptors==4.3.1
92 zope.deprecation==4.3.0
92 zope.deprecation==4.3.0
93 zope.event==4.3.0
93 zope.event==4.3.0
94 zope.interface==4.5.0
94 zope.interface==4.5.0
95
95
96 # DB drivers
96 # DB drivers
97 mysql-python==1.2.5
97 mysql-python==1.2.5
98 pymysql==0.8.1
98 pymysql==0.8.1
99 pysqlite==2.8.3
99 pysqlite==2.8.3
100 psycopg2==2.7.5
100 psycopg2==2.7.5
101
101
102 # IPYTHON RENDERING
102 # IPYTHON RENDERING
103 # entrypoints backport, pypi version doesn't support egg installs
103 # entrypoints backport, pypi version doesn't support egg installs
104 https://code.rhodecode.com/upstream/entrypoints/archive/96e6d645684e1af3d7df5b5272f3fe85a546b233.tar.gz?md5=7db37771aea9ac9fefe093e5d6987313#egg=entrypoints==0.2.2.rhodecode-upstream1
104 https://code.rhodecode.com/upstream/entrypoints/archive/96e6d645684e1af3d7df5b5272f3fe85a546b233.tar.gz?md5=7db37771aea9ac9fefe093e5d6987313#egg=entrypoints==0.2.2.rhodecode-upstream1
105 nbconvert==5.3.1
105 nbconvert==5.3.1
106 nbformat==4.4.0
106 nbformat==4.4.0
107 jupyter_client==5.0.0
107 jupyter_client==5.0.0
108
108
109 ## cli tools
109 ## cli tools
110 alembic==1.0.5
110 alembic==1.0.5
111 invoke==0.13.0
111 invoke==0.13.0
112 bumpversion==0.5.3
112 bumpversion==0.5.3
113
113
114 ## http servers
114 ## http servers
115 gevent==1.3.7
115 gevent==1.3.7
116 greenlet==0.4.15
116 greenlet==0.4.15
117 gunicorn==19.9.0
117 gunicorn==19.9.0
118 waitress==1.1.0
118 waitress==1.1.0
119 setproctitle==1.1.10
119 setproctitle==1.1.10
120
120
121 ## debug
121 ## debug
122 ipdb==0.11.0
122 ipdb==0.11.0
123 ipython==5.1.0
123 ipython==5.1.0
124
124
125 ## rhodecode-tools, special case
125 ## rhodecode-tools, special case
126 https://code.rhodecode.com/rhodecode-tools-ce/archive/v1.0.1.tar.gz?md5=ffb5d6bcb855305b93cfe23ad42e500b#egg=rhodecode-tools==1.0.1
126 https://code.rhodecode.com/rhodecode-tools-ce/archive/v1.1.0.tar.gz?md5=cc320c277cb2add546220290ac9be626#egg=rhodecode-tools==1.1.0
127
127
128 ## appenlight
128 ## appenlight
129 appenlight-client==0.6.26
129 appenlight-client==0.6.26
130
130
131 ## test related requirements
131 ## test related requirements
132 -r requirements_test.txt
132 -r requirements_test.txt
133
133
134 ## uncomment to add the debug libraries
134 ## uncomment to add the debug libraries
135 #-r requirements_debug.txt
135 #-r requirements_debug.txt
@@ -1,780 +1,780 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 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 import logging
22 import logging
23 import collections
23 import collections
24
24
25 import datetime
25 import datetime
26 import formencode
26 import formencode
27 import formencode.htmlfill
27 import formencode.htmlfill
28
28
29 import rhodecode
29 import rhodecode
30 from pyramid.view import view_config
30 from pyramid.view import view_config
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
31 from pyramid.httpexceptions import HTTPFound, HTTPNotFound
32 from pyramid.renderers import render
32 from pyramid.renderers import render
33 from pyramid.response import Response
33 from pyramid.response import Response
34
34
35 from rhodecode.apps._base import BaseAppView
35 from rhodecode.apps._base import BaseAppView
36 from rhodecode.apps._base.navigation import navigation_list
36 from rhodecode.apps._base.navigation import navigation_list
37 from rhodecode.apps.svn_support.config_keys import generate_config
37 from rhodecode.apps.svn_support.config_keys import generate_config
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import (
39 from rhodecode.lib.auth import (
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
40 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
41 from rhodecode.lib.celerylib import tasks, run_task
41 from rhodecode.lib.celerylib import tasks, run_task
42 from rhodecode.lib.utils import repo2db_mapper
42 from rhodecode.lib.utils import repo2db_mapper
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
43 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict
44 from rhodecode.lib.index import searcher_from_config
44 from rhodecode.lib.index import searcher_from_config
45
45
46 from rhodecode.model.db import RhodeCodeUi, Repository
46 from rhodecode.model.db import RhodeCodeUi, Repository
47 from rhodecode.model.forms import (ApplicationSettingsForm,
47 from rhodecode.model.forms import (ApplicationSettingsForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
48 ApplicationUiSettingsForm, ApplicationVisualisationForm,
49 LabsSettingsForm, IssueTrackerPatternsForm)
49 LabsSettingsForm, IssueTrackerPatternsForm)
50 from rhodecode.model.repo_group import RepoGroupModel
50 from rhodecode.model.repo_group import RepoGroupModel
51
51
52 from rhodecode.model.scm import ScmModel
52 from rhodecode.model.scm import ScmModel
53 from rhodecode.model.notification import EmailNotificationModel
53 from rhodecode.model.notification import EmailNotificationModel
54 from rhodecode.model.meta import Session
54 from rhodecode.model.meta import Session
55 from rhodecode.model.settings import (
55 from rhodecode.model.settings import (
56 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
56 IssueTrackerSettingsModel, VcsSettingsModel, SettingNotFound,
57 SettingsModel)
57 SettingsModel)
58
58
59
59
60 log = logging.getLogger(__name__)
60 log = logging.getLogger(__name__)
61
61
62
62
63 class AdminSettingsView(BaseAppView):
63 class AdminSettingsView(BaseAppView):
64
64
65 def load_default_context(self):
65 def load_default_context(self):
66 c = self._get_local_tmpl_context()
66 c = self._get_local_tmpl_context()
67 c.labs_active = str2bool(
67 c.labs_active = str2bool(
68 rhodecode.CONFIG.get('labs_settings_active', 'true'))
68 rhodecode.CONFIG.get('labs_settings_active', 'true'))
69 c.navlist = navigation_list(self.request)
69 c.navlist = navigation_list(self.request)
70
70
71 return c
71 return c
72
72
73 @classmethod
73 @classmethod
74 def _get_ui_settings(cls):
74 def _get_ui_settings(cls):
75 ret = RhodeCodeUi.query().all()
75 ret = RhodeCodeUi.query().all()
76
76
77 if not ret:
77 if not ret:
78 raise Exception('Could not get application ui settings !')
78 raise Exception('Could not get application ui settings !')
79 settings = {}
79 settings = {}
80 for each in ret:
80 for each in ret:
81 k = each.ui_key
81 k = each.ui_key
82 v = each.ui_value
82 v = each.ui_value
83 if k == '/':
83 if k == '/':
84 k = 'root_path'
84 k = 'root_path'
85
85
86 if k in ['push_ssl', 'publish', 'enabled']:
86 if k in ['push_ssl', 'publish', 'enabled']:
87 v = str2bool(v)
87 v = str2bool(v)
88
88
89 if k.find('.') != -1:
89 if k.find('.') != -1:
90 k = k.replace('.', '_')
90 k = k.replace('.', '_')
91
91
92 if each.ui_section in ['hooks', 'extensions']:
92 if each.ui_section in ['hooks', 'extensions']:
93 v = each.ui_active
93 v = each.ui_active
94
94
95 settings[each.ui_section + '_' + k] = v
95 settings[each.ui_section + '_' + k] = v
96 return settings
96 return settings
97
97
98 @classmethod
98 @classmethod
99 def _form_defaults(cls):
99 def _form_defaults(cls):
100 defaults = SettingsModel().get_all_settings()
100 defaults = SettingsModel().get_all_settings()
101 defaults.update(cls._get_ui_settings())
101 defaults.update(cls._get_ui_settings())
102
102
103 defaults.update({
103 defaults.update({
104 'new_svn_branch': '',
104 'new_svn_branch': '',
105 'new_svn_tag': '',
105 'new_svn_tag': '',
106 })
106 })
107 return defaults
107 return defaults
108
108
109 @LoginRequired()
109 @LoginRequired()
110 @HasPermissionAllDecorator('hg.admin')
110 @HasPermissionAllDecorator('hg.admin')
111 @view_config(
111 @view_config(
112 route_name='admin_settings_vcs', request_method='GET',
112 route_name='admin_settings_vcs', request_method='GET',
113 renderer='rhodecode:templates/admin/settings/settings.mako')
113 renderer='rhodecode:templates/admin/settings/settings.mako')
114 def settings_vcs(self):
114 def settings_vcs(self):
115 c = self.load_default_context()
115 c = self.load_default_context()
116 c.active = 'vcs'
116 c.active = 'vcs'
117 model = VcsSettingsModel()
117 model = VcsSettingsModel()
118 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
118 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
119 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
119 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
120
120
121 settings = self.request.registry.settings
121 settings = self.request.registry.settings
122 c.svn_proxy_generate_config = settings[generate_config]
122 c.svn_proxy_generate_config = settings[generate_config]
123
123
124 defaults = self._form_defaults()
124 defaults = self._form_defaults()
125
125
126 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
126 model.create_largeobjects_dirs_if_needed(defaults['paths_root_path'])
127
127
128 data = render('rhodecode:templates/admin/settings/settings.mako',
128 data = render('rhodecode:templates/admin/settings/settings.mako',
129 self._get_template_context(c), self.request)
129 self._get_template_context(c), self.request)
130 html = formencode.htmlfill.render(
130 html = formencode.htmlfill.render(
131 data,
131 data,
132 defaults=defaults,
132 defaults=defaults,
133 encoding="UTF-8",
133 encoding="UTF-8",
134 force_defaults=False
134 force_defaults=False
135 )
135 )
136 return Response(html)
136 return Response(html)
137
137
138 @LoginRequired()
138 @LoginRequired()
139 @HasPermissionAllDecorator('hg.admin')
139 @HasPermissionAllDecorator('hg.admin')
140 @CSRFRequired()
140 @CSRFRequired()
141 @view_config(
141 @view_config(
142 route_name='admin_settings_vcs_update', request_method='POST',
142 route_name='admin_settings_vcs_update', request_method='POST',
143 renderer='rhodecode:templates/admin/settings/settings.mako')
143 renderer='rhodecode:templates/admin/settings/settings.mako')
144 def settings_vcs_update(self):
144 def settings_vcs_update(self):
145 _ = self.request.translate
145 _ = self.request.translate
146 c = self.load_default_context()
146 c = self.load_default_context()
147 c.active = 'vcs'
147 c.active = 'vcs'
148
148
149 model = VcsSettingsModel()
149 model = VcsSettingsModel()
150 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
150 c.svn_branch_patterns = model.get_global_svn_branch_patterns()
151 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
151 c.svn_tag_patterns = model.get_global_svn_tag_patterns()
152
152
153 settings = self.request.registry.settings
153 settings = self.request.registry.settings
154 c.svn_proxy_generate_config = settings[generate_config]
154 c.svn_proxy_generate_config = settings[generate_config]
155
155
156 application_form = ApplicationUiSettingsForm(self.request.translate)()
156 application_form = ApplicationUiSettingsForm(self.request.translate)()
157
157
158 try:
158 try:
159 form_result = application_form.to_python(dict(self.request.POST))
159 form_result = application_form.to_python(dict(self.request.POST))
160 except formencode.Invalid as errors:
160 except formencode.Invalid as errors:
161 h.flash(
161 h.flash(
162 _("Some form inputs contain invalid data."),
162 _("Some form inputs contain invalid data."),
163 category='error')
163 category='error')
164 data = render('rhodecode:templates/admin/settings/settings.mako',
164 data = render('rhodecode:templates/admin/settings/settings.mako',
165 self._get_template_context(c), self.request)
165 self._get_template_context(c), self.request)
166 html = formencode.htmlfill.render(
166 html = formencode.htmlfill.render(
167 data,
167 data,
168 defaults=errors.value,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
169 errors=errors.error_dict or {},
170 prefix_error=False,
170 prefix_error=False,
171 encoding="UTF-8",
171 encoding="UTF-8",
172 force_defaults=False
172 force_defaults=False
173 )
173 )
174 return Response(html)
174 return Response(html)
175
175
176 try:
176 try:
177 if c.visual.allow_repo_location_change:
177 if c.visual.allow_repo_location_change:
178 model.update_global_path_setting(form_result['paths_root_path'])
178 model.update_global_path_setting(form_result['paths_root_path'])
179
179
180 model.update_global_ssl_setting(form_result['web_push_ssl'])
180 model.update_global_ssl_setting(form_result['web_push_ssl'])
181 model.update_global_hook_settings(form_result)
181 model.update_global_hook_settings(form_result)
182
182
183 model.create_or_update_global_svn_settings(form_result)
183 model.create_or_update_global_svn_settings(form_result)
184 model.create_or_update_global_hg_settings(form_result)
184 model.create_or_update_global_hg_settings(form_result)
185 model.create_or_update_global_git_settings(form_result)
185 model.create_or_update_global_git_settings(form_result)
186 model.create_or_update_global_pr_settings(form_result)
186 model.create_or_update_global_pr_settings(form_result)
187 except Exception:
187 except Exception:
188 log.exception("Exception while updating settings")
188 log.exception("Exception while updating settings")
189 h.flash(_('Error occurred during updating '
189 h.flash(_('Error occurred during updating '
190 'application settings'), category='error')
190 'application settings'), category='error')
191 else:
191 else:
192 Session().commit()
192 Session().commit()
193 h.flash(_('Updated VCS settings'), category='success')
193 h.flash(_('Updated VCS settings'), category='success')
194 raise HTTPFound(h.route_path('admin_settings_vcs'))
194 raise HTTPFound(h.route_path('admin_settings_vcs'))
195
195
196 data = render('rhodecode:templates/admin/settings/settings.mako',
196 data = render('rhodecode:templates/admin/settings/settings.mako',
197 self._get_template_context(c), self.request)
197 self._get_template_context(c), self.request)
198 html = formencode.htmlfill.render(
198 html = formencode.htmlfill.render(
199 data,
199 data,
200 defaults=self._form_defaults(),
200 defaults=self._form_defaults(),
201 encoding="UTF-8",
201 encoding="UTF-8",
202 force_defaults=False
202 force_defaults=False
203 )
203 )
204 return Response(html)
204 return Response(html)
205
205
206 @LoginRequired()
206 @LoginRequired()
207 @HasPermissionAllDecorator('hg.admin')
207 @HasPermissionAllDecorator('hg.admin')
208 @CSRFRequired()
208 @CSRFRequired()
209 @view_config(
209 @view_config(
210 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
210 route_name='admin_settings_vcs_svn_pattern_delete', request_method='POST',
211 renderer='json_ext', xhr=True)
211 renderer='json_ext', xhr=True)
212 def settings_vcs_delete_svn_pattern(self):
212 def settings_vcs_delete_svn_pattern(self):
213 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
213 delete_pattern_id = self.request.POST.get('delete_svn_pattern')
214 model = VcsSettingsModel()
214 model = VcsSettingsModel()
215 try:
215 try:
216 model.delete_global_svn_pattern(delete_pattern_id)
216 model.delete_global_svn_pattern(delete_pattern_id)
217 except SettingNotFound:
217 except SettingNotFound:
218 log.exception(
218 log.exception(
219 'Failed to delete svn_pattern with id %s', delete_pattern_id)
219 'Failed to delete svn_pattern with id %s', delete_pattern_id)
220 raise HTTPNotFound()
220 raise HTTPNotFound()
221
221
222 Session().commit()
222 Session().commit()
223 return True
223 return True
224
224
225 @LoginRequired()
225 @LoginRequired()
226 @HasPermissionAllDecorator('hg.admin')
226 @HasPermissionAllDecorator('hg.admin')
227 @view_config(
227 @view_config(
228 route_name='admin_settings_mapping', request_method='GET',
228 route_name='admin_settings_mapping', request_method='GET',
229 renderer='rhodecode:templates/admin/settings/settings.mako')
229 renderer='rhodecode:templates/admin/settings/settings.mako')
230 def settings_mapping(self):
230 def settings_mapping(self):
231 c = self.load_default_context()
231 c = self.load_default_context()
232 c.active = 'mapping'
232 c.active = 'mapping'
233
233
234 data = render('rhodecode:templates/admin/settings/settings.mako',
234 data = render('rhodecode:templates/admin/settings/settings.mako',
235 self._get_template_context(c), self.request)
235 self._get_template_context(c), self.request)
236 html = formencode.htmlfill.render(
236 html = formencode.htmlfill.render(
237 data,
237 data,
238 defaults=self._form_defaults(),
238 defaults=self._form_defaults(),
239 encoding="UTF-8",
239 encoding="UTF-8",
240 force_defaults=False
240 force_defaults=False
241 )
241 )
242 return Response(html)
242 return Response(html)
243
243
244 @LoginRequired()
244 @LoginRequired()
245 @HasPermissionAllDecorator('hg.admin')
245 @HasPermissionAllDecorator('hg.admin')
246 @CSRFRequired()
246 @CSRFRequired()
247 @view_config(
247 @view_config(
248 route_name='admin_settings_mapping_update', request_method='POST',
248 route_name='admin_settings_mapping_update', request_method='POST',
249 renderer='rhodecode:templates/admin/settings/settings.mako')
249 renderer='rhodecode:templates/admin/settings/settings.mako')
250 def settings_mapping_update(self):
250 def settings_mapping_update(self):
251 _ = self.request.translate
251 _ = self.request.translate
252 c = self.load_default_context()
252 c = self.load_default_context()
253 c.active = 'mapping'
253 c.active = 'mapping'
254 rm_obsolete = self.request.POST.get('destroy', False)
254 rm_obsolete = self.request.POST.get('destroy', False)
255 invalidate_cache = self.request.POST.get('invalidate', False)
255 invalidate_cache = self.request.POST.get('invalidate', False)
256 log.debug(
256 log.debug(
257 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
257 'rescanning repo location with destroy obsolete=%s', rm_obsolete)
258
258
259 if invalidate_cache:
259 if invalidate_cache:
260 log.debug('invalidating all repositories cache')
260 log.debug('invalidating all repositories cache')
261 for repo in Repository.get_all():
261 for repo in Repository.get_all():
262 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
262 ScmModel().mark_for_invalidation(repo.repo_name, delete=True)
263
263
264 filesystem_repos = ScmModel().repo_scan()
264 filesystem_repos = ScmModel().repo_scan()
265 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
265 added, removed = repo2db_mapper(filesystem_repos, rm_obsolete)
266 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
266 _repr = lambda l: ', '.join(map(safe_unicode, l)) or '-'
267 h.flash(_('Repositories successfully '
267 h.flash(_('Repositories successfully '
268 'rescanned added: %s ; removed: %s') %
268 'rescanned added: %s ; removed: %s') %
269 (_repr(added), _repr(removed)),
269 (_repr(added), _repr(removed)),
270 category='success')
270 category='success')
271 raise HTTPFound(h.route_path('admin_settings_mapping'))
271 raise HTTPFound(h.route_path('admin_settings_mapping'))
272
272
273 @LoginRequired()
273 @LoginRequired()
274 @HasPermissionAllDecorator('hg.admin')
274 @HasPermissionAllDecorator('hg.admin')
275 @view_config(
275 @view_config(
276 route_name='admin_settings', request_method='GET',
276 route_name='admin_settings', request_method='GET',
277 renderer='rhodecode:templates/admin/settings/settings.mako')
277 renderer='rhodecode:templates/admin/settings/settings.mako')
278 @view_config(
278 @view_config(
279 route_name='admin_settings_global', request_method='GET',
279 route_name='admin_settings_global', request_method='GET',
280 renderer='rhodecode:templates/admin/settings/settings.mako')
280 renderer='rhodecode:templates/admin/settings/settings.mako')
281 def settings_global(self):
281 def settings_global(self):
282 c = self.load_default_context()
282 c = self.load_default_context()
283 c.active = 'global'
283 c.active = 'global'
284 c.personal_repo_group_default_pattern = RepoGroupModel()\
284 c.personal_repo_group_default_pattern = RepoGroupModel()\
285 .get_personal_group_name_pattern()
285 .get_personal_group_name_pattern()
286
286
287 data = render('rhodecode:templates/admin/settings/settings.mako',
287 data = render('rhodecode:templates/admin/settings/settings.mako',
288 self._get_template_context(c), self.request)
288 self._get_template_context(c), self.request)
289 html = formencode.htmlfill.render(
289 html = formencode.htmlfill.render(
290 data,
290 data,
291 defaults=self._form_defaults(),
291 defaults=self._form_defaults(),
292 encoding="UTF-8",
292 encoding="UTF-8",
293 force_defaults=False
293 force_defaults=False
294 )
294 )
295 return Response(html)
295 return Response(html)
296
296
297 @LoginRequired()
297 @LoginRequired()
298 @HasPermissionAllDecorator('hg.admin')
298 @HasPermissionAllDecorator('hg.admin')
299 @CSRFRequired()
299 @CSRFRequired()
300 @view_config(
300 @view_config(
301 route_name='admin_settings_update', request_method='POST',
301 route_name='admin_settings_update', request_method='POST',
302 renderer='rhodecode:templates/admin/settings/settings.mako')
302 renderer='rhodecode:templates/admin/settings/settings.mako')
303 @view_config(
303 @view_config(
304 route_name='admin_settings_global_update', request_method='POST',
304 route_name='admin_settings_global_update', request_method='POST',
305 renderer='rhodecode:templates/admin/settings/settings.mako')
305 renderer='rhodecode:templates/admin/settings/settings.mako')
306 def settings_global_update(self):
306 def settings_global_update(self):
307 _ = self.request.translate
307 _ = self.request.translate
308 c = self.load_default_context()
308 c = self.load_default_context()
309 c.active = 'global'
309 c.active = 'global'
310 c.personal_repo_group_default_pattern = RepoGroupModel()\
310 c.personal_repo_group_default_pattern = RepoGroupModel()\
311 .get_personal_group_name_pattern()
311 .get_personal_group_name_pattern()
312 application_form = ApplicationSettingsForm(self.request.translate)()
312 application_form = ApplicationSettingsForm(self.request.translate)()
313 try:
313 try:
314 form_result = application_form.to_python(dict(self.request.POST))
314 form_result = application_form.to_python(dict(self.request.POST))
315 except formencode.Invalid as errors:
315 except formencode.Invalid as errors:
316 h.flash(
316 h.flash(
317 _("Some form inputs contain invalid data."),
317 _("Some form inputs contain invalid data."),
318 category='error')
318 category='error')
319 data = render('rhodecode:templates/admin/settings/settings.mako',
319 data = render('rhodecode:templates/admin/settings/settings.mako',
320 self._get_template_context(c), self.request)
320 self._get_template_context(c), self.request)
321 html = formencode.htmlfill.render(
321 html = formencode.htmlfill.render(
322 data,
322 data,
323 defaults=errors.value,
323 defaults=errors.value,
324 errors=errors.error_dict or {},
324 errors=errors.error_dict or {},
325 prefix_error=False,
325 prefix_error=False,
326 encoding="UTF-8",
326 encoding="UTF-8",
327 force_defaults=False
327 force_defaults=False
328 )
328 )
329 return Response(html)
329 return Response(html)
330
330
331 settings = [
331 settings = [
332 ('title', 'rhodecode_title', 'unicode'),
332 ('title', 'rhodecode_title', 'unicode'),
333 ('realm', 'rhodecode_realm', 'unicode'),
333 ('realm', 'rhodecode_realm', 'unicode'),
334 ('pre_code', 'rhodecode_pre_code', 'unicode'),
334 ('pre_code', 'rhodecode_pre_code', 'unicode'),
335 ('post_code', 'rhodecode_post_code', 'unicode'),
335 ('post_code', 'rhodecode_post_code', 'unicode'),
336 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
336 ('captcha_public_key', 'rhodecode_captcha_public_key', 'unicode'),
337 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
337 ('captcha_private_key', 'rhodecode_captcha_private_key', 'unicode'),
338 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
338 ('create_personal_repo_group', 'rhodecode_create_personal_repo_group', 'bool'),
339 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
339 ('personal_repo_group_pattern', 'rhodecode_personal_repo_group_pattern', 'unicode'),
340 ]
340 ]
341 try:
341 try:
342 for setting, form_key, type_ in settings:
342 for setting, form_key, type_ in settings:
343 sett = SettingsModel().create_or_update_setting(
343 sett = SettingsModel().create_or_update_setting(
344 setting, form_result[form_key], type_)
344 setting, form_result[form_key], type_)
345 Session().add(sett)
345 Session().add(sett)
346
346
347 Session().commit()
347 Session().commit()
348 SettingsModel().invalidate_settings_cache()
348 SettingsModel().invalidate_settings_cache()
349 h.flash(_('Updated application settings'), category='success')
349 h.flash(_('Updated application settings'), category='success')
350 except Exception:
350 except Exception:
351 log.exception("Exception while updating application settings")
351 log.exception("Exception while updating application settings")
352 h.flash(
352 h.flash(
353 _('Error occurred during updating application settings'),
353 _('Error occurred during updating application settings'),
354 category='error')
354 category='error')
355
355
356 raise HTTPFound(h.route_path('admin_settings_global'))
356 raise HTTPFound(h.route_path('admin_settings_global'))
357
357
358 @LoginRequired()
358 @LoginRequired()
359 @HasPermissionAllDecorator('hg.admin')
359 @HasPermissionAllDecorator('hg.admin')
360 @view_config(
360 @view_config(
361 route_name='admin_settings_visual', request_method='GET',
361 route_name='admin_settings_visual', request_method='GET',
362 renderer='rhodecode:templates/admin/settings/settings.mako')
362 renderer='rhodecode:templates/admin/settings/settings.mako')
363 def settings_visual(self):
363 def settings_visual(self):
364 c = self.load_default_context()
364 c = self.load_default_context()
365 c.active = 'visual'
365 c.active = 'visual'
366
366
367 data = render('rhodecode:templates/admin/settings/settings.mako',
367 data = render('rhodecode:templates/admin/settings/settings.mako',
368 self._get_template_context(c), self.request)
368 self._get_template_context(c), self.request)
369 html = formencode.htmlfill.render(
369 html = formencode.htmlfill.render(
370 data,
370 data,
371 defaults=self._form_defaults(),
371 defaults=self._form_defaults(),
372 encoding="UTF-8",
372 encoding="UTF-8",
373 force_defaults=False
373 force_defaults=False
374 )
374 )
375 return Response(html)
375 return Response(html)
376
376
377 @LoginRequired()
377 @LoginRequired()
378 @HasPermissionAllDecorator('hg.admin')
378 @HasPermissionAllDecorator('hg.admin')
379 @CSRFRequired()
379 @CSRFRequired()
380 @view_config(
380 @view_config(
381 route_name='admin_settings_visual_update', request_method='POST',
381 route_name='admin_settings_visual_update', request_method='POST',
382 renderer='rhodecode:templates/admin/settings/settings.mako')
382 renderer='rhodecode:templates/admin/settings/settings.mako')
383 def settings_visual_update(self):
383 def settings_visual_update(self):
384 _ = self.request.translate
384 _ = self.request.translate
385 c = self.load_default_context()
385 c = self.load_default_context()
386 c.active = 'visual'
386 c.active = 'visual'
387 application_form = ApplicationVisualisationForm(self.request.translate)()
387 application_form = ApplicationVisualisationForm(self.request.translate)()
388 try:
388 try:
389 form_result = application_form.to_python(dict(self.request.POST))
389 form_result = application_form.to_python(dict(self.request.POST))
390 except formencode.Invalid as errors:
390 except formencode.Invalid as errors:
391 h.flash(
391 h.flash(
392 _("Some form inputs contain invalid data."),
392 _("Some form inputs contain invalid data."),
393 category='error')
393 category='error')
394 data = render('rhodecode:templates/admin/settings/settings.mako',
394 data = render('rhodecode:templates/admin/settings/settings.mako',
395 self._get_template_context(c), self.request)
395 self._get_template_context(c), self.request)
396 html = formencode.htmlfill.render(
396 html = formencode.htmlfill.render(
397 data,
397 data,
398 defaults=errors.value,
398 defaults=errors.value,
399 errors=errors.error_dict or {},
399 errors=errors.error_dict or {},
400 prefix_error=False,
400 prefix_error=False,
401 encoding="UTF-8",
401 encoding="UTF-8",
402 force_defaults=False
402 force_defaults=False
403 )
403 )
404 return Response(html)
404 return Response(html)
405
405
406 try:
406 try:
407 settings = [
407 settings = [
408 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
408 ('show_public_icon', 'rhodecode_show_public_icon', 'bool'),
409 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
409 ('show_private_icon', 'rhodecode_show_private_icon', 'bool'),
410 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
410 ('stylify_metatags', 'rhodecode_stylify_metatags', 'bool'),
411 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
411 ('repository_fields', 'rhodecode_repository_fields', 'bool'),
412 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
412 ('dashboard_items', 'rhodecode_dashboard_items', 'int'),
413 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
413 ('admin_grid_items', 'rhodecode_admin_grid_items', 'int'),
414 ('show_version', 'rhodecode_show_version', 'bool'),
414 ('show_version', 'rhodecode_show_version', 'bool'),
415 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
415 ('use_gravatar', 'rhodecode_use_gravatar', 'bool'),
416 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
416 ('markup_renderer', 'rhodecode_markup_renderer', 'unicode'),
417 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
417 ('gravatar_url', 'rhodecode_gravatar_url', 'unicode'),
418 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
418 ('clone_uri_tmpl', 'rhodecode_clone_uri_tmpl', 'unicode'),
419 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
419 ('clone_uri_ssh_tmpl', 'rhodecode_clone_uri_ssh_tmpl', 'unicode'),
420 ('support_url', 'rhodecode_support_url', 'unicode'),
420 ('support_url', 'rhodecode_support_url', 'unicode'),
421 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
421 ('show_revision_number', 'rhodecode_show_revision_number', 'bool'),
422 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
422 ('show_sha_length', 'rhodecode_show_sha_length', 'int'),
423 ]
423 ]
424 for setting, form_key, type_ in settings:
424 for setting, form_key, type_ in settings:
425 sett = SettingsModel().create_or_update_setting(
425 sett = SettingsModel().create_or_update_setting(
426 setting, form_result[form_key], type_)
426 setting, form_result[form_key], type_)
427 Session().add(sett)
427 Session().add(sett)
428
428
429 Session().commit()
429 Session().commit()
430 SettingsModel().invalidate_settings_cache()
430 SettingsModel().invalidate_settings_cache()
431 h.flash(_('Updated visualisation settings'), category='success')
431 h.flash(_('Updated visualisation settings'), category='success')
432 except Exception:
432 except Exception:
433 log.exception("Exception updating visualization settings")
433 log.exception("Exception updating visualization settings")
434 h.flash(_('Error occurred during updating '
434 h.flash(_('Error occurred during updating '
435 'visualisation settings'),
435 'visualisation settings'),
436 category='error')
436 category='error')
437
437
438 raise HTTPFound(h.route_path('admin_settings_visual'))
438 raise HTTPFound(h.route_path('admin_settings_visual'))
439
439
440 @LoginRequired()
440 @LoginRequired()
441 @HasPermissionAllDecorator('hg.admin')
441 @HasPermissionAllDecorator('hg.admin')
442 @view_config(
442 @view_config(
443 route_name='admin_settings_issuetracker', request_method='GET',
443 route_name='admin_settings_issuetracker', request_method='GET',
444 renderer='rhodecode:templates/admin/settings/settings.mako')
444 renderer='rhodecode:templates/admin/settings/settings.mako')
445 def settings_issuetracker(self):
445 def settings_issuetracker(self):
446 c = self.load_default_context()
446 c = self.load_default_context()
447 c.active = 'issuetracker'
447 c.active = 'issuetracker'
448 defaults = SettingsModel().get_all_settings()
448 defaults = SettingsModel().get_all_settings()
449
449
450 entry_key = 'rhodecode_issuetracker_pat_'
450 entry_key = 'rhodecode_issuetracker_pat_'
451
451
452 c.issuetracker_entries = {}
452 c.issuetracker_entries = {}
453 for k, v in defaults.items():
453 for k, v in defaults.items():
454 if k.startswith(entry_key):
454 if k.startswith(entry_key):
455 uid = k[len(entry_key):]
455 uid = k[len(entry_key):]
456 c.issuetracker_entries[uid] = None
456 c.issuetracker_entries[uid] = None
457
457
458 for uid in c.issuetracker_entries:
458 for uid in c.issuetracker_entries:
459 c.issuetracker_entries[uid] = AttributeDict({
459 c.issuetracker_entries[uid] = AttributeDict({
460 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
460 'pat': defaults.get('rhodecode_issuetracker_pat_' + uid),
461 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
461 'url': defaults.get('rhodecode_issuetracker_url_' + uid),
462 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
462 'pref': defaults.get('rhodecode_issuetracker_pref_' + uid),
463 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
463 'desc': defaults.get('rhodecode_issuetracker_desc_' + uid),
464 })
464 })
465
465
466 return self._get_template_context(c)
466 return self._get_template_context(c)
467
467
468 @LoginRequired()
468 @LoginRequired()
469 @HasPermissionAllDecorator('hg.admin')
469 @HasPermissionAllDecorator('hg.admin')
470 @CSRFRequired()
470 @CSRFRequired()
471 @view_config(
471 @view_config(
472 route_name='admin_settings_issuetracker_test', request_method='POST',
472 route_name='admin_settings_issuetracker_test', request_method='POST',
473 renderer='string', xhr=True)
473 renderer='string', xhr=True)
474 def settings_issuetracker_test(self):
474 def settings_issuetracker_test(self):
475 return h.urlify_commit_message(
475 return h.urlify_commit_message(
476 self.request.POST.get('test_text', ''),
476 self.request.POST.get('test_text', ''),
477 'repo_group/test_repo1')
477 'repo_group/test_repo1')
478
478
479 @LoginRequired()
479 @LoginRequired()
480 @HasPermissionAllDecorator('hg.admin')
480 @HasPermissionAllDecorator('hg.admin')
481 @CSRFRequired()
481 @CSRFRequired()
482 @view_config(
482 @view_config(
483 route_name='admin_settings_issuetracker_update', request_method='POST',
483 route_name='admin_settings_issuetracker_update', request_method='POST',
484 renderer='rhodecode:templates/admin/settings/settings.mako')
484 renderer='rhodecode:templates/admin/settings/settings.mako')
485 def settings_issuetracker_update(self):
485 def settings_issuetracker_update(self):
486 _ = self.request.translate
486 _ = self.request.translate
487 self.load_default_context()
487 self.load_default_context()
488 settings_model = IssueTrackerSettingsModel()
488 settings_model = IssueTrackerSettingsModel()
489
489
490 try:
490 try:
491 form = IssueTrackerPatternsForm(self.request.translate)()
491 form = IssueTrackerPatternsForm(self.request.translate)()
492 data = form.to_python(self.request.POST)
492 data = form.to_python(self.request.POST)
493 except formencode.Invalid as errors:
493 except formencode.Invalid as errors:
494 log.exception('Failed to add new pattern')
494 log.exception('Failed to add new pattern')
495 error = errors
495 error = errors
496 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
496 h.flash(_('Invalid issue tracker pattern: {}'.format(error)),
497 category='error')
497 category='error')
498 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
498 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
499
499
500 if data:
500 if data:
501 for uid in data.get('delete_patterns', []):
501 for uid in data.get('delete_patterns', []):
502 settings_model.delete_entries(uid)
502 settings_model.delete_entries(uid)
503
503
504 for pattern in data.get('patterns', []):
504 for pattern in data.get('patterns', []):
505 for setting, value, type_ in pattern:
505 for setting, value, type_ in pattern:
506 sett = settings_model.create_or_update_setting(
506 sett = settings_model.create_or_update_setting(
507 setting, value, type_)
507 setting, value, type_)
508 Session().add(sett)
508 Session().add(sett)
509
509
510 Session().commit()
510 Session().commit()
511
511
512 SettingsModel().invalidate_settings_cache()
512 SettingsModel().invalidate_settings_cache()
513 h.flash(_('Updated issue tracker entries'), category='success')
513 h.flash(_('Updated issue tracker entries'), category='success')
514 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
514 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
515
515
516 @LoginRequired()
516 @LoginRequired()
517 @HasPermissionAllDecorator('hg.admin')
517 @HasPermissionAllDecorator('hg.admin')
518 @CSRFRequired()
518 @CSRFRequired()
519 @view_config(
519 @view_config(
520 route_name='admin_settings_issuetracker_delete', request_method='POST',
520 route_name='admin_settings_issuetracker_delete', request_method='POST',
521 renderer='rhodecode:templates/admin/settings/settings.mako')
521 renderer='rhodecode:templates/admin/settings/settings.mako')
522 def settings_issuetracker_delete(self):
522 def settings_issuetracker_delete(self):
523 _ = self.request.translate
523 _ = self.request.translate
524 self.load_default_context()
524 self.load_default_context()
525 uid = self.request.POST.get('uid')
525 uid = self.request.POST.get('uid')
526 try:
526 try:
527 IssueTrackerSettingsModel().delete_entries(uid)
527 IssueTrackerSettingsModel().delete_entries(uid)
528 except Exception:
528 except Exception:
529 log.exception('Failed to delete issue tracker setting %s', uid)
529 log.exception('Failed to delete issue tracker setting %s', uid)
530 raise HTTPNotFound()
530 raise HTTPNotFound()
531 h.flash(_('Removed issue tracker entry'), category='success')
531 h.flash(_('Removed issue tracker entry'), category='success')
532 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
532 raise HTTPFound(h.route_path('admin_settings_issuetracker'))
533
533
534 @LoginRequired()
534 @LoginRequired()
535 @HasPermissionAllDecorator('hg.admin')
535 @HasPermissionAllDecorator('hg.admin')
536 @view_config(
536 @view_config(
537 route_name='admin_settings_email', request_method='GET',
537 route_name='admin_settings_email', request_method='GET',
538 renderer='rhodecode:templates/admin/settings/settings.mako')
538 renderer='rhodecode:templates/admin/settings/settings.mako')
539 def settings_email(self):
539 def settings_email(self):
540 c = self.load_default_context()
540 c = self.load_default_context()
541 c.active = 'email'
541 c.active = 'email'
542 c.rhodecode_ini = rhodecode.CONFIG
542 c.rhodecode_ini = rhodecode.CONFIG
543
543
544 data = render('rhodecode:templates/admin/settings/settings.mako',
544 data = render('rhodecode:templates/admin/settings/settings.mako',
545 self._get_template_context(c), self.request)
545 self._get_template_context(c), self.request)
546 html = formencode.htmlfill.render(
546 html = formencode.htmlfill.render(
547 data,
547 data,
548 defaults=self._form_defaults(),
548 defaults=self._form_defaults(),
549 encoding="UTF-8",
549 encoding="UTF-8",
550 force_defaults=False
550 force_defaults=False
551 )
551 )
552 return Response(html)
552 return Response(html)
553
553
554 @LoginRequired()
554 @LoginRequired()
555 @HasPermissionAllDecorator('hg.admin')
555 @HasPermissionAllDecorator('hg.admin')
556 @CSRFRequired()
556 @CSRFRequired()
557 @view_config(
557 @view_config(
558 route_name='admin_settings_email_update', request_method='POST',
558 route_name='admin_settings_email_update', request_method='POST',
559 renderer='rhodecode:templates/admin/settings/settings.mako')
559 renderer='rhodecode:templates/admin/settings/settings.mako')
560 def settings_email_update(self):
560 def settings_email_update(self):
561 _ = self.request.translate
561 _ = self.request.translate
562 c = self.load_default_context()
562 c = self.load_default_context()
563 c.active = 'email'
563 c.active = 'email'
564
564
565 test_email = self.request.POST.get('test_email')
565 test_email = self.request.POST.get('test_email')
566
566
567 if not test_email:
567 if not test_email:
568 h.flash(_('Please enter email address'), category='error')
568 h.flash(_('Please enter email address'), category='error')
569 raise HTTPFound(h.route_path('admin_settings_email'))
569 raise HTTPFound(h.route_path('admin_settings_email'))
570
570
571 email_kwargs = {
571 email_kwargs = {
572 'date': datetime.datetime.now(),
572 'date': datetime.datetime.now(),
573 'user': c.rhodecode_user,
573 'user': c.rhodecode_user,
574 'rhodecode_version': c.rhodecode_version
574 'rhodecode_version': c.rhodecode_version
575 }
575 }
576
576
577 (subject, headers, email_body,
577 (subject, headers, email_body,
578 email_body_plaintext) = EmailNotificationModel().render_email(
578 email_body_plaintext) = EmailNotificationModel().render_email(
579 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
579 EmailNotificationModel.TYPE_EMAIL_TEST, **email_kwargs)
580
580
581 recipients = [test_email] if test_email else None
581 recipients = [test_email] if test_email else None
582
582
583 run_task(tasks.send_email, recipients, subject,
583 run_task(tasks.send_email, recipients, subject,
584 email_body_plaintext, email_body)
584 email_body_plaintext, email_body)
585
585
586 h.flash(_('Send email task created'), category='success')
586 h.flash(_('Send email task created'), category='success')
587 raise HTTPFound(h.route_path('admin_settings_email'))
587 raise HTTPFound(h.route_path('admin_settings_email'))
588
588
589 @LoginRequired()
589 @LoginRequired()
590 @HasPermissionAllDecorator('hg.admin')
590 @HasPermissionAllDecorator('hg.admin')
591 @view_config(
591 @view_config(
592 route_name='admin_settings_hooks', request_method='GET',
592 route_name='admin_settings_hooks', request_method='GET',
593 renderer='rhodecode:templates/admin/settings/settings.mako')
593 renderer='rhodecode:templates/admin/settings/settings.mako')
594 def settings_hooks(self):
594 def settings_hooks(self):
595 c = self.load_default_context()
595 c = self.load_default_context()
596 c.active = 'hooks'
596 c.active = 'hooks'
597
597
598 model = SettingsModel()
598 model = SettingsModel()
599 c.hooks = model.get_builtin_hooks()
599 c.hooks = model.get_builtin_hooks()
600 c.custom_hooks = model.get_custom_hooks()
600 c.custom_hooks = model.get_custom_hooks()
601
601
602 data = render('rhodecode:templates/admin/settings/settings.mako',
602 data = render('rhodecode:templates/admin/settings/settings.mako',
603 self._get_template_context(c), self.request)
603 self._get_template_context(c), self.request)
604 html = formencode.htmlfill.render(
604 html = formencode.htmlfill.render(
605 data,
605 data,
606 defaults=self._form_defaults(),
606 defaults=self._form_defaults(),
607 encoding="UTF-8",
607 encoding="UTF-8",
608 force_defaults=False
608 force_defaults=False
609 )
609 )
610 return Response(html)
610 return Response(html)
611
611
612 @LoginRequired()
612 @LoginRequired()
613 @HasPermissionAllDecorator('hg.admin')
613 @HasPermissionAllDecorator('hg.admin')
614 @CSRFRequired()
614 @CSRFRequired()
615 @view_config(
615 @view_config(
616 route_name='admin_settings_hooks_update', request_method='POST',
616 route_name='admin_settings_hooks_update', request_method='POST',
617 renderer='rhodecode:templates/admin/settings/settings.mako')
617 renderer='rhodecode:templates/admin/settings/settings.mako')
618 @view_config(
618 @view_config(
619 route_name='admin_settings_hooks_delete', request_method='POST',
619 route_name='admin_settings_hooks_delete', request_method='POST',
620 renderer='rhodecode:templates/admin/settings/settings.mako')
620 renderer='rhodecode:templates/admin/settings/settings.mako')
621 def settings_hooks_update(self):
621 def settings_hooks_update(self):
622 _ = self.request.translate
622 _ = self.request.translate
623 c = self.load_default_context()
623 c = self.load_default_context()
624 c.active = 'hooks'
624 c.active = 'hooks'
625 if c.visual.allow_custom_hooks_settings:
625 if c.visual.allow_custom_hooks_settings:
626 ui_key = self.request.POST.get('new_hook_ui_key')
626 ui_key = self.request.POST.get('new_hook_ui_key')
627 ui_value = self.request.POST.get('new_hook_ui_value')
627 ui_value = self.request.POST.get('new_hook_ui_value')
628
628
629 hook_id = self.request.POST.get('hook_id')
629 hook_id = self.request.POST.get('hook_id')
630 new_hook = False
630 new_hook = False
631
631
632 model = SettingsModel()
632 model = SettingsModel()
633 try:
633 try:
634 if ui_value and ui_key:
634 if ui_value and ui_key:
635 model.create_or_update_hook(ui_key, ui_value)
635 model.create_or_update_hook(ui_key, ui_value)
636 h.flash(_('Added new hook'), category='success')
636 h.flash(_('Added new hook'), category='success')
637 new_hook = True
637 new_hook = True
638 elif hook_id:
638 elif hook_id:
639 RhodeCodeUi.delete(hook_id)
639 RhodeCodeUi.delete(hook_id)
640 Session().commit()
640 Session().commit()
641
641
642 # check for edits
642 # check for edits
643 update = False
643 update = False
644 _d = self.request.POST.dict_of_lists()
644 _d = self.request.POST.dict_of_lists()
645 for k, v in zip(_d.get('hook_ui_key', []),
645 for k, v in zip(_d.get('hook_ui_key', []),
646 _d.get('hook_ui_value_new', [])):
646 _d.get('hook_ui_value_new', [])):
647 model.create_or_update_hook(k, v)
647 model.create_or_update_hook(k, v)
648 update = True
648 update = True
649
649
650 if update and not new_hook:
650 if update and not new_hook:
651 h.flash(_('Updated hooks'), category='success')
651 h.flash(_('Updated hooks'), category='success')
652 Session().commit()
652 Session().commit()
653 except Exception:
653 except Exception:
654 log.exception("Exception during hook creation")
654 log.exception("Exception during hook creation")
655 h.flash(_('Error occurred during hook creation'),
655 h.flash(_('Error occurred during hook creation'),
656 category='error')
656 category='error')
657
657
658 raise HTTPFound(h.route_path('admin_settings_hooks'))
658 raise HTTPFound(h.route_path('admin_settings_hooks'))
659
659
660 @LoginRequired()
660 @LoginRequired()
661 @HasPermissionAllDecorator('hg.admin')
661 @HasPermissionAllDecorator('hg.admin')
662 @view_config(
662 @view_config(
663 route_name='admin_settings_search', request_method='GET',
663 route_name='admin_settings_search', request_method='GET',
664 renderer='rhodecode:templates/admin/settings/settings.mako')
664 renderer='rhodecode:templates/admin/settings/settings.mako')
665 def settings_search(self):
665 def settings_search(self):
666 c = self.load_default_context()
666 c = self.load_default_context()
667 c.active = 'search'
667 c.active = 'search'
668
668
669 searcher = searcher_from_config(self.request.registry.settings)
669 c.searcher = searcher_from_config(self.request.registry.settings)
670 c.statistics = searcher.statistics(self.request.translate)
670 c.statistics = c.searcher.statistics(self.request.translate)
671
671
672 return self._get_template_context(c)
672 return self._get_template_context(c)
673
673
674 @LoginRequired()
674 @LoginRequired()
675 @HasPermissionAllDecorator('hg.admin')
675 @HasPermissionAllDecorator('hg.admin')
676 @view_config(
676 @view_config(
677 route_name='admin_settings_automation', request_method='GET',
677 route_name='admin_settings_automation', request_method='GET',
678 renderer='rhodecode:templates/admin/settings/settings.mako')
678 renderer='rhodecode:templates/admin/settings/settings.mako')
679 def settings_automation(self):
679 def settings_automation(self):
680 c = self.load_default_context()
680 c = self.load_default_context()
681 c.active = 'automation'
681 c.active = 'automation'
682
682
683 return self._get_template_context(c)
683 return self._get_template_context(c)
684
684
685 @LoginRequired()
685 @LoginRequired()
686 @HasPermissionAllDecorator('hg.admin')
686 @HasPermissionAllDecorator('hg.admin')
687 @view_config(
687 @view_config(
688 route_name='admin_settings_labs', request_method='GET',
688 route_name='admin_settings_labs', request_method='GET',
689 renderer='rhodecode:templates/admin/settings/settings.mako')
689 renderer='rhodecode:templates/admin/settings/settings.mako')
690 def settings_labs(self):
690 def settings_labs(self):
691 c = self.load_default_context()
691 c = self.load_default_context()
692 if not c.labs_active:
692 if not c.labs_active:
693 raise HTTPFound(h.route_path('admin_settings'))
693 raise HTTPFound(h.route_path('admin_settings'))
694
694
695 c.active = 'labs'
695 c.active = 'labs'
696 c.lab_settings = _LAB_SETTINGS
696 c.lab_settings = _LAB_SETTINGS
697
697
698 data = render('rhodecode:templates/admin/settings/settings.mako',
698 data = render('rhodecode:templates/admin/settings/settings.mako',
699 self._get_template_context(c), self.request)
699 self._get_template_context(c), self.request)
700 html = formencode.htmlfill.render(
700 html = formencode.htmlfill.render(
701 data,
701 data,
702 defaults=self._form_defaults(),
702 defaults=self._form_defaults(),
703 encoding="UTF-8",
703 encoding="UTF-8",
704 force_defaults=False
704 force_defaults=False
705 )
705 )
706 return Response(html)
706 return Response(html)
707
707
708 @LoginRequired()
708 @LoginRequired()
709 @HasPermissionAllDecorator('hg.admin')
709 @HasPermissionAllDecorator('hg.admin')
710 @CSRFRequired()
710 @CSRFRequired()
711 @view_config(
711 @view_config(
712 route_name='admin_settings_labs_update', request_method='POST',
712 route_name='admin_settings_labs_update', request_method='POST',
713 renderer='rhodecode:templates/admin/settings/settings.mako')
713 renderer='rhodecode:templates/admin/settings/settings.mako')
714 def settings_labs_update(self):
714 def settings_labs_update(self):
715 _ = self.request.translate
715 _ = self.request.translate
716 c = self.load_default_context()
716 c = self.load_default_context()
717 c.active = 'labs'
717 c.active = 'labs'
718
718
719 application_form = LabsSettingsForm(self.request.translate)()
719 application_form = LabsSettingsForm(self.request.translate)()
720 try:
720 try:
721 form_result = application_form.to_python(dict(self.request.POST))
721 form_result = application_form.to_python(dict(self.request.POST))
722 except formencode.Invalid as errors:
722 except formencode.Invalid as errors:
723 h.flash(
723 h.flash(
724 _("Some form inputs contain invalid data."),
724 _("Some form inputs contain invalid data."),
725 category='error')
725 category='error')
726 data = render('rhodecode:templates/admin/settings/settings.mako',
726 data = render('rhodecode:templates/admin/settings/settings.mako',
727 self._get_template_context(c), self.request)
727 self._get_template_context(c), self.request)
728 html = formencode.htmlfill.render(
728 html = formencode.htmlfill.render(
729 data,
729 data,
730 defaults=errors.value,
730 defaults=errors.value,
731 errors=errors.error_dict or {},
731 errors=errors.error_dict or {},
732 prefix_error=False,
732 prefix_error=False,
733 encoding="UTF-8",
733 encoding="UTF-8",
734 force_defaults=False
734 force_defaults=False
735 )
735 )
736 return Response(html)
736 return Response(html)
737
737
738 try:
738 try:
739 session = Session()
739 session = Session()
740 for setting in _LAB_SETTINGS:
740 for setting in _LAB_SETTINGS:
741 setting_name = setting.key[len('rhodecode_'):]
741 setting_name = setting.key[len('rhodecode_'):]
742 sett = SettingsModel().create_or_update_setting(
742 sett = SettingsModel().create_or_update_setting(
743 setting_name, form_result[setting.key], setting.type)
743 setting_name, form_result[setting.key], setting.type)
744 session.add(sett)
744 session.add(sett)
745
745
746 except Exception:
746 except Exception:
747 log.exception('Exception while updating lab settings')
747 log.exception('Exception while updating lab settings')
748 h.flash(_('Error occurred during updating labs settings'),
748 h.flash(_('Error occurred during updating labs settings'),
749 category='error')
749 category='error')
750 else:
750 else:
751 Session().commit()
751 Session().commit()
752 SettingsModel().invalidate_settings_cache()
752 SettingsModel().invalidate_settings_cache()
753 h.flash(_('Updated Labs settings'), category='success')
753 h.flash(_('Updated Labs settings'), category='success')
754 raise HTTPFound(h.route_path('admin_settings_labs'))
754 raise HTTPFound(h.route_path('admin_settings_labs'))
755
755
756 data = render('rhodecode:templates/admin/settings/settings.mako',
756 data = render('rhodecode:templates/admin/settings/settings.mako',
757 self._get_template_context(c), self.request)
757 self._get_template_context(c), self.request)
758 html = formencode.htmlfill.render(
758 html = formencode.htmlfill.render(
759 data,
759 data,
760 defaults=self._form_defaults(),
760 defaults=self._form_defaults(),
761 encoding="UTF-8",
761 encoding="UTF-8",
762 force_defaults=False
762 force_defaults=False
763 )
763 )
764 return Response(html)
764 return Response(html)
765
765
766
766
767 # :param key: name of the setting including the 'rhodecode_' prefix
767 # :param key: name of the setting including the 'rhodecode_' prefix
768 # :param type: the RhodeCodeSetting type to use.
768 # :param type: the RhodeCodeSetting type to use.
769 # :param group: the i18ned group in which we should dispaly this setting
769 # :param group: the i18ned group in which we should dispaly this setting
770 # :param label: the i18ned label we should display for this setting
770 # :param label: the i18ned label we should display for this setting
771 # :param help: the i18ned help we should dispaly for this setting
771 # :param help: the i18ned help we should dispaly for this setting
772 LabSetting = collections.namedtuple(
772 LabSetting = collections.namedtuple(
773 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
773 'LabSetting', ('key', 'type', 'group', 'label', 'help'))
774
774
775
775
776 # This list has to be kept in sync with the form
776 # This list has to be kept in sync with the form
777 # rhodecode.model.forms.LabsSettingsForm.
777 # rhodecode.model.forms.LabsSettingsForm.
778 _LAB_SETTINGS = [
778 _LAB_SETTINGS = [
779
779
780 ]
780 ]
@@ -1,462 +1,534 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 import re
21 import re
22 import logging
22 import logging
23 import collections
23 import collections
24
24
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode.apps._base import BaseAppView
27 from rhodecode.apps._base import BaseAppView
28 from rhodecode.lib import helpers as h
28 from rhodecode.lib import helpers as h
29 from rhodecode.lib.auth import (
29 from rhodecode.lib.auth import (
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator,
30 LoginRequired, NotAnonymous, HasRepoGroupPermissionAnyDecorator,
31 CSRFRequired)
31 CSRFRequired)
32 from rhodecode.lib.index import searcher_from_config
32 from rhodecode.lib.index import searcher_from_config
33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
33 from rhodecode.lib.utils2 import safe_unicode, str2bool, safe_int
34 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
35 from rhodecode.model.db import (
35 from rhodecode.model.db import (
36 func, true, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
36 func, true, or_, in_filter_generator, Repository, RepoGroup, User, UserGroup)
37 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.repo_group import RepoGroupModel
38 from rhodecode.model.repo_group import RepoGroupModel
39 from rhodecode.model.scm import RepoGroupList, RepoList
39 from rhodecode.model.scm import RepoGroupList, RepoList
40 from rhodecode.model.user import UserModel
40 from rhodecode.model.user import UserModel
41 from rhodecode.model.user_group import UserGroupModel
41 from rhodecode.model.user_group import UserGroupModel
42
42
43 log = logging.getLogger(__name__)
43 log = logging.getLogger(__name__)
44
44
45
45
46 class HomeView(BaseAppView):
46 class HomeView(BaseAppView):
47
47
48 def load_default_context(self):
48 def load_default_context(self):
49 c = self._get_local_tmpl_context()
49 c = self._get_local_tmpl_context()
50 c.user = c.auth_user.get_instance()
50 c.user = c.auth_user.get_instance()
51
51
52 return c
52 return c
53
53
54 @LoginRequired()
54 @LoginRequired()
55 @view_config(
55 @view_config(
56 route_name='user_autocomplete_data', request_method='GET',
56 route_name='user_autocomplete_data', request_method='GET',
57 renderer='json_ext', xhr=True)
57 renderer='json_ext', xhr=True)
58 def user_autocomplete_data(self):
58 def user_autocomplete_data(self):
59 self.load_default_context()
59 self.load_default_context()
60 query = self.request.GET.get('query')
60 query = self.request.GET.get('query')
61 active = str2bool(self.request.GET.get('active') or True)
61 active = str2bool(self.request.GET.get('active') or True)
62 include_groups = str2bool(self.request.GET.get('user_groups'))
62 include_groups = str2bool(self.request.GET.get('user_groups'))
63 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
63 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
64 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
64 skip_default_user = str2bool(self.request.GET.get('skip_default_user'))
65
65
66 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
66 log.debug('generating user list, query:%s, active:%s, with_groups:%s',
67 query, active, include_groups)
67 query, active, include_groups)
68
68
69 _users = UserModel().get_users(
69 _users = UserModel().get_users(
70 name_contains=query, only_active=active)
70 name_contains=query, only_active=active)
71
71
72 def maybe_skip_default_user(usr):
72 def maybe_skip_default_user(usr):
73 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
73 if skip_default_user and usr['username'] == UserModel.cls.DEFAULT_USER:
74 return False
74 return False
75 return True
75 return True
76 _users = filter(maybe_skip_default_user, _users)
76 _users = filter(maybe_skip_default_user, _users)
77
77
78 if include_groups:
78 if include_groups:
79 # extend with user groups
79 # extend with user groups
80 _user_groups = UserGroupModel().get_user_groups(
80 _user_groups = UserGroupModel().get_user_groups(
81 name_contains=query, only_active=active,
81 name_contains=query, only_active=active,
82 expand_groups=expand_groups)
82 expand_groups=expand_groups)
83 _users = _users + _user_groups
83 _users = _users + _user_groups
84
84
85 return {'suggestions': _users}
85 return {'suggestions': _users}
86
86
87 @LoginRequired()
87 @LoginRequired()
88 @NotAnonymous()
88 @NotAnonymous()
89 @view_config(
89 @view_config(
90 route_name='user_group_autocomplete_data', request_method='GET',
90 route_name='user_group_autocomplete_data', request_method='GET',
91 renderer='json_ext', xhr=True)
91 renderer='json_ext', xhr=True)
92 def user_group_autocomplete_data(self):
92 def user_group_autocomplete_data(self):
93 self.load_default_context()
93 self.load_default_context()
94 query = self.request.GET.get('query')
94 query = self.request.GET.get('query')
95 active = str2bool(self.request.GET.get('active') or True)
95 active = str2bool(self.request.GET.get('active') or True)
96 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
96 expand_groups = str2bool(self.request.GET.get('user_groups_expand'))
97
97
98 log.debug('generating user group list, query:%s, active:%s',
98 log.debug('generating user group list, query:%s, active:%s',
99 query, active)
99 query, active)
100
100
101 _user_groups = UserGroupModel().get_user_groups(
101 _user_groups = UserGroupModel().get_user_groups(
102 name_contains=query, only_active=active,
102 name_contains=query, only_active=active,
103 expand_groups=expand_groups)
103 expand_groups=expand_groups)
104 _user_groups = _user_groups
104 _user_groups = _user_groups
105
105
106 return {'suggestions': _user_groups}
106 return {'suggestions': _user_groups}
107
107
108 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
108 def _get_repo_list(self, name_contains=None, repo_type=None, limit=20):
109 org_query = name_contains
109 org_query = name_contains
110 allowed_ids = self._rhodecode_user.repo_acl_ids(
110 allowed_ids = self._rhodecode_user.repo_acl_ids(
111 ['repository.read', 'repository.write', 'repository.admin'],
111 ['repository.read', 'repository.write', 'repository.admin'],
112 cache=False, name_filter=name_contains) or [-1]
112 cache=False, name_filter=name_contains) or [-1]
113
113
114 query = Repository.query()\
114 query = Repository.query()\
115 .order_by(func.length(Repository.repo_name))\
115 .order_by(func.length(Repository.repo_name))\
116 .order_by(Repository.repo_name)\
116 .order_by(Repository.repo_name)\
117 .filter(Repository.archived.isnot(true()))\
117 .filter(Repository.archived.isnot(true()))\
118 .filter(or_(
118 .filter(or_(
119 # generate multiple IN to fix limitation problems
119 # generate multiple IN to fix limitation problems
120 *in_filter_generator(Repository.repo_id, allowed_ids)
120 *in_filter_generator(Repository.repo_id, allowed_ids)
121 ))
121 ))
122
122
123 if repo_type:
123 if repo_type:
124 query = query.filter(Repository.repo_type == repo_type)
124 query = query.filter(Repository.repo_type == repo_type)
125
125
126 if name_contains:
126 if name_contains:
127 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
127 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
128 query = query.filter(
128 query = query.filter(
129 Repository.repo_name.ilike(ilike_expression))
129 Repository.repo_name.ilike(ilike_expression))
130 query = query.limit(limit)
130 query = query.limit(limit)
131
131
132 acl_iter = query
132 acl_iter = query
133
133
134 return [
134 return [
135 {
135 {
136 'id': obj.repo_name,
136 'id': obj.repo_name,
137 'value': org_query,
137 'value': org_query,
138 'value_display': obj.repo_name,
138 'value_display': obj.repo_name,
139 'text': obj.repo_name,
139 'text': obj.repo_name,
140 'type': 'repo',
140 'type': 'repo',
141 'repo_id': obj.repo_id,
141 'repo_id': obj.repo_id,
142 'repo_type': obj.repo_type,
142 'repo_type': obj.repo_type,
143 'private': obj.private,
143 'private': obj.private,
144 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
144 'url': h.route_path('repo_summary', repo_name=obj.repo_name)
145 }
145 }
146 for obj in acl_iter]
146 for obj in acl_iter]
147
147
148 def _get_repo_group_list(self, name_contains=None, limit=20):
148 def _get_repo_group_list(self, name_contains=None, limit=20):
149 org_query = name_contains
149 org_query = name_contains
150 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
150 allowed_ids = self._rhodecode_user.repo_group_acl_ids(
151 ['group.read', 'group.write', 'group.admin'],
151 ['group.read', 'group.write', 'group.admin'],
152 cache=False, name_filter=name_contains) or [-1]
152 cache=False, name_filter=name_contains) or [-1]
153
153
154 query = RepoGroup.query()\
154 query = RepoGroup.query()\
155 .order_by(func.length(RepoGroup.group_name))\
155 .order_by(func.length(RepoGroup.group_name))\
156 .order_by(RepoGroup.group_name) \
156 .order_by(RepoGroup.group_name) \
157 .filter(or_(
157 .filter(or_(
158 # generate multiple IN to fix limitation problems
158 # generate multiple IN to fix limitation problems
159 *in_filter_generator(RepoGroup.group_id, allowed_ids)
159 *in_filter_generator(RepoGroup.group_id, allowed_ids)
160 ))
160 ))
161
161
162 if name_contains:
162 if name_contains:
163 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
163 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
164 query = query.filter(
164 query = query.filter(
165 RepoGroup.group_name.ilike(ilike_expression))
165 RepoGroup.group_name.ilike(ilike_expression))
166 query = query.limit(limit)
166 query = query.limit(limit)
167
167
168 acl_iter = query
168 acl_iter = query
169
169
170 return [
170 return [
171 {
171 {
172 'id': obj.group_name,
172 'id': obj.group_name,
173 'value': org_query,
173 'value': org_query,
174 'value_display': obj.group_name,
174 'value_display': obj.group_name,
175 'type': 'repo_group',
175 'type': 'repo_group',
176 'url': h.route_path(
176 'url': h.route_path(
177 'repo_group_home', repo_group_name=obj.group_name)
177 'repo_group_home', repo_group_name=obj.group_name)
178 }
178 }
179 for obj in acl_iter]
179 for obj in acl_iter]
180
180
181 def _get_user_list(self, name_contains=None, limit=20):
181 def _get_user_list(self, name_contains=None, limit=20):
182 org_query = name_contains
182 org_query = name_contains
183 if not name_contains:
183 if not name_contains:
184 return []
184 return []
185
185
186 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
186 name_contains = re.compile('(?:user:)(.+)').findall(name_contains)
187 if len(name_contains) != 1:
187 if len(name_contains) != 1:
188 return []
188 return []
189 name_contains = name_contains[0]
189 name_contains = name_contains[0]
190
190
191 query = User.query()\
191 query = User.query()\
192 .order_by(func.length(User.username))\
192 .order_by(func.length(User.username))\
193 .order_by(User.username) \
193 .order_by(User.username) \
194 .filter(User.username != User.DEFAULT_USER)
194 .filter(User.username != User.DEFAULT_USER)
195
195
196 if name_contains:
196 if name_contains:
197 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
197 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
198 query = query.filter(
198 query = query.filter(
199 User.username.ilike(ilike_expression))
199 User.username.ilike(ilike_expression))
200 query = query.limit(limit)
200 query = query.limit(limit)
201
201
202 acl_iter = query
202 acl_iter = query
203
203
204 return [
204 return [
205 {
205 {
206 'id': obj.user_id,
206 'id': obj.user_id,
207 'value': org_query,
207 'value': org_query,
208 'value_display': obj.username,
208 'value_display': obj.username,
209 'type': 'user',
209 'type': 'user',
210 'icon_link': h.gravatar_url(obj.email, 30),
210 'icon_link': h.gravatar_url(obj.email, 30),
211 'url': h.route_path(
211 'url': h.route_path(
212 'user_profile', username=obj.username)
212 'user_profile', username=obj.username)
213 }
213 }
214 for obj in acl_iter]
214 for obj in acl_iter]
215
215
216 def _get_user_groups_list(self, name_contains=None, limit=20):
216 def _get_user_groups_list(self, name_contains=None, limit=20):
217 org_query = name_contains
217 org_query = name_contains
218 if not name_contains:
218 if not name_contains:
219 return []
219 return []
220
220
221 name_contains = re.compile('(?:user_group:)(.+)').findall(name_contains)
221 name_contains = re.compile('(?:user_group:)(.+)').findall(name_contains)
222 if len(name_contains) != 1:
222 if len(name_contains) != 1:
223 return []
223 return []
224 name_contains = name_contains[0]
224 name_contains = name_contains[0]
225
225
226 query = UserGroup.query()\
226 query = UserGroup.query()\
227 .order_by(func.length(UserGroup.users_group_name))\
227 .order_by(func.length(UserGroup.users_group_name))\
228 .order_by(UserGroup.users_group_name)
228 .order_by(UserGroup.users_group_name)
229
229
230 if name_contains:
230 if name_contains:
231 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
231 ilike_expression = u'%{}%'.format(safe_unicode(name_contains))
232 query = query.filter(
232 query = query.filter(
233 UserGroup.users_group_name.ilike(ilike_expression))
233 UserGroup.users_group_name.ilike(ilike_expression))
234 query = query.limit(limit)
234 query = query.limit(limit)
235
235
236 acl_iter = query
236 acl_iter = query
237
237
238 return [
238 return [
239 {
239 {
240 'id': obj.users_group_id,
240 'id': obj.users_group_id,
241 'value': org_query,
241 'value': org_query,
242 'value_display': obj.users_group_name,
242 'value_display': obj.users_group_name,
243 'type': 'user_group',
243 'type': 'user_group',
244 'url': h.route_path(
244 'url': h.route_path(
245 'user_group_profile', user_group_name=obj.users_group_name)
245 'user_group_profile', user_group_name=obj.users_group_name)
246 }
246 }
247 for obj in acl_iter]
247 for obj in acl_iter]
248
248
249 def _get_hash_commit_list(self, auth_user, query):
249 def _get_hash_commit_list(self, auth_user, searcher, query):
250 org_query = query
250 org_query = query
251 if not query or len(query) < 3:
251 if not query or len(query) < 3 or not searcher:
252 return []
252 return []
253
253
254 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
254 commit_hashes = re.compile('(?:commit:)([0-9a-f]{2,40})').findall(query)
255
255
256 if len(commit_hashes) != 1:
256 if len(commit_hashes) != 1:
257 return []
257 return []
258 commit_hash = commit_hashes[0]
258 commit_hash = commit_hashes[0]
259
259
260 searcher = searcher_from_config(self.request.registry.settings)
261 result = searcher.search(
260 result = searcher.search(
262 'commit_id:%s*' % commit_hash, 'commit', auth_user,
261 'commit_id:{}*'.format(commit_hash), 'commit', auth_user,
263 raise_on_exc=False)
262 raise_on_exc=False)
264
263
265 return [
264 return [
266 {
265 {
267 'id': entry['commit_id'],
266 'id': entry['commit_id'],
268 'value': org_query,
267 'value': org_query,
269 'value_display': 'repo `{}` commit: {}'.format(
268 'value_display': 'repo `{}` commit: {}'.format(
270 entry['repository'], entry['commit_id']),
269 entry['repository'], entry['commit_id']),
271 'type': 'commit',
270 'type': 'commit',
272 'repo': entry['repository'],
271 'repo': entry['repository'],
273 'url': h.route_path(
272 'url': h.route_path(
274 'repo_commit',
273 'repo_commit',
275 repo_name=entry['repository'], commit_id=entry['commit_id'])
274 repo_name=entry['repository'], commit_id=entry['commit_id'])
276 }
275 }
277 for entry in result['results']]
276 for entry in result['results']]
278
277
279 @LoginRequired()
278 @LoginRequired()
280 @view_config(
279 @view_config(
281 route_name='repo_list_data', request_method='GET',
280 route_name='repo_list_data', request_method='GET',
282 renderer='json_ext', xhr=True)
281 renderer='json_ext', xhr=True)
283 def repo_list_data(self):
282 def repo_list_data(self):
284 _ = self.request.translate
283 _ = self.request.translate
285 self.load_default_context()
284 self.load_default_context()
286
285
287 query = self.request.GET.get('query')
286 query = self.request.GET.get('query')
288 repo_type = self.request.GET.get('repo_type')
287 repo_type = self.request.GET.get('repo_type')
289 log.debug('generating repo list, query:%s, repo_type:%s',
288 log.debug('generating repo list, query:%s, repo_type:%s',
290 query, repo_type)
289 query, repo_type)
291
290
292 res = []
291 res = []
293 repos = self._get_repo_list(query, repo_type=repo_type)
292 repos = self._get_repo_list(query, repo_type=repo_type)
294 if repos:
293 if repos:
295 res.append({
294 res.append({
296 'text': _('Repositories'),
295 'text': _('Repositories'),
297 'children': repos
296 'children': repos
298 })
297 })
299
298
300 data = {
299 data = {
301 'more': False,
300 'more': False,
302 'results': res
301 'results': res
303 }
302 }
304 return data
303 return data
305
304
305 def _get_default_search_queries(self, search_context, searcher, query):
306 if not searcher:
307 return []
308 is_es_6 = searcher.is_es_6
309
310 queries = []
311 repo_group_name, repo_name, repo_context = None, None, None
312
313 # repo group context
314 if search_context.get('search_context[repo_group_name]'):
315 repo_group_name = search_context.get('search_context[repo_group_name]')
316 if search_context.get('search_context[repo_name]'):
317 repo_name = search_context.get('search_context[repo_name]')
318 repo_context = search_context.get('search_context[repo_view_type]')
319
320 if is_es_6 and repo_name:
321 def query_modifier():
322 qry = '{} repo_name.raw:{} '.format(
323 query, searcher.escape_specials(repo_name))
324 return {'q': qry, 'type': 'content'}
325 label = u'Search for `{}` through files in this repository.'.format(query)
326 queries.append(
327 {
328 'id': -10,
329 'value': query,
330 'value_display': label,
331 'type': 'search',
332 'url': h.route_path(
333 'search_repo', repo_name=repo_name, _query=query_modifier())
334 }
335 )
336
337 def query_modifier():
338 qry = '{} repo_name.raw:{} '.format(
339 query, searcher.escape_specials(repo_name))
340 return {'q': qry, 'type': 'commit'}
341 label = u'Search for `{}` through commits in this repository.'.format(query)
342 queries.append(
343 {
344 'id': -10,
345 'value': query,
346 'value_display': label,
347 'type': 'search',
348 'url': h.route_path(
349 'search_repo', repo_name=repo_name, _query=query_modifier())
350 }
351 )
352
353 elif is_es_6 and repo_group_name:
354 def query_modifier():
355 qry = '{} repo_name.raw:{} '.format(
356 query, searcher.escape_specials(repo_group_name + '/*'))
357 return {'q': qry, 'type': 'content'}
358 label = u'Search for `{}` through files in this repository group'.format(query)
359 queries.append(
360 {
361 'id': -20,
362 'value': query,
363 'value_display': label,
364 'type': 'search',
365 'url': h.route_path('search', _query=query_modifier())
366 }
367 )
368
369 if not queries:
370 queries.append(
371 {
372 'id': -1,
373 'value': query,
374 'value_display': u'Search for: `{}`'.format(query),
375 'type': 'search',
376 'url': h.route_path('search',
377 _query={'q': query, 'type': 'content'})
378 }
379 )
380
381 return queries
382
306 @LoginRequired()
383 @LoginRequired()
307 @view_config(
384 @view_config(
308 route_name='goto_switcher_data', request_method='GET',
385 route_name='goto_switcher_data', request_method='GET',
309 renderer='json_ext', xhr=True)
386 renderer='json_ext', xhr=True)
310 def goto_switcher_data(self):
387 def goto_switcher_data(self):
311 c = self.load_default_context()
388 c = self.load_default_context()
312
389
313 _ = self.request.translate
390 _ = self.request.translate
314
391
315 query = self.request.GET.get('query')
392 query = self.request.GET.get('query')
316 log.debug('generating main filter data, query %s', query)
393 log.debug('generating main filter data, query %s', query)
317
394
318 default_search_val = u'Full text search for: `{}`'.format(query)
319 res = []
395 res = []
320 if not query:
396 if not query:
321 return {'suggestions': res}
397 return {'suggestions': res}
322
398
323 res.append({
399 searcher = searcher_from_config(self.request.registry.settings)
324 'id': -1,
400 for _q in self._get_default_search_queries(self.request.GET, searcher, query):
325 'value': query,
401 res.append(_q)
326 'value_display': default_search_val,
402
327 'type': 'search',
403 repo_group_id = safe_int(self.request.GET.get('search_context[repo_group_id]'))
328 'url': h.route_path(
329 'search', _query={'q': query})
330 })
331 repo_group_id = safe_int(self.request.GET.get('repo_group_id'))
332 if repo_group_id:
404 if repo_group_id:
333 repo_group = RepoGroup.get(repo_group_id)
405 repo_group = RepoGroup.get(repo_group_id)
334 composed_hint = '{}/{}'.format(repo_group.group_name, query)
406 composed_hint = '{}/{}'.format(repo_group.group_name, query)
335 show_hint = not query.startswith(repo_group.group_name)
407 show_hint = not query.startswith(repo_group.group_name)
336 if repo_group and show_hint:
408 if repo_group and show_hint:
337 hint = u'Group search: `{}`'.format(composed_hint)
409 hint = u'Repository search inside: `{}`'.format(composed_hint)
338 res.append({
410 res.append({
339 'id': -1,
411 'id': -1,
340 'value': composed_hint,
412 'value': composed_hint,
341 'value_display': hint,
413 'value_display': hint,
342 'type': 'hint',
414 'type': 'hint',
343 'url': ""
415 'url': ""
344 })
416 })
345
417
346 repo_groups = self._get_repo_group_list(query)
418 repo_groups = self._get_repo_group_list(query)
347 for serialized_repo_group in repo_groups:
419 for serialized_repo_group in repo_groups:
348 res.append(serialized_repo_group)
420 res.append(serialized_repo_group)
349
421
350 repos = self._get_repo_list(query)
422 repos = self._get_repo_list(query)
351 for serialized_repo in repos:
423 for serialized_repo in repos:
352 res.append(serialized_repo)
424 res.append(serialized_repo)
353
425
354 # TODO(marcink): permissions for that ?
426 # TODO(marcink): should all logged in users be allowed to search others?
355 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
427 allowed_user_search = self._rhodecode_user.username != User.DEFAULT_USER
356 if allowed_user_search:
428 if allowed_user_search:
357 users = self._get_user_list(query)
429 users = self._get_user_list(query)
358 for serialized_user in users:
430 for serialized_user in users:
359 res.append(serialized_user)
431 res.append(serialized_user)
360
432
361 user_groups = self._get_user_groups_list(query)
433 user_groups = self._get_user_groups_list(query)
362 for serialized_user_group in user_groups:
434 for serialized_user_group in user_groups:
363 res.append(serialized_user_group)
435 res.append(serialized_user_group)
364
436
365 commits = self._get_hash_commit_list(c.auth_user, query)
437 commits = self._get_hash_commit_list(c.auth_user, searcher, query)
366 if commits:
438 if commits:
367 unique_repos = collections.OrderedDict()
439 unique_repos = collections.OrderedDict()
368 for commit in commits:
440 for commit in commits:
369 repo_name = commit['repo']
441 repo_name = commit['repo']
370 unique_repos.setdefault(repo_name, []).append(commit)
442 unique_repos.setdefault(repo_name, []).append(commit)
371
443
372 for repo, commits in unique_repos.items():
444 for repo, commits in unique_repos.items():
373 for commit in commits:
445 for commit in commits:
374 res.append(commit)
446 res.append(commit)
375
447
376 return {'suggestions': res}
448 return {'suggestions': res}
377
449
378 def _get_groups_and_repos(self, repo_group_id=None):
450 def _get_groups_and_repos(self, repo_group_id=None):
379 # repo groups groups
451 # repo groups groups
380 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
452 repo_group_list = RepoGroup.get_all_repo_groups(group_id=repo_group_id)
381 _perms = ['group.read', 'group.write', 'group.admin']
453 _perms = ['group.read', 'group.write', 'group.admin']
382 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
454 repo_group_list_acl = RepoGroupList(repo_group_list, perm_set=_perms)
383 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
455 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
384 repo_group_list=repo_group_list_acl, admin=False)
456 repo_group_list=repo_group_list_acl, admin=False)
385
457
386 # repositories
458 # repositories
387 repo_list = Repository.get_all_repos(group_id=repo_group_id)
459 repo_list = Repository.get_all_repos(group_id=repo_group_id)
388 _perms = ['repository.read', 'repository.write', 'repository.admin']
460 _perms = ['repository.read', 'repository.write', 'repository.admin']
389 repo_list_acl = RepoList(repo_list, perm_set=_perms)
461 repo_list_acl = RepoList(repo_list, perm_set=_perms)
390 repo_data = RepoModel().get_repos_as_dict(
462 repo_data = RepoModel().get_repos_as_dict(
391 repo_list=repo_list_acl, admin=False)
463 repo_list=repo_list_acl, admin=False)
392
464
393 return repo_data, repo_group_data
465 return repo_data, repo_group_data
394
466
395 @LoginRequired()
467 @LoginRequired()
396 @view_config(
468 @view_config(
397 route_name='home', request_method='GET',
469 route_name='home', request_method='GET',
398 renderer='rhodecode:templates/index.mako')
470 renderer='rhodecode:templates/index.mako')
399 def main_page(self):
471 def main_page(self):
400 c = self.load_default_context()
472 c = self.load_default_context()
401 c.repo_group = None
473 c.repo_group = None
402
474
403 repo_data, repo_group_data = self._get_groups_and_repos()
475 repo_data, repo_group_data = self._get_groups_and_repos()
404 # json used to render the grids
476 # json used to render the grids
405 c.repos_data = json.dumps(repo_data)
477 c.repos_data = json.dumps(repo_data)
406 c.repo_groups_data = json.dumps(repo_group_data)
478 c.repo_groups_data = json.dumps(repo_group_data)
407
479
408 return self._get_template_context(c)
480 return self._get_template_context(c)
409
481
410 @LoginRequired()
482 @LoginRequired()
411 @HasRepoGroupPermissionAnyDecorator(
483 @HasRepoGroupPermissionAnyDecorator(
412 'group.read', 'group.write', 'group.admin')
484 'group.read', 'group.write', 'group.admin')
413 @view_config(
485 @view_config(
414 route_name='repo_group_home', request_method='GET',
486 route_name='repo_group_home', request_method='GET',
415 renderer='rhodecode:templates/index_repo_group.mako')
487 renderer='rhodecode:templates/index_repo_group.mako')
416 @view_config(
488 @view_config(
417 route_name='repo_group_home_slash', request_method='GET',
489 route_name='repo_group_home_slash', request_method='GET',
418 renderer='rhodecode:templates/index_repo_group.mako')
490 renderer='rhodecode:templates/index_repo_group.mako')
419 def repo_group_main_page(self):
491 def repo_group_main_page(self):
420 c = self.load_default_context()
492 c = self.load_default_context()
421 c.repo_group = self.request.db_repo_group
493 c.repo_group = self.request.db_repo_group
422 repo_data, repo_group_data = self._get_groups_and_repos(
494 repo_data, repo_group_data = self._get_groups_and_repos(
423 c.repo_group.group_id)
495 c.repo_group.group_id)
424
496
425 # json used to render the grids
497 # json used to render the grids
426 c.repos_data = json.dumps(repo_data)
498 c.repos_data = json.dumps(repo_data)
427 c.repo_groups_data = json.dumps(repo_group_data)
499 c.repo_groups_data = json.dumps(repo_group_data)
428
500
429 return self._get_template_context(c)
501 return self._get_template_context(c)
430
502
431 @LoginRequired()
503 @LoginRequired()
432 @CSRFRequired()
504 @CSRFRequired()
433 @view_config(
505 @view_config(
434 route_name='markup_preview', request_method='POST',
506 route_name='markup_preview', request_method='POST',
435 renderer='string', xhr=True)
507 renderer='string', xhr=True)
436 def markup_preview(self):
508 def markup_preview(self):
437 # Technically a CSRF token is not needed as no state changes with this
509 # Technically a CSRF token is not needed as no state changes with this
438 # call. However, as this is a POST is better to have it, so automated
510 # call. However, as this is a POST is better to have it, so automated
439 # tools don't flag it as potential CSRF.
511 # tools don't flag it as potential CSRF.
440 # Post is required because the payload could be bigger than the maximum
512 # Post is required because the payload could be bigger than the maximum
441 # allowed by GET.
513 # allowed by GET.
442
514
443 text = self.request.POST.get('text')
515 text = self.request.POST.get('text')
444 renderer = self.request.POST.get('renderer') or 'rst'
516 renderer = self.request.POST.get('renderer') or 'rst'
445 if text:
517 if text:
446 return h.render(text, renderer=renderer, mentions=True)
518 return h.render(text, renderer=renderer, mentions=True)
447 return ''
519 return ''
448
520
449 @LoginRequired()
521 @LoginRequired()
450 @CSRFRequired()
522 @CSRFRequired()
451 @view_config(
523 @view_config(
452 route_name='store_user_session_value', request_method='POST',
524 route_name='store_user_session_value', request_method='POST',
453 renderer='string', xhr=True)
525 renderer='string', xhr=True)
454 def store_user_session_attr(self):
526 def store_user_session_attr(self):
455 key = self.request.POST.get('key')
527 key = self.request.POST.get('key')
456 val = self.request.POST.get('val')
528 val = self.request.POST.get('val')
457
529
458 existing_value = self.request.session.get(key)
530 existing_value = self.request.session.get(key)
459 if existing_value != val:
531 if existing_value != val:
460 self.request.session[key] = val
532 self.request.session[key] = val
461
533
462 return 'stored:{}'.format(key)
534 return 'stored:{}'.format(key)
@@ -1,134 +1,138 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2018 RhodeCode GmbH
3 # Copyright (C) 2011-2018 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 import logging
21 import logging
22 import urllib
22 import urllib
23 from pyramid.view import view_config
23 from pyramid.view import view_config
24 from webhelpers.util import update_params
24 from webhelpers.util import update_params
25
25
26 from rhodecode.apps._base import BaseAppView, RepoAppView
26 from rhodecode.apps._base import BaseAppView, RepoAppView
27 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator)
27 from rhodecode.lib.auth import (LoginRequired, HasRepoPermissionAnyDecorator)
28 from rhodecode.lib.helpers import Page
28 from rhodecode.lib.helpers import Page
29 from rhodecode.lib.utils2 import safe_str
29 from rhodecode.lib.utils2 import safe_str
30 from rhodecode.lib.index import searcher_from_config
30 from rhodecode.lib.index import searcher_from_config
31 from rhodecode.model import validation_schema
31 from rhodecode.model import validation_schema
32 from rhodecode.model.validation_schema.schemas import search_schema
32 from rhodecode.model.validation_schema.schemas import search_schema
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 def search(request, tmpl_context, repo_name):
37 def search(request, tmpl_context, repo_name):
38 searcher = searcher_from_config(request.registry.settings)
38 searcher = searcher_from_config(request.registry.settings)
39 formatted_results = []
39 formatted_results = []
40 execution_time = ''
40 execution_time = ''
41
41
42 schema = search_schema.SearchParamsSchema()
42 schema = search_schema.SearchParamsSchema()
43
43
44 search_params = {}
44 search_params = {}
45 errors = []
45 errors = []
46 try:
46 try:
47 search_params = schema.deserialize(
47 search_params = schema.deserialize(
48 dict(search_query=request.GET.get('q'),
48 dict(
49 search_type=request.GET.get('type'),
49 search_query=request.GET.get('q'),
50 search_sort=request.GET.get('sort'),
50 search_type=request.GET.get('type'),
51 page_limit=request.GET.get('page_limit'),
51 search_sort=request.GET.get('sort'),
52 requested_page=request.GET.get('page'))
52 search_max_lines=request.GET.get('max_lines'),
53 page_limit=request.GET.get('page_limit'),
54 requested_page=request.GET.get('page'),
55 )
53 )
56 )
54 except validation_schema.Invalid as e:
57 except validation_schema.Invalid as e:
55 errors = e.children
58 errors = e.children
56
59
57 def url_generator(**kw):
60 def url_generator(**kw):
58 q = urllib.quote(safe_str(search_query))
61 q = urllib.quote(safe_str(search_query))
59 return update_params(
62 return update_params(
60 "?q=%s&type=%s" % (q, safe_str(search_type)), **kw)
63 "?q=%s&type=%s&max_lines=%s" % (q, safe_str(search_type), search_max_lines), **kw)
61
64
62 c = tmpl_context
65 c = tmpl_context
63 search_query = search_params.get('search_query')
66 search_query = search_params.get('search_query')
64 search_type = search_params.get('search_type')
67 search_type = search_params.get('search_type')
65 search_sort = search_params.get('search_sort')
68 search_sort = search_params.get('search_sort')
69 search_max_lines = search_params.get('search_max_lines')
66 if search_params.get('search_query'):
70 if search_params.get('search_query'):
67 page_limit = search_params['page_limit']
71 page_limit = search_params['page_limit']
68 requested_page = search_params['requested_page']
72 requested_page = search_params['requested_page']
69
73
70 try:
74 try:
71 search_result = searcher.search(
75 search_result = searcher.search(
72 search_query, search_type, c.auth_user, repo_name,
76 search_query, search_type, c.auth_user, repo_name,
73 requested_page, page_limit, search_sort)
77 requested_page, page_limit, search_sort)
74
78
75 formatted_results = Page(
79 formatted_results = Page(
76 search_result['results'], page=requested_page,
80 search_result['results'], page=requested_page,
77 item_count=search_result['count'],
81 item_count=search_result['count'],
78 items_per_page=page_limit, url=url_generator)
82 items_per_page=page_limit, url=url_generator)
79 finally:
83 finally:
80 searcher.cleanup()
84 searcher.cleanup()
81
85
82 if not search_result['error']:
86 if not search_result['error']:
83 execution_time = '%s results (%.3f seconds)' % (
87 execution_time = '%s results (%.3f seconds)' % (
84 search_result['count'],
88 search_result['count'],
85 search_result['runtime'])
89 search_result['runtime'])
86 elif not errors:
90 elif not errors:
87 node = schema['search_query']
91 node = schema['search_query']
88 errors = [
92 errors = [
89 validation_schema.Invalid(node, search_result['error'])]
93 validation_schema.Invalid(node, search_result['error'])]
90
94
91 c.perm_user = c.auth_user
95 c.perm_user = c.auth_user
92 c.repo_name = repo_name
96 c.repo_name = repo_name
93 c.sort = search_sort
97 c.sort = search_sort
94 c.url_generator = url_generator
98 c.url_generator = url_generator
95 c.errors = errors
99 c.errors = errors
96 c.formatted_results = formatted_results
100 c.formatted_results = formatted_results
97 c.runtime = execution_time
101 c.runtime = execution_time
98 c.cur_query = search_query
102 c.cur_query = search_query
99 c.search_type = search_type
103 c.search_type = search_type
100 c.searcher = searcher
104 c.searcher = searcher
101
105
102
106
103 class SearchView(BaseAppView):
107 class SearchView(BaseAppView):
104 def load_default_context(self):
108 def load_default_context(self):
105 c = self._get_local_tmpl_context()
109 c = self._get_local_tmpl_context()
106
110
107 return c
111 return c
108
112
109 @LoginRequired()
113 @LoginRequired()
110 @view_config(
114 @view_config(
111 route_name='search', request_method='GET',
115 route_name='search', request_method='GET',
112 renderer='rhodecode:templates/search/search.mako')
116 renderer='rhodecode:templates/search/search.mako')
113 def search(self):
117 def search(self):
114 c = self.load_default_context()
118 c = self.load_default_context()
115 search(self.request, c, repo_name=None)
119 search(self.request, c, repo_name=None)
116 return self._get_template_context(c)
120 return self._get_template_context(c)
117
121
118
122
119 class SearchRepoView(RepoAppView):
123 class SearchRepoView(RepoAppView):
120 def load_default_context(self):
124 def load_default_context(self):
121 c = self._get_local_tmpl_context()
125 c = self._get_local_tmpl_context()
122
126
123 return c
127 return c
124
128
125 @LoginRequired()
129 @LoginRequired()
126 @HasRepoPermissionAnyDecorator(
130 @HasRepoPermissionAnyDecorator(
127 'repository.read', 'repository.write', 'repository.admin')
131 'repository.read', 'repository.write', 'repository.admin')
128 @view_config(
132 @view_config(
129 route_name='search_repo', request_method='GET',
133 route_name='search_repo', request_method='GET',
130 renderer='rhodecode:templates/search/search.mako')
134 renderer='rhodecode:templates/search/search.mako')
131 def search_repo(self):
135 def search_repo(self):
132 c = self.load_default_context()
136 c = self.load_default_context()
133 search(self.request, c, repo_name=self.db_repo_name)
137 search(self.request, c, repo_name=self.db_repo_name)
134 return self._get_template_context(c)
138 return self._get_template_context(c)
@@ -1,2109 +1,2017 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Helper functions
22 Helper functions
23
23
24 Consists of functions to typically be used within templates, but also
24 Consists of functions to typically be used within templates, but also
25 available to Controllers. This module is available to both as 'h'.
25 available to Controllers. This module is available to both as 'h'.
26 """
26 """
27
27
28 import os
28 import os
29 import random
29 import random
30 import hashlib
30 import hashlib
31 import StringIO
31 import StringIO
32 import textwrap
32 import textwrap
33 import urllib
33 import urllib
34 import math
34 import math
35 import logging
35 import logging
36 import re
36 import re
37 import urlparse
37 import urlparse
38 import time
38 import time
39 import string
39 import string
40 import hashlib
40 import hashlib
41 from collections import OrderedDict
41 from collections import OrderedDict
42
42
43 import pygments
43 import pygments
44 import itertools
44 import itertools
45 import fnmatch
45 import fnmatch
46 import bleach
46 import bleach
47
47
48 from datetime import datetime
48 from datetime import datetime
49 from functools import partial
49 from functools import partial
50 from pygments.formatters.html import HtmlFormatter
50 from pygments.formatters.html import HtmlFormatter
51 from pygments import highlight as code_highlight
52 from pygments.lexers import (
51 from pygments.lexers import (
53 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
52 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
54
53
55 from pyramid.threadlocal import get_current_request
54 from pyramid.threadlocal import get_current_request
56
55
57 from webhelpers.html import literal, HTML, escape
56 from webhelpers.html import literal, HTML, escape
58 from webhelpers.html.tools import *
57 from webhelpers.html.tools import *
59 from webhelpers.html.builder import make_tag
58 from webhelpers.html.builder import make_tag
60 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
59 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
61 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
60 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
62 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
61 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
63 submit, text, password, textarea, title, ul, xml_declaration, radio
62 submit, text, password, textarea, title, ul, xml_declaration, radio
64 from webhelpers.html.tools import auto_link, button_to, highlight, \
63 from webhelpers.html.tools import auto_link, button_to, highlight, \
65 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
64 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
66 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
65 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
67 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
66 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
68 replace_whitespace, urlify, truncate, wrap_paragraphs
67 replace_whitespace, urlify, truncate, wrap_paragraphs
69 from webhelpers.date import time_ago_in_words
68 from webhelpers.date import time_ago_in_words
70 from webhelpers.paginate import Page as _Page
69 from webhelpers.paginate import Page as _Page
71 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
70 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
72 convert_boolean_attrs, NotGiven, _make_safe_id_component
71 convert_boolean_attrs, NotGiven, _make_safe_id_component
73 from webhelpers2.number import format_byte_size
72 from webhelpers2.number import format_byte_size
74
73
75 from rhodecode.lib.action_parser import action_parser
74 from rhodecode.lib.action_parser import action_parser
76 from rhodecode.lib.ext_json import json
75 from rhodecode.lib.ext_json import json
77 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
76 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
78 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
77 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
79 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
78 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
80 AttributeDict, safe_int, md5, md5_safe
79 AttributeDict, safe_int, md5, md5_safe
81 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
80 from rhodecode.lib.markup_renderer import MarkupRenderer, relative_links
82 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
81 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
83 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
82 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
83 from rhodecode.lib.index.search_utils import get_matching_line_offsets
84 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
84 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
85 from rhodecode.model.changeset_status import ChangesetStatusModel
85 from rhodecode.model.changeset_status import ChangesetStatusModel
86 from rhodecode.model.db import Permission, User, Repository
86 from rhodecode.model.db import Permission, User, Repository
87 from rhodecode.model.repo_group import RepoGroupModel
87 from rhodecode.model.repo_group import RepoGroupModel
88 from rhodecode.model.settings import IssueTrackerSettingsModel
88 from rhodecode.model.settings import IssueTrackerSettingsModel
89
89
90
90 log = logging.getLogger(__name__)
91 log = logging.getLogger(__name__)
91
92
92
93
93 DEFAULT_USER = User.DEFAULT_USER
94 DEFAULT_USER = User.DEFAULT_USER
94 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
95 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
95
96
96
97
97 def asset(path, ver=None, **kwargs):
98 def asset(path, ver=None, **kwargs):
98 """
99 """
99 Helper to generate a static asset file path for rhodecode assets
100 Helper to generate a static asset file path for rhodecode assets
100
101
101 eg. h.asset('images/image.png', ver='3923')
102 eg. h.asset('images/image.png', ver='3923')
102
103
103 :param path: path of asset
104 :param path: path of asset
104 :param ver: optional version query param to append as ?ver=
105 :param ver: optional version query param to append as ?ver=
105 """
106 """
106 request = get_current_request()
107 request = get_current_request()
107 query = {}
108 query = {}
108 query.update(kwargs)
109 query.update(kwargs)
109 if ver:
110 if ver:
110 query = {'ver': ver}
111 query = {'ver': ver}
111 return request.static_path(
112 return request.static_path(
112 'rhodecode:public/{}'.format(path), _query=query)
113 'rhodecode:public/{}'.format(path), _query=query)
113
114
114
115
115 default_html_escape_table = {
116 default_html_escape_table = {
116 ord('&'): u'&amp;',
117 ord('&'): u'&amp;',
117 ord('<'): u'&lt;',
118 ord('<'): u'&lt;',
118 ord('>'): u'&gt;',
119 ord('>'): u'&gt;',
119 ord('"'): u'&quot;',
120 ord('"'): u'&quot;',
120 ord("'"): u'&#39;',
121 ord("'"): u'&#39;',
121 }
122 }
122
123
123
124
124 def html_escape(text, html_escape_table=default_html_escape_table):
125 def html_escape(text, html_escape_table=default_html_escape_table):
125 """Produce entities within text."""
126 """Produce entities within text."""
126 return text.translate(html_escape_table)
127 return text.translate(html_escape_table)
127
128
128
129
129 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
130 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
130 """
131 """
131 Truncate string ``s`` at the first occurrence of ``sub``.
132 Truncate string ``s`` at the first occurrence of ``sub``.
132
133
133 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
134 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
134 """
135 """
135 suffix_if_chopped = suffix_if_chopped or ''
136 suffix_if_chopped = suffix_if_chopped or ''
136 pos = s.find(sub)
137 pos = s.find(sub)
137 if pos == -1:
138 if pos == -1:
138 return s
139 return s
139
140
140 if inclusive:
141 if inclusive:
141 pos += len(sub)
142 pos += len(sub)
142
143
143 chopped = s[:pos]
144 chopped = s[:pos]
144 left = s[pos:].strip()
145 left = s[pos:].strip()
145
146
146 if left and suffix_if_chopped:
147 if left and suffix_if_chopped:
147 chopped += suffix_if_chopped
148 chopped += suffix_if_chopped
148
149
149 return chopped
150 return chopped
150
151
151
152
152 def shorter(text, size=20):
153 def shorter(text, size=20):
153 postfix = '...'
154 postfix = '...'
154 if len(text) > size:
155 if len(text) > size:
155 return text[:size - len(postfix)] + postfix
156 return text[:size - len(postfix)] + postfix
156 return text
157 return text
157
158
158
159
159 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
160 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
160 """
161 """
161 Reset button
162 Reset button
162 """
163 """
163 _set_input_attrs(attrs, type, name, value)
164 _set_input_attrs(attrs, type, name, value)
164 _set_id_attr(attrs, id, name)
165 _set_id_attr(attrs, id, name)
165 convert_boolean_attrs(attrs, ["disabled"])
166 convert_boolean_attrs(attrs, ["disabled"])
166 return HTML.input(**attrs)
167 return HTML.input(**attrs)
167
168
168 reset = _reset
169 reset = _reset
169 safeid = _make_safe_id_component
170 safeid = _make_safe_id_component
170
171
171
172
172 def branding(name, length=40):
173 def branding(name, length=40):
173 return truncate(name, length, indicator="")
174 return truncate(name, length, indicator="")
174
175
175
176
176 def FID(raw_id, path):
177 def FID(raw_id, path):
177 """
178 """
178 Creates a unique ID for filenode based on it's hash of path and commit
179 Creates a unique ID for filenode based on it's hash of path and commit
179 it's safe to use in urls
180 it's safe to use in urls
180
181
181 :param raw_id:
182 :param raw_id:
182 :param path:
183 :param path:
183 """
184 """
184
185
185 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
186 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
186
187
187
188
188 class _GetError(object):
189 class _GetError(object):
189 """Get error from form_errors, and represent it as span wrapped error
190 """Get error from form_errors, and represent it as span wrapped error
190 message
191 message
191
192
192 :param field_name: field to fetch errors for
193 :param field_name: field to fetch errors for
193 :param form_errors: form errors dict
194 :param form_errors: form errors dict
194 """
195 """
195
196
196 def __call__(self, field_name, form_errors):
197 def __call__(self, field_name, form_errors):
197 tmpl = """<span class="error_msg">%s</span>"""
198 tmpl = """<span class="error_msg">%s</span>"""
198 if form_errors and field_name in form_errors:
199 if form_errors and field_name in form_errors:
199 return literal(tmpl % form_errors.get(field_name))
200 return literal(tmpl % form_errors.get(field_name))
200
201
201 get_error = _GetError()
202 get_error = _GetError()
202
203
203
204
204 class _ToolTip(object):
205 class _ToolTip(object):
205
206
206 def __call__(self, tooltip_title, trim_at=50):
207 def __call__(self, tooltip_title, trim_at=50):
207 """
208 """
208 Special function just to wrap our text into nice formatted
209 Special function just to wrap our text into nice formatted
209 autowrapped text
210 autowrapped text
210
211
211 :param tooltip_title:
212 :param tooltip_title:
212 """
213 """
213 tooltip_title = escape(tooltip_title)
214 tooltip_title = escape(tooltip_title)
214 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
215 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
215 return tooltip_title
216 return tooltip_title
216 tooltip = _ToolTip()
217 tooltip = _ToolTip()
217
218
218
219
219 def files_breadcrumbs(repo_name, commit_id, file_path):
220 def files_breadcrumbs(repo_name, commit_id, file_path):
220 if isinstance(file_path, str):
221 if isinstance(file_path, str):
221 file_path = safe_unicode(file_path)
222 file_path = safe_unicode(file_path)
222
223
223 # TODO: johbo: Is this always a url like path, or is this operating
224 # TODO: johbo: Is this always a url like path, or is this operating
224 # system dependent?
225 # system dependent?
225 path_segments = file_path.split('/')
226 path_segments = file_path.split('/')
226
227
227 repo_name_html = escape(repo_name)
228 repo_name_html = escape(repo_name)
228 if len(path_segments) == 1 and path_segments[0] == '':
229 if len(path_segments) == 1 and path_segments[0] == '':
229 url_segments = [repo_name_html]
230 url_segments = [repo_name_html]
230 else:
231 else:
231 url_segments = [
232 url_segments = [
232 link_to(
233 link_to(
233 repo_name_html,
234 repo_name_html,
234 route_path(
235 route_path(
235 'repo_files',
236 'repo_files',
236 repo_name=repo_name,
237 repo_name=repo_name,
237 commit_id=commit_id,
238 commit_id=commit_id,
238 f_path=''),
239 f_path=''),
239 class_='pjax-link')]
240 class_='pjax-link')]
240
241
241 last_cnt = len(path_segments) - 1
242 last_cnt = len(path_segments) - 1
242 for cnt, segment in enumerate(path_segments):
243 for cnt, segment in enumerate(path_segments):
243 if not segment:
244 if not segment:
244 continue
245 continue
245 segment_html = escape(segment)
246 segment_html = escape(segment)
246
247
247 if cnt != last_cnt:
248 if cnt != last_cnt:
248 url_segments.append(
249 url_segments.append(
249 link_to(
250 link_to(
250 segment_html,
251 segment_html,
251 route_path(
252 route_path(
252 'repo_files',
253 'repo_files',
253 repo_name=repo_name,
254 repo_name=repo_name,
254 commit_id=commit_id,
255 commit_id=commit_id,
255 f_path='/'.join(path_segments[:cnt + 1])),
256 f_path='/'.join(path_segments[:cnt + 1])),
256 class_='pjax-link'))
257 class_='pjax-link'))
257 else:
258 else:
258 url_segments.append(segment_html)
259 url_segments.append(segment_html)
259
260
260 return literal('/'.join(url_segments))
261 return literal('/'.join(url_segments))
261
262
262
263
264 def code_highlight(code, lexer, formatter, use_hl_filter=False):
265 """
266 Lex ``code`` with ``lexer`` and format it with the formatter ``formatter``.
267
268 If ``outfile`` is given and a valid file object (an object
269 with a ``write`` method), the result will be written to it, otherwise
270 it is returned as a string.
271 """
272 if use_hl_filter:
273 # add HL filter
274 from rhodecode.lib.index import search_utils
275 lexer.add_filter(search_utils.ElasticSearchHLFilter())
276 return pygments.format(pygments.lex(code, lexer), formatter)
277
278
263 class CodeHtmlFormatter(HtmlFormatter):
279 class CodeHtmlFormatter(HtmlFormatter):
264 """
280 """
265 My code Html Formatter for source codes
281 My code Html Formatter for source codes
266 """
282 """
267
283
268 def wrap(self, source, outfile):
284 def wrap(self, source, outfile):
269 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
285 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
270
286
271 def _wrap_code(self, source):
287 def _wrap_code(self, source):
272 for cnt, it in enumerate(source):
288 for cnt, it in enumerate(source):
273 i, t = it
289 i, t = it
274 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
290 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
275 yield i, t
291 yield i, t
276
292
277 def _wrap_tablelinenos(self, inner):
293 def _wrap_tablelinenos(self, inner):
278 dummyoutfile = StringIO.StringIO()
294 dummyoutfile = StringIO.StringIO()
279 lncount = 0
295 lncount = 0
280 for t, line in inner:
296 for t, line in inner:
281 if t:
297 if t:
282 lncount += 1
298 lncount += 1
283 dummyoutfile.write(line)
299 dummyoutfile.write(line)
284
300
285 fl = self.linenostart
301 fl = self.linenostart
286 mw = len(str(lncount + fl - 1))
302 mw = len(str(lncount + fl - 1))
287 sp = self.linenospecial
303 sp = self.linenospecial
288 st = self.linenostep
304 st = self.linenostep
289 la = self.lineanchors
305 la = self.lineanchors
290 aln = self.anchorlinenos
306 aln = self.anchorlinenos
291 nocls = self.noclasses
307 nocls = self.noclasses
292 if sp:
308 if sp:
293 lines = []
309 lines = []
294
310
295 for i in range(fl, fl + lncount):
311 for i in range(fl, fl + lncount):
296 if i % st == 0:
312 if i % st == 0:
297 if i % sp == 0:
313 if i % sp == 0:
298 if aln:
314 if aln:
299 lines.append('<a href="#%s%d" class="special">%*d</a>' %
315 lines.append('<a href="#%s%d" class="special">%*d</a>' %
300 (la, i, mw, i))
316 (la, i, mw, i))
301 else:
317 else:
302 lines.append('<span class="special">%*d</span>' % (mw, i))
318 lines.append('<span class="special">%*d</span>' % (mw, i))
303 else:
319 else:
304 if aln:
320 if aln:
305 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
321 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
306 else:
322 else:
307 lines.append('%*d' % (mw, i))
323 lines.append('%*d' % (mw, i))
308 else:
324 else:
309 lines.append('')
325 lines.append('')
310 ls = '\n'.join(lines)
326 ls = '\n'.join(lines)
311 else:
327 else:
312 lines = []
328 lines = []
313 for i in range(fl, fl + lncount):
329 for i in range(fl, fl + lncount):
314 if i % st == 0:
330 if i % st == 0:
315 if aln:
331 if aln:
316 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
332 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
317 else:
333 else:
318 lines.append('%*d' % (mw, i))
334 lines.append('%*d' % (mw, i))
319 else:
335 else:
320 lines.append('')
336 lines.append('')
321 ls = '\n'.join(lines)
337 ls = '\n'.join(lines)
322
338
323 # in case you wonder about the seemingly redundant <div> here: since the
339 # in case you wonder about the seemingly redundant <div> here: since the
324 # content in the other cell also is wrapped in a div, some browsers in
340 # content in the other cell also is wrapped in a div, some browsers in
325 # some configurations seem to mess up the formatting...
341 # some configurations seem to mess up the formatting...
326 if nocls:
342 if nocls:
327 yield 0, ('<table class="%stable">' % self.cssclass +
343 yield 0, ('<table class="%stable">' % self.cssclass +
328 '<tr><td><div class="linenodiv" '
344 '<tr><td><div class="linenodiv" '
329 'style="background-color: #f0f0f0; padding-right: 10px">'
345 'style="background-color: #f0f0f0; padding-right: 10px">'
330 '<pre style="line-height: 125%">' +
346 '<pre style="line-height: 125%">' +
331 ls + '</pre></div></td><td id="hlcode" class="code">')
347 ls + '</pre></div></td><td id="hlcode" class="code">')
332 else:
348 else:
333 yield 0, ('<table class="%stable">' % self.cssclass +
349 yield 0, ('<table class="%stable">' % self.cssclass +
334 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
350 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
335 ls + '</pre></div></td><td id="hlcode" class="code">')
351 ls + '</pre></div></td><td id="hlcode" class="code">')
336 yield 0, dummyoutfile.getvalue()
352 yield 0, dummyoutfile.getvalue()
337 yield 0, '</td></tr></table>'
353 yield 0, '</td></tr></table>'
338
354
339
355
340 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
356 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
341 def __init__(self, **kw):
357 def __init__(self, **kw):
342 # only show these line numbers if set
358 # only show these line numbers if set
343 self.only_lines = kw.pop('only_line_numbers', [])
359 self.only_lines = kw.pop('only_line_numbers', [])
344 self.query_terms = kw.pop('query_terms', [])
360 self.query_terms = kw.pop('query_terms', [])
345 self.max_lines = kw.pop('max_lines', 5)
361 self.max_lines = kw.pop('max_lines', 5)
346 self.line_context = kw.pop('line_context', 3)
362 self.line_context = kw.pop('line_context', 3)
347 self.url = kw.pop('url', None)
363 self.url = kw.pop('url', None)
348
364
349 super(CodeHtmlFormatter, self).__init__(**kw)
365 super(CodeHtmlFormatter, self).__init__(**kw)
350
366
351 def _wrap_code(self, source):
367 def _wrap_code(self, source):
352 for cnt, it in enumerate(source):
368 for cnt, it in enumerate(source):
353 i, t = it
369 i, t = it
354 t = '<pre>%s</pre>' % t
370 t = '<pre>%s</pre>' % t
355 yield i, t
371 yield i, t
356
372
357 def _wrap_tablelinenos(self, inner):
373 def _wrap_tablelinenos(self, inner):
358 yield 0, '<table class="code-highlight %stable">' % self.cssclass
374 yield 0, '<table class="code-highlight %stable">' % self.cssclass
359
375
360 last_shown_line_number = 0
376 last_shown_line_number = 0
361 current_line_number = 1
377 current_line_number = 1
362
378
363 for t, line in inner:
379 for t, line in inner:
364 if not t:
380 if not t:
365 yield t, line
381 yield t, line
366 continue
382 continue
367
383
368 if current_line_number in self.only_lines:
384 if current_line_number in self.only_lines:
369 if last_shown_line_number + 1 != current_line_number:
385 if last_shown_line_number + 1 != current_line_number:
370 yield 0, '<tr>'
386 yield 0, '<tr>'
371 yield 0, '<td class="line">...</td>'
387 yield 0, '<td class="line">...</td>'
372 yield 0, '<td id="hlcode" class="code"></td>'
388 yield 0, '<td id="hlcode" class="code"></td>'
373 yield 0, '</tr>'
389 yield 0, '</tr>'
374
390
375 yield 0, '<tr>'
391 yield 0, '<tr>'
376 if self.url:
392 if self.url:
377 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
393 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
378 self.url, current_line_number, current_line_number)
394 self.url, current_line_number, current_line_number)
379 else:
395 else:
380 yield 0, '<td class="line"><a href="">%i</a></td>' % (
396 yield 0, '<td class="line"><a href="">%i</a></td>' % (
381 current_line_number)
397 current_line_number)
382 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
398 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
383 yield 0, '</tr>'
399 yield 0, '</tr>'
384
400
385 last_shown_line_number = current_line_number
401 last_shown_line_number = current_line_number
386
402
387 current_line_number += 1
403 current_line_number += 1
388
404
389
390 yield 0, '</table>'
405 yield 0, '</table>'
391
406
392
407
393 def extract_phrases(text_query):
394 """
395 Extracts phrases from search term string making sure phrases
396 contained in double quotes are kept together - and discarding empty values
397 or fully whitespace values eg.
398
399 'some text "a phrase" more' => ['some', 'text', 'a phrase', 'more']
400
401 """
402
403 in_phrase = False
404 buf = ''
405 phrases = []
406 for char in text_query:
407 if in_phrase:
408 if char == '"': # end phrase
409 phrases.append(buf)
410 buf = ''
411 in_phrase = False
412 continue
413 else:
414 buf += char
415 continue
416 else:
417 if char == '"': # start phrase
418 in_phrase = True
419 phrases.append(buf)
420 buf = ''
421 continue
422 elif char == ' ':
423 phrases.append(buf)
424 buf = ''
425 continue
426 else:
427 buf += char
428
429 phrases.append(buf)
430 phrases = [phrase.strip() for phrase in phrases if phrase.strip()]
431 return phrases
432
433
434 def get_matching_offsets(text, phrases):
435 """
436 Returns a list of string offsets in `text` that the list of `terms` match
437
438 >>> get_matching_offsets('some text here', ['some', 'here'])
439 [(0, 4), (10, 14)]
440
441 """
442 offsets = []
443 for phrase in phrases:
444 for match in re.finditer(phrase, text):
445 offsets.append((match.start(), match.end()))
446
447 return offsets
448
449
450 def normalize_text_for_matching(x):
451 """
452 Replaces all non alnum characters to spaces and lower cases the string,
453 useful for comparing two text strings without punctuation
454 """
455 return re.sub(r'[^\w]', ' ', x.lower())
456
457
458 def get_matching_line_offsets(lines, terms):
459 """ Return a set of `lines` indices (starting from 1) matching a
460 text search query, along with `context` lines above/below matching lines
461
462 :param lines: list of strings representing lines
463 :param terms: search term string to match in lines eg. 'some text'
464 :param context: number of lines above/below a matching line to add to result
465 :param max_lines: cut off for lines of interest
466 eg.
467
468 text = '''
469 words words words
470 words words words
471 some text some
472 words words words
473 words words words
474 text here what
475 '''
476 get_matching_line_offsets(text, 'text', context=1)
477 {3: [(5, 9)], 6: [(0, 4)]]
478
479 """
480 matching_lines = {}
481 phrases = [normalize_text_for_matching(phrase)
482 for phrase in extract_phrases(terms)]
483
484 for line_index, line in enumerate(lines, start=1):
485 match_offsets = get_matching_offsets(
486 normalize_text_for_matching(line), phrases)
487 if match_offsets:
488 matching_lines[line_index] = match_offsets
489
490 return matching_lines
491
492
493 def hsv_to_rgb(h, s, v):
408 def hsv_to_rgb(h, s, v):
494 """ Convert hsv color values to rgb """
409 """ Convert hsv color values to rgb """
495
410
496 if s == 0.0:
411 if s == 0.0:
497 return v, v, v
412 return v, v, v
498 i = int(h * 6.0) # XXX assume int() truncates!
413 i = int(h * 6.0) # XXX assume int() truncates!
499 f = (h * 6.0) - i
414 f = (h * 6.0) - i
500 p = v * (1.0 - s)
415 p = v * (1.0 - s)
501 q = v * (1.0 - s * f)
416 q = v * (1.0 - s * f)
502 t = v * (1.0 - s * (1.0 - f))
417 t = v * (1.0 - s * (1.0 - f))
503 i = i % 6
418 i = i % 6
504 if i == 0:
419 if i == 0:
505 return v, t, p
420 return v, t, p
506 if i == 1:
421 if i == 1:
507 return q, v, p
422 return q, v, p
508 if i == 2:
423 if i == 2:
509 return p, v, t
424 return p, v, t
510 if i == 3:
425 if i == 3:
511 return p, q, v
426 return p, q, v
512 if i == 4:
427 if i == 4:
513 return t, p, v
428 return t, p, v
514 if i == 5:
429 if i == 5:
515 return v, p, q
430 return v, p, q
516
431
517
432
518 def unique_color_generator(n=10000, saturation=0.10, lightness=0.95):
433 def unique_color_generator(n=10000, saturation=0.10, lightness=0.95):
519 """
434 """
520 Generator for getting n of evenly distributed colors using
435 Generator for getting n of evenly distributed colors using
521 hsv color and golden ratio. It always return same order of colors
436 hsv color and golden ratio. It always return same order of colors
522
437
523 :param n: number of colors to generate
438 :param n: number of colors to generate
524 :param saturation: saturation of returned colors
439 :param saturation: saturation of returned colors
525 :param lightness: lightness of returned colors
440 :param lightness: lightness of returned colors
526 :returns: RGB tuple
441 :returns: RGB tuple
527 """
442 """
528
443
529 golden_ratio = 0.618033988749895
444 golden_ratio = 0.618033988749895
530 h = 0.22717784590367374
445 h = 0.22717784590367374
531
446
532 for _ in xrange(n):
447 for _ in xrange(n):
533 h += golden_ratio
448 h += golden_ratio
534 h %= 1
449 h %= 1
535 HSV_tuple = [h, saturation, lightness]
450 HSV_tuple = [h, saturation, lightness]
536 RGB_tuple = hsv_to_rgb(*HSV_tuple)
451 RGB_tuple = hsv_to_rgb(*HSV_tuple)
537 yield map(lambda x: str(int(x * 256)), RGB_tuple)
452 yield map(lambda x: str(int(x * 256)), RGB_tuple)
538
453
539
454
540 def color_hasher(n=10000, saturation=0.10, lightness=0.95):
455 def color_hasher(n=10000, saturation=0.10, lightness=0.95):
541 """
456 """
542 Returns a function which when called with an argument returns a unique
457 Returns a function which when called with an argument returns a unique
543 color for that argument, eg.
458 color for that argument, eg.
544
459
545 :param n: number of colors to generate
460 :param n: number of colors to generate
546 :param saturation: saturation of returned colors
461 :param saturation: saturation of returned colors
547 :param lightness: lightness of returned colors
462 :param lightness: lightness of returned colors
548 :returns: css RGB string
463 :returns: css RGB string
549
464
550 >>> color_hash = color_hasher()
465 >>> color_hash = color_hasher()
551 >>> color_hash('hello')
466 >>> color_hash('hello')
552 'rgb(34, 12, 59)'
467 'rgb(34, 12, 59)'
553 >>> color_hash('hello')
468 >>> color_hash('hello')
554 'rgb(34, 12, 59)'
469 'rgb(34, 12, 59)'
555 >>> color_hash('other')
470 >>> color_hash('other')
556 'rgb(90, 224, 159)'
471 'rgb(90, 224, 159)'
557 """
472 """
558
473
559 color_dict = {}
474 color_dict = {}
560 cgenerator = unique_color_generator(
475 cgenerator = unique_color_generator(
561 saturation=saturation, lightness=lightness)
476 saturation=saturation, lightness=lightness)
562
477
563 def get_color_string(thing):
478 def get_color_string(thing):
564 if thing in color_dict:
479 if thing in color_dict:
565 col = color_dict[thing]
480 col = color_dict[thing]
566 else:
481 else:
567 col = color_dict[thing] = cgenerator.next()
482 col = color_dict[thing] = cgenerator.next()
568 return "rgb(%s)" % (', '.join(col))
483 return "rgb(%s)" % (', '.join(col))
569
484
570 return get_color_string
485 return get_color_string
571
486
572
487
573 def get_lexer_safe(mimetype=None, filepath=None):
488 def get_lexer_safe(mimetype=None, filepath=None):
574 """
489 """
575 Tries to return a relevant pygments lexer using mimetype/filepath name,
490 Tries to return a relevant pygments lexer using mimetype/filepath name,
576 defaulting to plain text if none could be found
491 defaulting to plain text if none could be found
577 """
492 """
578 lexer = None
493 lexer = None
579 try:
494 try:
580 if mimetype:
495 if mimetype:
581 lexer = get_lexer_for_mimetype(mimetype)
496 lexer = get_lexer_for_mimetype(mimetype)
582 if not lexer:
497 if not lexer:
583 lexer = get_lexer_for_filename(filepath)
498 lexer = get_lexer_for_filename(filepath)
584 except pygments.util.ClassNotFound:
499 except pygments.util.ClassNotFound:
585 pass
500 pass
586
501
587 if not lexer:
502 if not lexer:
588 lexer = get_lexer_by_name('text')
503 lexer = get_lexer_by_name('text')
589
504
590 return lexer
505 return lexer
591
506
592
507
593 def get_lexer_for_filenode(filenode):
508 def get_lexer_for_filenode(filenode):
594 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
509 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
595 return lexer
510 return lexer
596
511
597
512
598 def pygmentize(filenode, **kwargs):
513 def pygmentize(filenode, **kwargs):
599 """
514 """
600 pygmentize function using pygments
515 pygmentize function using pygments
601
516
602 :param filenode:
517 :param filenode:
603 """
518 """
604 lexer = get_lexer_for_filenode(filenode)
519 lexer = get_lexer_for_filenode(filenode)
605 return literal(code_highlight(filenode.content, lexer,
520 return literal(code_highlight(filenode.content, lexer,
606 CodeHtmlFormatter(**kwargs)))
521 CodeHtmlFormatter(**kwargs)))
607
522
608
523
609 def is_following_repo(repo_name, user_id):
524 def is_following_repo(repo_name, user_id):
610 from rhodecode.model.scm import ScmModel
525 from rhodecode.model.scm import ScmModel
611 return ScmModel().is_following_repo(repo_name, user_id)
526 return ScmModel().is_following_repo(repo_name, user_id)
612
527
613
528
614 class _Message(object):
529 class _Message(object):
615 """A message returned by ``Flash.pop_messages()``.
530 """A message returned by ``Flash.pop_messages()``.
616
531
617 Converting the message to a string returns the message text. Instances
532 Converting the message to a string returns the message text. Instances
618 also have the following attributes:
533 also have the following attributes:
619
534
620 * ``message``: the message text.
535 * ``message``: the message text.
621 * ``category``: the category specified when the message was created.
536 * ``category``: the category specified when the message was created.
622 """
537 """
623
538
624 def __init__(self, category, message):
539 def __init__(self, category, message):
625 self.category = category
540 self.category = category
626 self.message = message
541 self.message = message
627
542
628 def __str__(self):
543 def __str__(self):
629 return self.message
544 return self.message
630
545
631 __unicode__ = __str__
546 __unicode__ = __str__
632
547
633 def __html__(self):
548 def __html__(self):
634 return escape(safe_unicode(self.message))
549 return escape(safe_unicode(self.message))
635
550
636
551
637 class Flash(object):
552 class Flash(object):
638 # List of allowed categories. If None, allow any category.
553 # List of allowed categories. If None, allow any category.
639 categories = ["warning", "notice", "error", "success"]
554 categories = ["warning", "notice", "error", "success"]
640
555
641 # Default category if none is specified.
556 # Default category if none is specified.
642 default_category = "notice"
557 default_category = "notice"
643
558
644 def __init__(self, session_key="flash", categories=None,
559 def __init__(self, session_key="flash", categories=None,
645 default_category=None):
560 default_category=None):
646 """
561 """
647 Instantiate a ``Flash`` object.
562 Instantiate a ``Flash`` object.
648
563
649 ``session_key`` is the key to save the messages under in the user's
564 ``session_key`` is the key to save the messages under in the user's
650 session.
565 session.
651
566
652 ``categories`` is an optional list which overrides the default list
567 ``categories`` is an optional list which overrides the default list
653 of categories.
568 of categories.
654
569
655 ``default_category`` overrides the default category used for messages
570 ``default_category`` overrides the default category used for messages
656 when none is specified.
571 when none is specified.
657 """
572 """
658 self.session_key = session_key
573 self.session_key = session_key
659 if categories is not None:
574 if categories is not None:
660 self.categories = categories
575 self.categories = categories
661 if default_category is not None:
576 if default_category is not None:
662 self.default_category = default_category
577 self.default_category = default_category
663 if self.categories and self.default_category not in self.categories:
578 if self.categories and self.default_category not in self.categories:
664 raise ValueError(
579 raise ValueError(
665 "unrecognized default category %r" % (self.default_category,))
580 "unrecognized default category %r" % (self.default_category,))
666
581
667 def pop_messages(self, session=None, request=None):
582 def pop_messages(self, session=None, request=None):
668 """
583 """
669 Return all accumulated messages and delete them from the session.
584 Return all accumulated messages and delete them from the session.
670
585
671 The return value is a list of ``Message`` objects.
586 The return value is a list of ``Message`` objects.
672 """
587 """
673 messages = []
588 messages = []
674
589
675 if not session:
590 if not session:
676 if not request:
591 if not request:
677 request = get_current_request()
592 request = get_current_request()
678 session = request.session
593 session = request.session
679
594
680 # Pop the 'old' pylons flash messages. They are tuples of the form
595 # Pop the 'old' pylons flash messages. They are tuples of the form
681 # (category, message)
596 # (category, message)
682 for cat, msg in session.pop(self.session_key, []):
597 for cat, msg in session.pop(self.session_key, []):
683 messages.append(_Message(cat, msg))
598 messages.append(_Message(cat, msg))
684
599
685 # Pop the 'new' pyramid flash messages for each category as list
600 # Pop the 'new' pyramid flash messages for each category as list
686 # of strings.
601 # of strings.
687 for cat in self.categories:
602 for cat in self.categories:
688 for msg in session.pop_flash(queue=cat):
603 for msg in session.pop_flash(queue=cat):
689 messages.append(_Message(cat, msg))
604 messages.append(_Message(cat, msg))
690 # Map messages from the default queue to the 'notice' category.
605 # Map messages from the default queue to the 'notice' category.
691 for msg in session.pop_flash():
606 for msg in session.pop_flash():
692 messages.append(_Message('notice', msg))
607 messages.append(_Message('notice', msg))
693
608
694 session.save()
609 session.save()
695 return messages
610 return messages
696
611
697 def json_alerts(self, session=None, request=None):
612 def json_alerts(self, session=None, request=None):
698 payloads = []
613 payloads = []
699 messages = flash.pop_messages(session=session, request=request)
614 messages = flash.pop_messages(session=session, request=request)
700 if messages:
615 if messages:
701 for message in messages:
616 for message in messages:
702 subdata = {}
617 subdata = {}
703 if hasattr(message.message, 'rsplit'):
618 if hasattr(message.message, 'rsplit'):
704 flash_data = message.message.rsplit('|DELIM|', 1)
619 flash_data = message.message.rsplit('|DELIM|', 1)
705 org_message = flash_data[0]
620 org_message = flash_data[0]
706 if len(flash_data) > 1:
621 if len(flash_data) > 1:
707 subdata = json.loads(flash_data[1])
622 subdata = json.loads(flash_data[1])
708 else:
623 else:
709 org_message = message.message
624 org_message = message.message
710 payloads.append({
625 payloads.append({
711 'message': {
626 'message': {
712 'message': u'{}'.format(org_message),
627 'message': u'{}'.format(org_message),
713 'level': message.category,
628 'level': message.category,
714 'force': True,
629 'force': True,
715 'subdata': subdata
630 'subdata': subdata
716 }
631 }
717 })
632 })
718 return json.dumps(payloads)
633 return json.dumps(payloads)
719
634
720 def __call__(self, message, category=None, ignore_duplicate=False,
635 def __call__(self, message, category=None, ignore_duplicate=False,
721 session=None, request=None):
636 session=None, request=None):
722
637
723 if not session:
638 if not session:
724 if not request:
639 if not request:
725 request = get_current_request()
640 request = get_current_request()
726 session = request.session
641 session = request.session
727
642
728 session.flash(
643 session.flash(
729 message, queue=category, allow_duplicate=not ignore_duplicate)
644 message, queue=category, allow_duplicate=not ignore_duplicate)
730
645
731
646
732 flash = Flash()
647 flash = Flash()
733
648
734 #==============================================================================
649 #==============================================================================
735 # SCM FILTERS available via h.
650 # SCM FILTERS available via h.
736 #==============================================================================
651 #==============================================================================
737 from rhodecode.lib.vcs.utils import author_name, author_email
652 from rhodecode.lib.vcs.utils import author_name, author_email
738 from rhodecode.lib.utils2 import credentials_filter, age as _age
653 from rhodecode.lib.utils2 import credentials_filter, age as _age
739 from rhodecode.model.db import User, ChangesetStatus
654 from rhodecode.model.db import User, ChangesetStatus
740
655
741 age = _age
656 age = _age
742 capitalize = lambda x: x.capitalize()
657 capitalize = lambda x: x.capitalize()
743 email = author_email
658 email = author_email
744 short_id = lambda x: x[:12]
659 short_id = lambda x: x[:12]
745 hide_credentials = lambda x: ''.join(credentials_filter(x))
660 hide_credentials = lambda x: ''.join(credentials_filter(x))
746
661
747
662
748 import pytz
663 import pytz
749 import tzlocal
664 import tzlocal
750 local_timezone = tzlocal.get_localzone()
665 local_timezone = tzlocal.get_localzone()
751
666
752
667
753 def age_component(datetime_iso, value=None, time_is_local=False):
668 def age_component(datetime_iso, value=None, time_is_local=False):
754 title = value or format_date(datetime_iso)
669 title = value or format_date(datetime_iso)
755 tzinfo = '+00:00'
670 tzinfo = '+00:00'
756
671
757 # detect if we have a timezone info, otherwise, add it
672 # detect if we have a timezone info, otherwise, add it
758 if time_is_local and isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
673 if time_is_local and isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
759 force_timezone = os.environ.get('RC_TIMEZONE', '')
674 force_timezone = os.environ.get('RC_TIMEZONE', '')
760 if force_timezone:
675 if force_timezone:
761 force_timezone = pytz.timezone(force_timezone)
676 force_timezone = pytz.timezone(force_timezone)
762 timezone = force_timezone or local_timezone
677 timezone = force_timezone or local_timezone
763 offset = timezone.localize(datetime_iso).strftime('%z')
678 offset = timezone.localize(datetime_iso).strftime('%z')
764 tzinfo = '{}:{}'.format(offset[:-2], offset[-2:])
679 tzinfo = '{}:{}'.format(offset[:-2], offset[-2:])
765
680
766 return literal(
681 return literal(
767 '<time class="timeago tooltip" '
682 '<time class="timeago tooltip" '
768 'title="{1}{2}" datetime="{0}{2}">{1}</time>'.format(
683 'title="{1}{2}" datetime="{0}{2}">{1}</time>'.format(
769 datetime_iso, title, tzinfo))
684 datetime_iso, title, tzinfo))
770
685
771
686
772 def _shorten_commit_id(commit_id):
687 def _shorten_commit_id(commit_id):
773 from rhodecode import CONFIG
688 from rhodecode import CONFIG
774 def_len = safe_int(CONFIG.get('rhodecode_show_sha_length', 12))
689 def_len = safe_int(CONFIG.get('rhodecode_show_sha_length', 12))
775 return commit_id[:def_len]
690 return commit_id[:def_len]
776
691
777
692
778 def show_id(commit):
693 def show_id(commit):
779 """
694 """
780 Configurable function that shows ID
695 Configurable function that shows ID
781 by default it's r123:fffeeefffeee
696 by default it's r123:fffeeefffeee
782
697
783 :param commit: commit instance
698 :param commit: commit instance
784 """
699 """
785 from rhodecode import CONFIG
700 from rhodecode import CONFIG
786 show_idx = str2bool(CONFIG.get('rhodecode_show_revision_number', True))
701 show_idx = str2bool(CONFIG.get('rhodecode_show_revision_number', True))
787
702
788 raw_id = _shorten_commit_id(commit.raw_id)
703 raw_id = _shorten_commit_id(commit.raw_id)
789 if show_idx:
704 if show_idx:
790 return 'r%s:%s' % (commit.idx, raw_id)
705 return 'r%s:%s' % (commit.idx, raw_id)
791 else:
706 else:
792 return '%s' % (raw_id, )
707 return '%s' % (raw_id, )
793
708
794
709
795 def format_date(date):
710 def format_date(date):
796 """
711 """
797 use a standardized formatting for dates used in RhodeCode
712 use a standardized formatting for dates used in RhodeCode
798
713
799 :param date: date/datetime object
714 :param date: date/datetime object
800 :return: formatted date
715 :return: formatted date
801 """
716 """
802
717
803 if date:
718 if date:
804 _fmt = "%a, %d %b %Y %H:%M:%S"
719 _fmt = "%a, %d %b %Y %H:%M:%S"
805 return safe_unicode(date.strftime(_fmt))
720 return safe_unicode(date.strftime(_fmt))
806
721
807 return u""
722 return u""
808
723
809
724
810 class _RepoChecker(object):
725 class _RepoChecker(object):
811
726
812 def __init__(self, backend_alias):
727 def __init__(self, backend_alias):
813 self._backend_alias = backend_alias
728 self._backend_alias = backend_alias
814
729
815 def __call__(self, repository):
730 def __call__(self, repository):
816 if hasattr(repository, 'alias'):
731 if hasattr(repository, 'alias'):
817 _type = repository.alias
732 _type = repository.alias
818 elif hasattr(repository, 'repo_type'):
733 elif hasattr(repository, 'repo_type'):
819 _type = repository.repo_type
734 _type = repository.repo_type
820 else:
735 else:
821 _type = repository
736 _type = repository
822 return _type == self._backend_alias
737 return _type == self._backend_alias
823
738
824 is_git = _RepoChecker('git')
739 is_git = _RepoChecker('git')
825 is_hg = _RepoChecker('hg')
740 is_hg = _RepoChecker('hg')
826 is_svn = _RepoChecker('svn')
741 is_svn = _RepoChecker('svn')
827
742
828
743
829 def get_repo_type_by_name(repo_name):
744 def get_repo_type_by_name(repo_name):
830 repo = Repository.get_by_repo_name(repo_name)
745 repo = Repository.get_by_repo_name(repo_name)
831 return repo.repo_type
746 return repo.repo_type
832
747
833
748
834 def is_svn_without_proxy(repository):
749 def is_svn_without_proxy(repository):
835 if is_svn(repository):
750 if is_svn(repository):
836 from rhodecode.model.settings import VcsSettingsModel
751 from rhodecode.model.settings import VcsSettingsModel
837 conf = VcsSettingsModel().get_ui_settings_as_config_obj()
752 conf = VcsSettingsModel().get_ui_settings_as_config_obj()
838 return not str2bool(conf.get('vcs_svn_proxy', 'http_requests_enabled'))
753 return not str2bool(conf.get('vcs_svn_proxy', 'http_requests_enabled'))
839 return False
754 return False
840
755
841
756
842 def discover_user(author):
757 def discover_user(author):
843 """
758 """
844 Tries to discover RhodeCode User based on the autho string. Author string
759 Tries to discover RhodeCode User based on the autho string. Author string
845 is typically `FirstName LastName <email@address.com>`
760 is typically `FirstName LastName <email@address.com>`
846 """
761 """
847
762
848 # if author is already an instance use it for extraction
763 # if author is already an instance use it for extraction
849 if isinstance(author, User):
764 if isinstance(author, User):
850 return author
765 return author
851
766
852 # Valid email in the attribute passed, see if they're in the system
767 # Valid email in the attribute passed, see if they're in the system
853 _email = author_email(author)
768 _email = author_email(author)
854 if _email != '':
769 if _email != '':
855 user = User.get_by_email(_email, case_insensitive=True, cache=True)
770 user = User.get_by_email(_email, case_insensitive=True, cache=True)
856 if user is not None:
771 if user is not None:
857 return user
772 return user
858
773
859 # Maybe it's a username, we try to extract it and fetch by username ?
774 # Maybe it's a username, we try to extract it and fetch by username ?
860 _author = author_name(author)
775 _author = author_name(author)
861 user = User.get_by_username(_author, case_insensitive=True, cache=True)
776 user = User.get_by_username(_author, case_insensitive=True, cache=True)
862 if user is not None:
777 if user is not None:
863 return user
778 return user
864
779
865 return None
780 return None
866
781
867
782
868 def email_or_none(author):
783 def email_or_none(author):
869 # extract email from the commit string
784 # extract email from the commit string
870 _email = author_email(author)
785 _email = author_email(author)
871
786
872 # If we have an email, use it, otherwise
787 # If we have an email, use it, otherwise
873 # see if it contains a username we can get an email from
788 # see if it contains a username we can get an email from
874 if _email != '':
789 if _email != '':
875 return _email
790 return _email
876 else:
791 else:
877 user = User.get_by_username(
792 user = User.get_by_username(
878 author_name(author), case_insensitive=True, cache=True)
793 author_name(author), case_insensitive=True, cache=True)
879
794
880 if user is not None:
795 if user is not None:
881 return user.email
796 return user.email
882
797
883 # No valid email, not a valid user in the system, none!
798 # No valid email, not a valid user in the system, none!
884 return None
799 return None
885
800
886
801
887 def link_to_user(author, length=0, **kwargs):
802 def link_to_user(author, length=0, **kwargs):
888 user = discover_user(author)
803 user = discover_user(author)
889 # user can be None, but if we have it already it means we can re-use it
804 # user can be None, but if we have it already it means we can re-use it
890 # in the person() function, so we save 1 intensive-query
805 # in the person() function, so we save 1 intensive-query
891 if user:
806 if user:
892 author = user
807 author = user
893
808
894 display_person = person(author, 'username_or_name_or_email')
809 display_person = person(author, 'username_or_name_or_email')
895 if length:
810 if length:
896 display_person = shorter(display_person, length)
811 display_person = shorter(display_person, length)
897
812
898 if user:
813 if user:
899 return link_to(
814 return link_to(
900 escape(display_person),
815 escape(display_person),
901 route_path('user_profile', username=user.username),
816 route_path('user_profile', username=user.username),
902 **kwargs)
817 **kwargs)
903 else:
818 else:
904 return escape(display_person)
819 return escape(display_person)
905
820
906
821
907 def link_to_group(users_group_name, **kwargs):
822 def link_to_group(users_group_name, **kwargs):
908 return link_to(
823 return link_to(
909 escape(users_group_name),
824 escape(users_group_name),
910 route_path('user_group_profile', user_group_name=users_group_name),
825 route_path('user_group_profile', user_group_name=users_group_name),
911 **kwargs)
826 **kwargs)
912
827
913
828
914 def person(author, show_attr="username_and_name"):
829 def person(author, show_attr="username_and_name"):
915 user = discover_user(author)
830 user = discover_user(author)
916 if user:
831 if user:
917 return getattr(user, show_attr)
832 return getattr(user, show_attr)
918 else:
833 else:
919 _author = author_name(author)
834 _author = author_name(author)
920 _email = email(author)
835 _email = email(author)
921 return _author or _email
836 return _author or _email
922
837
923
838
924 def author_string(email):
839 def author_string(email):
925 if email:
840 if email:
926 user = User.get_by_email(email, case_insensitive=True, cache=True)
841 user = User.get_by_email(email, case_insensitive=True, cache=True)
927 if user:
842 if user:
928 if user.first_name or user.last_name:
843 if user.first_name or user.last_name:
929 return '%s %s &lt;%s&gt;' % (
844 return '%s %s &lt;%s&gt;' % (
930 user.first_name, user.last_name, email)
845 user.first_name, user.last_name, email)
931 else:
846 else:
932 return email
847 return email
933 else:
848 else:
934 return email
849 return email
935 else:
850 else:
936 return None
851 return None
937
852
938
853
939 def person_by_id(id_, show_attr="username_and_name"):
854 def person_by_id(id_, show_attr="username_and_name"):
940 # attr to return from fetched user
855 # attr to return from fetched user
941 person_getter = lambda usr: getattr(usr, show_attr)
856 person_getter = lambda usr: getattr(usr, show_attr)
942
857
943 #maybe it's an ID ?
858 #maybe it's an ID ?
944 if str(id_).isdigit() or isinstance(id_, int):
859 if str(id_).isdigit() or isinstance(id_, int):
945 id_ = int(id_)
860 id_ = int(id_)
946 user = User.get(id_)
861 user = User.get(id_)
947 if user is not None:
862 if user is not None:
948 return person_getter(user)
863 return person_getter(user)
949 return id_
864 return id_
950
865
951
866
952 def gravatar_with_user(request, author, show_disabled=False):
867 def gravatar_with_user(request, author, show_disabled=False):
953 _render = request.get_partial_renderer(
868 _render = request.get_partial_renderer(
954 'rhodecode:templates/base/base.mako')
869 'rhodecode:templates/base/base.mako')
955 return _render('gravatar_with_user', author, show_disabled=show_disabled)
870 return _render('gravatar_with_user', author, show_disabled=show_disabled)
956
871
957
872
958 tags_paterns = OrderedDict((
873 tags_paterns = OrderedDict((
959 ('lang', (re.compile(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+\.]*)\]'),
874 ('lang', (re.compile(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+\.]*)\]'),
960 '<div class="metatag" tag="lang">\\2</div>')),
875 '<div class="metatag" tag="lang">\\2</div>')),
961
876
962 ('see', (re.compile(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]'),
877 ('see', (re.compile(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]'),
963 '<div class="metatag" tag="see">see: \\1 </div>')),
878 '<div class="metatag" tag="see">see: \\1 </div>')),
964
879
965 ('url', (re.compile(r'\[url\ \=\&gt;\ \[([a-zA-Z0-9\ \.\-\_]+)\]\((http://|https://|/)(.*?)\)\]'),
880 ('url', (re.compile(r'\[url\ \=\&gt;\ \[([a-zA-Z0-9\ \.\-\_]+)\]\((http://|https://|/)(.*?)\)\]'),
966 '<div class="metatag" tag="url"> <a href="\\2\\3">\\1</a> </div>')),
881 '<div class="metatag" tag="url"> <a href="\\2\\3">\\1</a> </div>')),
967
882
968 ('license', (re.compile(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]'),
883 ('license', (re.compile(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]'),
969 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>')),
884 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>')),
970
885
971 ('ref', (re.compile(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]'),
886 ('ref', (re.compile(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]'),
972 '<div class="metatag" tag="ref \\1">\\1: <a href="/\\2">\\2</a></div>')),
887 '<div class="metatag" tag="ref \\1">\\1: <a href="/\\2">\\2</a></div>')),
973
888
974 ('state', (re.compile(r'\[(stable|featured|stale|dead|dev|deprecated)\]'),
889 ('state', (re.compile(r'\[(stable|featured|stale|dead|dev|deprecated)\]'),
975 '<div class="metatag" tag="state \\1">\\1</div>')),
890 '<div class="metatag" tag="state \\1">\\1</div>')),
976
891
977 # label in grey
892 # label in grey
978 ('label', (re.compile(r'\[([a-z]+)\]'),
893 ('label', (re.compile(r'\[([a-z]+)\]'),
979 '<div class="metatag" tag="label">\\1</div>')),
894 '<div class="metatag" tag="label">\\1</div>')),
980
895
981 # generic catch all in grey
896 # generic catch all in grey
982 ('generic', (re.compile(r'\[([a-zA-Z0-9\.\-\_]+)\]'),
897 ('generic', (re.compile(r'\[([a-zA-Z0-9\.\-\_]+)\]'),
983 '<div class="metatag" tag="generic">\\1</div>')),
898 '<div class="metatag" tag="generic">\\1</div>')),
984 ))
899 ))
985
900
986
901
987 def extract_metatags(value):
902 def extract_metatags(value):
988 """
903 """
989 Extract supported meta-tags from given text value
904 Extract supported meta-tags from given text value
990 """
905 """
991 tags = []
906 tags = []
992 if not value:
907 if not value:
993 return tags, ''
908 return tags, ''
994
909
995 for key, val in tags_paterns.items():
910 for key, val in tags_paterns.items():
996 pat, replace_html = val
911 pat, replace_html = val
997 tags.extend([(key, x.group()) for x in pat.finditer(value)])
912 tags.extend([(key, x.group()) for x in pat.finditer(value)])
998 value = pat.sub('', value)
913 value = pat.sub('', value)
999
914
1000 return tags, value
915 return tags, value
1001
916
1002
917
1003 def style_metatag(tag_type, value):
918 def style_metatag(tag_type, value):
1004 """
919 """
1005 converts tags from value into html equivalent
920 converts tags from value into html equivalent
1006 """
921 """
1007 if not value:
922 if not value:
1008 return ''
923 return ''
1009
924
1010 html_value = value
925 html_value = value
1011 tag_data = tags_paterns.get(tag_type)
926 tag_data = tags_paterns.get(tag_type)
1012 if tag_data:
927 if tag_data:
1013 pat, replace_html = tag_data
928 pat, replace_html = tag_data
1014 # convert to plain `unicode` instead of a markup tag to be used in
929 # convert to plain `unicode` instead of a markup tag to be used in
1015 # regex expressions. safe_unicode doesn't work here
930 # regex expressions. safe_unicode doesn't work here
1016 html_value = pat.sub(replace_html, unicode(value))
931 html_value = pat.sub(replace_html, unicode(value))
1017
932
1018 return html_value
933 return html_value
1019
934
1020
935
1021 def bool2icon(value, show_at_false=True):
936 def bool2icon(value, show_at_false=True):
1022 """
937 """
1023 Returns boolean value of a given value, represented as html element with
938 Returns boolean value of a given value, represented as html element with
1024 classes that will represent icons
939 classes that will represent icons
1025
940
1026 :param value: given value to convert to html node
941 :param value: given value to convert to html node
1027 """
942 """
1028
943
1029 if value: # does bool conversion
944 if value: # does bool conversion
1030 return HTML.tag('i', class_="icon-true")
945 return HTML.tag('i', class_="icon-true")
1031 else: # not true as bool
946 else: # not true as bool
1032 if show_at_false:
947 if show_at_false:
1033 return HTML.tag('i', class_="icon-false")
948 return HTML.tag('i', class_="icon-false")
1034 return HTML.tag('i')
949 return HTML.tag('i')
1035
950
1036 #==============================================================================
951 #==============================================================================
1037 # PERMS
952 # PERMS
1038 #==============================================================================
953 #==============================================================================
1039 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
954 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
1040 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
955 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
1041 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token, \
956 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token, \
1042 csrf_token_key
957 csrf_token_key
1043
958
1044
959
1045 #==============================================================================
960 #==============================================================================
1046 # GRAVATAR URL
961 # GRAVATAR URL
1047 #==============================================================================
962 #==============================================================================
1048 class InitialsGravatar(object):
963 class InitialsGravatar(object):
1049 def __init__(self, email_address, first_name, last_name, size=30,
964 def __init__(self, email_address, first_name, last_name, size=30,
1050 background=None, text_color='#fff'):
965 background=None, text_color='#fff'):
1051 self.size = size
966 self.size = size
1052 self.first_name = first_name
967 self.first_name = first_name
1053 self.last_name = last_name
968 self.last_name = last_name
1054 self.email_address = email_address
969 self.email_address = email_address
1055 self.background = background or self.str2color(email_address)
970 self.background = background or self.str2color(email_address)
1056 self.text_color = text_color
971 self.text_color = text_color
1057
972
1058 def get_color_bank(self):
973 def get_color_bank(self):
1059 """
974 """
1060 returns a predefined list of colors that gravatars can use.
975 returns a predefined list of colors that gravatars can use.
1061 Those are randomized distinct colors that guarantee readability and
976 Those are randomized distinct colors that guarantee readability and
1062 uniqueness.
977 uniqueness.
1063
978
1064 generated with: http://phrogz.net/css/distinct-colors.html
979 generated with: http://phrogz.net/css/distinct-colors.html
1065 """
980 """
1066 return [
981 return [
1067 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
982 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
1068 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
983 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
1069 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
984 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
1070 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
985 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
1071 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
986 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
1072 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
987 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
1073 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
988 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
1074 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
989 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
1075 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
990 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
1076 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
991 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
1077 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
992 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
1078 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
993 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
1079 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
994 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
1080 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
995 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
1081 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
996 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
1082 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
997 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
1083 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
998 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
1084 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
999 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
1085 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
1000 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
1086 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
1001 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
1087 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
1002 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
1088 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
1003 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
1089 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
1004 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
1090 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
1005 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
1091 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
1006 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
1092 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
1007 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
1093 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
1008 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
1094 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
1009 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
1095 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
1010 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
1096 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
1011 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
1097 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
1012 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
1098 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
1013 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
1099 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
1014 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
1100 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
1015 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
1101 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
1016 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
1102 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
1017 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
1103 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
1018 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
1104 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
1019 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
1105 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
1020 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
1106 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
1021 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
1107 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
1022 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
1108 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
1023 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
1109 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
1024 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
1110 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
1025 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
1111 '#4f8c46', '#368dd9', '#5c0073'
1026 '#4f8c46', '#368dd9', '#5c0073'
1112 ]
1027 ]
1113
1028
1114 def rgb_to_hex_color(self, rgb_tuple):
1029 def rgb_to_hex_color(self, rgb_tuple):
1115 """
1030 """
1116 Converts an rgb_tuple passed to an hex color.
1031 Converts an rgb_tuple passed to an hex color.
1117
1032
1118 :param rgb_tuple: tuple with 3 ints represents rgb color space
1033 :param rgb_tuple: tuple with 3 ints represents rgb color space
1119 """
1034 """
1120 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
1035 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
1121
1036
1122 def email_to_int_list(self, email_str):
1037 def email_to_int_list(self, email_str):
1123 """
1038 """
1124 Get every byte of the hex digest value of email and turn it to integer.
1039 Get every byte of the hex digest value of email and turn it to integer.
1125 It's going to be always between 0-255
1040 It's going to be always between 0-255
1126 """
1041 """
1127 digest = md5_safe(email_str.lower())
1042 digest = md5_safe(email_str.lower())
1128 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
1043 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
1129
1044
1130 def pick_color_bank_index(self, email_str, color_bank):
1045 def pick_color_bank_index(self, email_str, color_bank):
1131 return self.email_to_int_list(email_str)[0] % len(color_bank)
1046 return self.email_to_int_list(email_str)[0] % len(color_bank)
1132
1047
1133 def str2color(self, email_str):
1048 def str2color(self, email_str):
1134 """
1049 """
1135 Tries to map in a stable algorithm an email to color
1050 Tries to map in a stable algorithm an email to color
1136
1051
1137 :param email_str:
1052 :param email_str:
1138 """
1053 """
1139 color_bank = self.get_color_bank()
1054 color_bank = self.get_color_bank()
1140 # pick position (module it's length so we always find it in the
1055 # pick position (module it's length so we always find it in the
1141 # bank even if it's smaller than 256 values
1056 # bank even if it's smaller than 256 values
1142 pos = self.pick_color_bank_index(email_str, color_bank)
1057 pos = self.pick_color_bank_index(email_str, color_bank)
1143 return color_bank[pos]
1058 return color_bank[pos]
1144
1059
1145 def normalize_email(self, email_address):
1060 def normalize_email(self, email_address):
1146 import unicodedata
1061 import unicodedata
1147 # default host used to fill in the fake/missing email
1062 # default host used to fill in the fake/missing email
1148 default_host = u'localhost'
1063 default_host = u'localhost'
1149
1064
1150 if not email_address:
1065 if not email_address:
1151 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1066 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1152
1067
1153 email_address = safe_unicode(email_address)
1068 email_address = safe_unicode(email_address)
1154
1069
1155 if u'@' not in email_address:
1070 if u'@' not in email_address:
1156 email_address = u'%s@%s' % (email_address, default_host)
1071 email_address = u'%s@%s' % (email_address, default_host)
1157
1072
1158 if email_address.endswith(u'@'):
1073 if email_address.endswith(u'@'):
1159 email_address = u'%s%s' % (email_address, default_host)
1074 email_address = u'%s%s' % (email_address, default_host)
1160
1075
1161 email_address = unicodedata.normalize('NFKD', email_address)\
1076 email_address = unicodedata.normalize('NFKD', email_address)\
1162 .encode('ascii', 'ignore')
1077 .encode('ascii', 'ignore')
1163 return email_address
1078 return email_address
1164
1079
1165 def get_initials(self):
1080 def get_initials(self):
1166 """
1081 """
1167 Returns 2 letter initials calculated based on the input.
1082 Returns 2 letter initials calculated based on the input.
1168 The algorithm picks first given email address, and takes first letter
1083 The algorithm picks first given email address, and takes first letter
1169 of part before @, and then the first letter of server name. In case
1084 of part before @, and then the first letter of server name. In case
1170 the part before @ is in a format of `somestring.somestring2` it replaces
1085 the part before @ is in a format of `somestring.somestring2` it replaces
1171 the server letter with first letter of somestring2
1086 the server letter with first letter of somestring2
1172
1087
1173 In case function was initialized with both first and lastname, this
1088 In case function was initialized with both first and lastname, this
1174 overrides the extraction from email by first letter of the first and
1089 overrides the extraction from email by first letter of the first and
1175 last name. We add special logic to that functionality, In case Full name
1090 last name. We add special logic to that functionality, In case Full name
1176 is compound, like Guido Von Rossum, we use last part of the last name
1091 is compound, like Guido Von Rossum, we use last part of the last name
1177 (Von Rossum) picking `R`.
1092 (Von Rossum) picking `R`.
1178
1093
1179 Function also normalizes the non-ascii characters to they ascii
1094 Function also normalizes the non-ascii characters to they ascii
1180 representation, eg Δ„ => A
1095 representation, eg Δ„ => A
1181 """
1096 """
1182 import unicodedata
1097 import unicodedata
1183 # replace non-ascii to ascii
1098 # replace non-ascii to ascii
1184 first_name = unicodedata.normalize(
1099 first_name = unicodedata.normalize(
1185 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1100 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1186 last_name = unicodedata.normalize(
1101 last_name = unicodedata.normalize(
1187 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1102 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1188
1103
1189 # do NFKD encoding, and also make sure email has proper format
1104 # do NFKD encoding, and also make sure email has proper format
1190 email_address = self.normalize_email(self.email_address)
1105 email_address = self.normalize_email(self.email_address)
1191
1106
1192 # first push the email initials
1107 # first push the email initials
1193 prefix, server = email_address.split('@', 1)
1108 prefix, server = email_address.split('@', 1)
1194
1109
1195 # check if prefix is maybe a 'first_name.last_name' syntax
1110 # check if prefix is maybe a 'first_name.last_name' syntax
1196 _dot_split = prefix.rsplit('.', 1)
1111 _dot_split = prefix.rsplit('.', 1)
1197 if len(_dot_split) == 2 and _dot_split[1]:
1112 if len(_dot_split) == 2 and _dot_split[1]:
1198 initials = [_dot_split[0][0], _dot_split[1][0]]
1113 initials = [_dot_split[0][0], _dot_split[1][0]]
1199 else:
1114 else:
1200 initials = [prefix[0], server[0]]
1115 initials = [prefix[0], server[0]]
1201
1116
1202 # then try to replace either first_name or last_name
1117 # then try to replace either first_name or last_name
1203 fn_letter = (first_name or " ")[0].strip()
1118 fn_letter = (first_name or " ")[0].strip()
1204 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1119 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1205
1120
1206 if fn_letter:
1121 if fn_letter:
1207 initials[0] = fn_letter
1122 initials[0] = fn_letter
1208
1123
1209 if ln_letter:
1124 if ln_letter:
1210 initials[1] = ln_letter
1125 initials[1] = ln_letter
1211
1126
1212 return ''.join(initials).upper()
1127 return ''.join(initials).upper()
1213
1128
1214 def get_img_data_by_type(self, font_family, img_type):
1129 def get_img_data_by_type(self, font_family, img_type):
1215 default_user = """
1130 default_user = """
1216 <svg xmlns="http://www.w3.org/2000/svg"
1131 <svg xmlns="http://www.w3.org/2000/svg"
1217 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1132 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1218 viewBox="-15 -10 439.165 429.164"
1133 viewBox="-15 -10 439.165 429.164"
1219
1134
1220 xml:space="preserve"
1135 xml:space="preserve"
1221 style="background:{background};" >
1136 style="background:{background};" >
1222
1137
1223 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1138 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1224 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1139 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1225 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1140 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1226 168.596,153.916,216.671,
1141 168.596,153.916,216.671,
1227 204.583,216.671z" fill="{text_color}"/>
1142 204.583,216.671z" fill="{text_color}"/>
1228 <path d="M407.164,374.717L360.88,
1143 <path d="M407.164,374.717L360.88,
1229 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1144 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1230 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1145 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1231 15.366-44.203,23.488-69.076,23.488c-24.877,
1146 15.366-44.203,23.488-69.076,23.488c-24.877,
1232 0-48.762-8.122-69.078-23.488
1147 0-48.762-8.122-69.078-23.488
1233 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1148 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1234 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1149 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1235 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1150 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1236 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1151 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1237 19.402-10.527 C409.699,390.129,
1152 19.402-10.527 C409.699,390.129,
1238 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1153 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1239 </svg>""".format(
1154 </svg>""".format(
1240 size=self.size,
1155 size=self.size,
1241 background='#979797', # @grey4
1156 background='#979797', # @grey4
1242 text_color=self.text_color,
1157 text_color=self.text_color,
1243 font_family=font_family)
1158 font_family=font_family)
1244
1159
1245 return {
1160 return {
1246 "default_user": default_user
1161 "default_user": default_user
1247 }[img_type]
1162 }[img_type]
1248
1163
1249 def get_img_data(self, svg_type=None):
1164 def get_img_data(self, svg_type=None):
1250 """
1165 """
1251 generates the svg metadata for image
1166 generates the svg metadata for image
1252 """
1167 """
1253 fonts = [
1168 fonts = [
1254 '-apple-system',
1169 '-apple-system',
1255 'BlinkMacSystemFont',
1170 'BlinkMacSystemFont',
1256 'Segoe UI',
1171 'Segoe UI',
1257 'Roboto',
1172 'Roboto',
1258 'Oxygen-Sans',
1173 'Oxygen-Sans',
1259 'Ubuntu',
1174 'Ubuntu',
1260 'Cantarell',
1175 'Cantarell',
1261 'Helvetica Neue',
1176 'Helvetica Neue',
1262 'sans-serif'
1177 'sans-serif'
1263 ]
1178 ]
1264 font_family = ','.join(fonts)
1179 font_family = ','.join(fonts)
1265 if svg_type:
1180 if svg_type:
1266 return self.get_img_data_by_type(font_family, svg_type)
1181 return self.get_img_data_by_type(font_family, svg_type)
1267
1182
1268 initials = self.get_initials()
1183 initials = self.get_initials()
1269 img_data = """
1184 img_data = """
1270 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1185 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1271 width="{size}" height="{size}"
1186 width="{size}" height="{size}"
1272 style="width: 100%; height: 100%; background-color: {background}"
1187 style="width: 100%; height: 100%; background-color: {background}"
1273 viewBox="0 0 {size} {size}">
1188 viewBox="0 0 {size} {size}">
1274 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1189 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1275 pointer-events="auto" fill="{text_color}"
1190 pointer-events="auto" fill="{text_color}"
1276 font-family="{font_family}"
1191 font-family="{font_family}"
1277 style="font-weight: 400; font-size: {f_size}px;">{text}
1192 style="font-weight: 400; font-size: {f_size}px;">{text}
1278 </text>
1193 </text>
1279 </svg>""".format(
1194 </svg>""".format(
1280 size=self.size,
1195 size=self.size,
1281 f_size=self.size/1.85, # scale the text inside the box nicely
1196 f_size=self.size/1.85, # scale the text inside the box nicely
1282 background=self.background,
1197 background=self.background,
1283 text_color=self.text_color,
1198 text_color=self.text_color,
1284 text=initials.upper(),
1199 text=initials.upper(),
1285 font_family=font_family)
1200 font_family=font_family)
1286
1201
1287 return img_data
1202 return img_data
1288
1203
1289 def generate_svg(self, svg_type=None):
1204 def generate_svg(self, svg_type=None):
1290 img_data = self.get_img_data(svg_type)
1205 img_data = self.get_img_data(svg_type)
1291 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1206 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1292
1207
1293
1208
1294 def initials_gravatar(email_address, first_name, last_name, size=30):
1209 def initials_gravatar(email_address, first_name, last_name, size=30):
1295 svg_type = None
1210 svg_type = None
1296 if email_address == User.DEFAULT_USER_EMAIL:
1211 if email_address == User.DEFAULT_USER_EMAIL:
1297 svg_type = 'default_user'
1212 svg_type = 'default_user'
1298 klass = InitialsGravatar(email_address, first_name, last_name, size)
1213 klass = InitialsGravatar(email_address, first_name, last_name, size)
1299 return klass.generate_svg(svg_type=svg_type)
1214 return klass.generate_svg(svg_type=svg_type)
1300
1215
1301
1216
1302 def gravatar_url(email_address, size=30, request=None):
1217 def gravatar_url(email_address, size=30, request=None):
1303 request = get_current_request()
1218 request = get_current_request()
1304 _use_gravatar = request.call_context.visual.use_gravatar
1219 _use_gravatar = request.call_context.visual.use_gravatar
1305 _gravatar_url = request.call_context.visual.gravatar_url
1220 _gravatar_url = request.call_context.visual.gravatar_url
1306
1221
1307 _gravatar_url = _gravatar_url or User.DEFAULT_GRAVATAR_URL
1222 _gravatar_url = _gravatar_url or User.DEFAULT_GRAVATAR_URL
1308
1223
1309 email_address = email_address or User.DEFAULT_USER_EMAIL
1224 email_address = email_address or User.DEFAULT_USER_EMAIL
1310 if isinstance(email_address, unicode):
1225 if isinstance(email_address, unicode):
1311 # hashlib crashes on unicode items
1226 # hashlib crashes on unicode items
1312 email_address = safe_str(email_address)
1227 email_address = safe_str(email_address)
1313
1228
1314 # empty email or default user
1229 # empty email or default user
1315 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1230 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1316 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1231 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1317
1232
1318 if _use_gravatar:
1233 if _use_gravatar:
1319 # TODO: Disuse pyramid thread locals. Think about another solution to
1234 # TODO: Disuse pyramid thread locals. Think about another solution to
1320 # get the host and schema here.
1235 # get the host and schema here.
1321 request = get_current_request()
1236 request = get_current_request()
1322 tmpl = safe_str(_gravatar_url)
1237 tmpl = safe_str(_gravatar_url)
1323 tmpl = tmpl.replace('{email}', email_address)\
1238 tmpl = tmpl.replace('{email}', email_address)\
1324 .replace('{md5email}', md5_safe(email_address.lower())) \
1239 .replace('{md5email}', md5_safe(email_address.lower())) \
1325 .replace('{netloc}', request.host)\
1240 .replace('{netloc}', request.host)\
1326 .replace('{scheme}', request.scheme)\
1241 .replace('{scheme}', request.scheme)\
1327 .replace('{size}', safe_str(size))
1242 .replace('{size}', safe_str(size))
1328 return tmpl
1243 return tmpl
1329 else:
1244 else:
1330 return initials_gravatar(email_address, '', '', size=size)
1245 return initials_gravatar(email_address, '', '', size=size)
1331
1246
1332
1247
1333 class Page(_Page):
1248 class Page(_Page):
1334 """
1249 """
1335 Custom pager to match rendering style with paginator
1250 Custom pager to match rendering style with paginator
1336 """
1251 """
1337
1252
1338 def _get_pos(self, cur_page, max_page, items):
1253 def _get_pos(self, cur_page, max_page, items):
1339 edge = (items / 2) + 1
1254 edge = (items / 2) + 1
1340 if (cur_page <= edge):
1255 if (cur_page <= edge):
1341 radius = max(items / 2, items - cur_page)
1256 radius = max(items / 2, items - cur_page)
1342 elif (max_page - cur_page) < edge:
1257 elif (max_page - cur_page) < edge:
1343 radius = (items - 1) - (max_page - cur_page)
1258 radius = (items - 1) - (max_page - cur_page)
1344 else:
1259 else:
1345 radius = items / 2
1260 radius = items / 2
1346
1261
1347 left = max(1, (cur_page - (radius)))
1262 left = max(1, (cur_page - (radius)))
1348 right = min(max_page, cur_page + (radius))
1263 right = min(max_page, cur_page + (radius))
1349 return left, cur_page, right
1264 return left, cur_page, right
1350
1265
1351 def _range(self, regexp_match):
1266 def _range(self, regexp_match):
1352 """
1267 """
1353 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1268 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1354
1269
1355 Arguments:
1270 Arguments:
1356
1271
1357 regexp_match
1272 regexp_match
1358 A "re" (regular expressions) match object containing the
1273 A "re" (regular expressions) match object containing the
1359 radius of linked pages around the current page in
1274 radius of linked pages around the current page in
1360 regexp_match.group(1) as a string
1275 regexp_match.group(1) as a string
1361
1276
1362 This function is supposed to be called as a callable in
1277 This function is supposed to be called as a callable in
1363 re.sub.
1278 re.sub.
1364
1279
1365 """
1280 """
1366 radius = int(regexp_match.group(1))
1281 radius = int(regexp_match.group(1))
1367
1282
1368 # Compute the first and last page number within the radius
1283 # Compute the first and last page number within the radius
1369 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1284 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1370 # -> leftmost_page = 5
1285 # -> leftmost_page = 5
1371 # -> rightmost_page = 9
1286 # -> rightmost_page = 9
1372 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1287 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1373 self.last_page,
1288 self.last_page,
1374 (radius * 2) + 1)
1289 (radius * 2) + 1)
1375 nav_items = []
1290 nav_items = []
1376
1291
1377 # Create a link to the first page (unless we are on the first page
1292 # Create a link to the first page (unless we are on the first page
1378 # or there would be no need to insert '..' spacers)
1293 # or there would be no need to insert '..' spacers)
1379 if self.page != self.first_page and self.first_page < leftmost_page:
1294 if self.page != self.first_page and self.first_page < leftmost_page:
1380 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1295 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1381
1296
1382 # Insert dots if there are pages between the first page
1297 # Insert dots if there are pages between the first page
1383 # and the currently displayed page range
1298 # and the currently displayed page range
1384 if leftmost_page - self.first_page > 1:
1299 if leftmost_page - self.first_page > 1:
1385 # Wrap in a SPAN tag if nolink_attr is set
1300 # Wrap in a SPAN tag if nolink_attr is set
1386 text = '..'
1301 text = '..'
1387 if self.dotdot_attr:
1302 if self.dotdot_attr:
1388 text = HTML.span(c=text, **self.dotdot_attr)
1303 text = HTML.span(c=text, **self.dotdot_attr)
1389 nav_items.append(text)
1304 nav_items.append(text)
1390
1305
1391 for thispage in xrange(leftmost_page, rightmost_page + 1):
1306 for thispage in xrange(leftmost_page, rightmost_page + 1):
1392 # Hilight the current page number and do not use a link
1307 # Hilight the current page number and do not use a link
1393 if thispage == self.page:
1308 if thispage == self.page:
1394 text = '%s' % (thispage,)
1309 text = '%s' % (thispage,)
1395 # Wrap in a SPAN tag if nolink_attr is set
1310 # Wrap in a SPAN tag if nolink_attr is set
1396 if self.curpage_attr:
1311 if self.curpage_attr:
1397 text = HTML.span(c=text, **self.curpage_attr)
1312 text = HTML.span(c=text, **self.curpage_attr)
1398 nav_items.append(text)
1313 nav_items.append(text)
1399 # Otherwise create just a link to that page
1314 # Otherwise create just a link to that page
1400 else:
1315 else:
1401 text = '%s' % (thispage,)
1316 text = '%s' % (thispage,)
1402 nav_items.append(self._pagerlink(thispage, text))
1317 nav_items.append(self._pagerlink(thispage, text))
1403
1318
1404 # Insert dots if there are pages between the displayed
1319 # Insert dots if there are pages between the displayed
1405 # page numbers and the end of the page range
1320 # page numbers and the end of the page range
1406 if self.last_page - rightmost_page > 1:
1321 if self.last_page - rightmost_page > 1:
1407 text = '..'
1322 text = '..'
1408 # Wrap in a SPAN tag if nolink_attr is set
1323 # Wrap in a SPAN tag if nolink_attr is set
1409 if self.dotdot_attr:
1324 if self.dotdot_attr:
1410 text = HTML.span(c=text, **self.dotdot_attr)
1325 text = HTML.span(c=text, **self.dotdot_attr)
1411 nav_items.append(text)
1326 nav_items.append(text)
1412
1327
1413 # Create a link to the very last page (unless we are on the last
1328 # Create a link to the very last page (unless we are on the last
1414 # page or there would be no need to insert '..' spacers)
1329 # page or there would be no need to insert '..' spacers)
1415 if self.page != self.last_page and rightmost_page < self.last_page:
1330 if self.page != self.last_page and rightmost_page < self.last_page:
1416 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1331 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1417
1332
1418 ## prerender links
1333 ## prerender links
1419 #_page_link = url.current()
1334 #_page_link = url.current()
1420 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1335 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1421 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1336 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1422 return self.separator.join(nav_items)
1337 return self.separator.join(nav_items)
1423
1338
1424 def pager(self, format='~2~', page_param='page', partial_param='partial',
1339 def pager(self, format='~2~', page_param='page', partial_param='partial',
1425 show_if_single_page=False, separator=' ', onclick=None,
1340 show_if_single_page=False, separator=' ', onclick=None,
1426 symbol_first='<<', symbol_last='>>',
1341 symbol_first='<<', symbol_last='>>',
1427 symbol_previous='<', symbol_next='>',
1342 symbol_previous='<', symbol_next='>',
1428 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1343 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1429 curpage_attr={'class': 'pager_curpage'},
1344 curpage_attr={'class': 'pager_curpage'},
1430 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1345 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1431
1346
1432 self.curpage_attr = curpage_attr
1347 self.curpage_attr = curpage_attr
1433 self.separator = separator
1348 self.separator = separator
1434 self.pager_kwargs = kwargs
1349 self.pager_kwargs = kwargs
1435 self.page_param = page_param
1350 self.page_param = page_param
1436 self.partial_param = partial_param
1351 self.partial_param = partial_param
1437 self.onclick = onclick
1352 self.onclick = onclick
1438 self.link_attr = link_attr
1353 self.link_attr = link_attr
1439 self.dotdot_attr = dotdot_attr
1354 self.dotdot_attr = dotdot_attr
1440
1355
1441 # Don't show navigator if there is no more than one page
1356 # Don't show navigator if there is no more than one page
1442 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1357 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1443 return ''
1358 return ''
1444
1359
1445 from string import Template
1360 from string import Template
1446 # Replace ~...~ in token format by range of pages
1361 # Replace ~...~ in token format by range of pages
1447 result = re.sub(r'~(\d+)~', self._range, format)
1362 result = re.sub(r'~(\d+)~', self._range, format)
1448
1363
1449 # Interpolate '%' variables
1364 # Interpolate '%' variables
1450 result = Template(result).safe_substitute({
1365 result = Template(result).safe_substitute({
1451 'first_page': self.first_page,
1366 'first_page': self.first_page,
1452 'last_page': self.last_page,
1367 'last_page': self.last_page,
1453 'page': self.page,
1368 'page': self.page,
1454 'page_count': self.page_count,
1369 'page_count': self.page_count,
1455 'items_per_page': self.items_per_page,
1370 'items_per_page': self.items_per_page,
1456 'first_item': self.first_item,
1371 'first_item': self.first_item,
1457 'last_item': self.last_item,
1372 'last_item': self.last_item,
1458 'item_count': self.item_count,
1373 'item_count': self.item_count,
1459 'link_first': self.page > self.first_page and \
1374 'link_first': self.page > self.first_page and \
1460 self._pagerlink(self.first_page, symbol_first) or '',
1375 self._pagerlink(self.first_page, symbol_first) or '',
1461 'link_last': self.page < self.last_page and \
1376 'link_last': self.page < self.last_page and \
1462 self._pagerlink(self.last_page, symbol_last) or '',
1377 self._pagerlink(self.last_page, symbol_last) or '',
1463 'link_previous': self.previous_page and \
1378 'link_previous': self.previous_page and \
1464 self._pagerlink(self.previous_page, symbol_previous) \
1379 self._pagerlink(self.previous_page, symbol_previous) \
1465 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1380 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1466 'link_next': self.next_page and \
1381 'link_next': self.next_page and \
1467 self._pagerlink(self.next_page, symbol_next) \
1382 self._pagerlink(self.next_page, symbol_next) \
1468 or HTML.span(symbol_next, class_="pg-next disabled")
1383 or HTML.span(symbol_next, class_="pg-next disabled")
1469 })
1384 })
1470
1385
1471 return literal(result)
1386 return literal(result)
1472
1387
1473
1388
1474 #==============================================================================
1389 #==============================================================================
1475 # REPO PAGER, PAGER FOR REPOSITORY
1390 # REPO PAGER, PAGER FOR REPOSITORY
1476 #==============================================================================
1391 #==============================================================================
1477 class RepoPage(Page):
1392 class RepoPage(Page):
1478
1393
1479 def __init__(self, collection, page=1, items_per_page=20,
1394 def __init__(self, collection, page=1, items_per_page=20,
1480 item_count=None, url=None, **kwargs):
1395 item_count=None, url=None, **kwargs):
1481
1396
1482 """Create a "RepoPage" instance. special pager for paging
1397 """Create a "RepoPage" instance. special pager for paging
1483 repository
1398 repository
1484 """
1399 """
1485 self._url_generator = url
1400 self._url_generator = url
1486
1401
1487 # Safe the kwargs class-wide so they can be used in the pager() method
1402 # Safe the kwargs class-wide so they can be used in the pager() method
1488 self.kwargs = kwargs
1403 self.kwargs = kwargs
1489
1404
1490 # Save a reference to the collection
1405 # Save a reference to the collection
1491 self.original_collection = collection
1406 self.original_collection = collection
1492
1407
1493 self.collection = collection
1408 self.collection = collection
1494
1409
1495 # The self.page is the number of the current page.
1410 # The self.page is the number of the current page.
1496 # The first page has the number 1!
1411 # The first page has the number 1!
1497 try:
1412 try:
1498 self.page = int(page) # make it int() if we get it as a string
1413 self.page = int(page) # make it int() if we get it as a string
1499 except (ValueError, TypeError):
1414 except (ValueError, TypeError):
1500 self.page = 1
1415 self.page = 1
1501
1416
1502 self.items_per_page = items_per_page
1417 self.items_per_page = items_per_page
1503
1418
1504 # Unless the user tells us how many items the collections has
1419 # Unless the user tells us how many items the collections has
1505 # we calculate that ourselves.
1420 # we calculate that ourselves.
1506 if item_count is not None:
1421 if item_count is not None:
1507 self.item_count = item_count
1422 self.item_count = item_count
1508 else:
1423 else:
1509 self.item_count = len(self.collection)
1424 self.item_count = len(self.collection)
1510
1425
1511 # Compute the number of the first and last available page
1426 # Compute the number of the first and last available page
1512 if self.item_count > 0:
1427 if self.item_count > 0:
1513 self.first_page = 1
1428 self.first_page = 1
1514 self.page_count = int(math.ceil(float(self.item_count) /
1429 self.page_count = int(math.ceil(float(self.item_count) /
1515 self.items_per_page))
1430 self.items_per_page))
1516 self.last_page = self.first_page + self.page_count - 1
1431 self.last_page = self.first_page + self.page_count - 1
1517
1432
1518 # Make sure that the requested page number is the range of
1433 # Make sure that the requested page number is the range of
1519 # valid pages
1434 # valid pages
1520 if self.page > self.last_page:
1435 if self.page > self.last_page:
1521 self.page = self.last_page
1436 self.page = self.last_page
1522 elif self.page < self.first_page:
1437 elif self.page < self.first_page:
1523 self.page = self.first_page
1438 self.page = self.first_page
1524
1439
1525 # Note: the number of items on this page can be less than
1440 # Note: the number of items on this page can be less than
1526 # items_per_page if the last page is not full
1441 # items_per_page if the last page is not full
1527 self.first_item = max(0, (self.item_count) - (self.page *
1442 self.first_item = max(0, (self.item_count) - (self.page *
1528 items_per_page))
1443 items_per_page))
1529 self.last_item = ((self.item_count - 1) - items_per_page *
1444 self.last_item = ((self.item_count - 1) - items_per_page *
1530 (self.page - 1))
1445 (self.page - 1))
1531
1446
1532 self.items = list(self.collection[self.first_item:self.last_item + 1])
1447 self.items = list(self.collection[self.first_item:self.last_item + 1])
1533
1448
1534 # Links to previous and next page
1449 # Links to previous and next page
1535 if self.page > self.first_page:
1450 if self.page > self.first_page:
1536 self.previous_page = self.page - 1
1451 self.previous_page = self.page - 1
1537 else:
1452 else:
1538 self.previous_page = None
1453 self.previous_page = None
1539
1454
1540 if self.page < self.last_page:
1455 if self.page < self.last_page:
1541 self.next_page = self.page + 1
1456 self.next_page = self.page + 1
1542 else:
1457 else:
1543 self.next_page = None
1458 self.next_page = None
1544
1459
1545 # No items available
1460 # No items available
1546 else:
1461 else:
1547 self.first_page = None
1462 self.first_page = None
1548 self.page_count = 0
1463 self.page_count = 0
1549 self.last_page = None
1464 self.last_page = None
1550 self.first_item = None
1465 self.first_item = None
1551 self.last_item = None
1466 self.last_item = None
1552 self.previous_page = None
1467 self.previous_page = None
1553 self.next_page = None
1468 self.next_page = None
1554 self.items = []
1469 self.items = []
1555
1470
1556 # This is a subclass of the 'list' type. Initialise the list now.
1471 # This is a subclass of the 'list' type. Initialise the list now.
1557 list.__init__(self, reversed(self.items))
1472 list.__init__(self, reversed(self.items))
1558
1473
1559
1474
1560 def breadcrumb_repo_link(repo):
1475 def breadcrumb_repo_link(repo):
1561 """
1476 """
1562 Makes a breadcrumbs path link to repo
1477 Makes a breadcrumbs path link to repo
1563
1478
1564 ex::
1479 ex::
1565 group >> subgroup >> repo
1480 group >> subgroup >> repo
1566
1481
1567 :param repo: a Repository instance
1482 :param repo: a Repository instance
1568 """
1483 """
1569
1484
1570 path = [
1485 path = [
1571 link_to(group.name, route_path('repo_group_home', repo_group_name=group.group_name))
1486 link_to(group.name, route_path('repo_group_home', repo_group_name=group.group_name))
1572 for group in repo.groups_with_parents
1487 for group in repo.groups_with_parents
1573 ] + [
1488 ] + [
1574 link_to(repo.just_name, route_path('repo_summary', repo_name=repo.repo_name))
1489 link_to(repo.just_name, route_path('repo_summary', repo_name=repo.repo_name))
1575 ]
1490 ]
1576
1491
1577 return literal(' &raquo; '.join(path))
1492 return literal(' &raquo; '.join(path))
1578
1493
1579
1494
1580 def format_byte_size_binary(file_size):
1495 def format_byte_size_binary(file_size):
1581 """
1496 """
1582 Formats file/folder sizes to standard.
1497 Formats file/folder sizes to standard.
1583 """
1498 """
1584 if file_size is None:
1499 if file_size is None:
1585 file_size = 0
1500 file_size = 0
1586
1501
1587 formatted_size = format_byte_size(file_size, binary=True)
1502 formatted_size = format_byte_size(file_size, binary=True)
1588 return formatted_size
1503 return formatted_size
1589
1504
1590
1505
1591 def urlify_text(text_, safe=True):
1506 def urlify_text(text_, safe=True):
1592 """
1507 """
1593 Extrac urls from text and make html links out of them
1508 Extrac urls from text and make html links out of them
1594
1509
1595 :param text_:
1510 :param text_:
1596 """
1511 """
1597
1512
1598 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1513 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1599 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1514 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1600
1515
1601 def url_func(match_obj):
1516 def url_func(match_obj):
1602 url_full = match_obj.groups()[0]
1517 url_full = match_obj.groups()[0]
1603 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1518 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1604 _newtext = url_pat.sub(url_func, text_)
1519 _newtext = url_pat.sub(url_func, text_)
1605 if safe:
1520 if safe:
1606 return literal(_newtext)
1521 return literal(_newtext)
1607 return _newtext
1522 return _newtext
1608
1523
1609
1524
1610 def urlify_commits(text_, repository):
1525 def urlify_commits(text_, repository):
1611 """
1526 """
1612 Extract commit ids from text and make link from them
1527 Extract commit ids from text and make link from them
1613
1528
1614 :param text_:
1529 :param text_:
1615 :param repository: repo name to build the URL with
1530 :param repository: repo name to build the URL with
1616 """
1531 """
1617
1532
1618 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1533 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1619
1534
1620 def url_func(match_obj):
1535 def url_func(match_obj):
1621 commit_id = match_obj.groups()[1]
1536 commit_id = match_obj.groups()[1]
1622 pref = match_obj.groups()[0]
1537 pref = match_obj.groups()[0]
1623 suf = match_obj.groups()[2]
1538 suf = match_obj.groups()[2]
1624
1539
1625 tmpl = (
1540 tmpl = (
1626 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1541 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1627 '%(commit_id)s</a>%(suf)s'
1542 '%(commit_id)s</a>%(suf)s'
1628 )
1543 )
1629 return tmpl % {
1544 return tmpl % {
1630 'pref': pref,
1545 'pref': pref,
1631 'cls': 'revision-link',
1546 'cls': 'revision-link',
1632 'url': route_url('repo_commit', repo_name=repository,
1547 'url': route_url('repo_commit', repo_name=repository,
1633 commit_id=commit_id),
1548 commit_id=commit_id),
1634 'commit_id': commit_id,
1549 'commit_id': commit_id,
1635 'suf': suf
1550 'suf': suf
1636 }
1551 }
1637
1552
1638 newtext = URL_PAT.sub(url_func, text_)
1553 newtext = URL_PAT.sub(url_func, text_)
1639
1554
1640 return newtext
1555 return newtext
1641
1556
1642
1557
1643 def _process_url_func(match_obj, repo_name, uid, entry,
1558 def _process_url_func(match_obj, repo_name, uid, entry,
1644 return_raw_data=False, link_format='html'):
1559 return_raw_data=False, link_format='html'):
1645 pref = ''
1560 pref = ''
1646 if match_obj.group().startswith(' '):
1561 if match_obj.group().startswith(' '):
1647 pref = ' '
1562 pref = ' '
1648
1563
1649 issue_id = ''.join(match_obj.groups())
1564 issue_id = ''.join(match_obj.groups())
1650
1565
1651 if link_format == 'html':
1566 if link_format == 'html':
1652 tmpl = (
1567 tmpl = (
1653 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1568 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1654 '%(issue-prefix)s%(id-repr)s'
1569 '%(issue-prefix)s%(id-repr)s'
1655 '</a>')
1570 '</a>')
1656 elif link_format == 'rst':
1571 elif link_format == 'rst':
1657 tmpl = '`%(issue-prefix)s%(id-repr)s <%(url)s>`_'
1572 tmpl = '`%(issue-prefix)s%(id-repr)s <%(url)s>`_'
1658 elif link_format == 'markdown':
1573 elif link_format == 'markdown':
1659 tmpl = '[%(issue-prefix)s%(id-repr)s](%(url)s)'
1574 tmpl = '[%(issue-prefix)s%(id-repr)s](%(url)s)'
1660 else:
1575 else:
1661 raise ValueError('Bad link_format:{}'.format(link_format))
1576 raise ValueError('Bad link_format:{}'.format(link_format))
1662
1577
1663 (repo_name_cleaned,
1578 (repo_name_cleaned,
1664 parent_group_name) = RepoGroupModel().\
1579 parent_group_name) = RepoGroupModel().\
1665 _get_group_name_and_parent(repo_name)
1580 _get_group_name_and_parent(repo_name)
1666
1581
1667 # variables replacement
1582 # variables replacement
1668 named_vars = {
1583 named_vars = {
1669 'id': issue_id,
1584 'id': issue_id,
1670 'repo': repo_name,
1585 'repo': repo_name,
1671 'repo_name': repo_name_cleaned,
1586 'repo_name': repo_name_cleaned,
1672 'group_name': parent_group_name
1587 'group_name': parent_group_name
1673 }
1588 }
1674 # named regex variables
1589 # named regex variables
1675 named_vars.update(match_obj.groupdict())
1590 named_vars.update(match_obj.groupdict())
1676 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1591 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1677
1592
1678 data = {
1593 data = {
1679 'pref': pref,
1594 'pref': pref,
1680 'cls': 'issue-tracker-link',
1595 'cls': 'issue-tracker-link',
1681 'url': _url,
1596 'url': _url,
1682 'id-repr': issue_id,
1597 'id-repr': issue_id,
1683 'issue-prefix': entry['pref'],
1598 'issue-prefix': entry['pref'],
1684 'serv': entry['url'],
1599 'serv': entry['url'],
1685 }
1600 }
1686 if return_raw_data:
1601 if return_raw_data:
1687 return {
1602 return {
1688 'id': issue_id,
1603 'id': issue_id,
1689 'url': _url
1604 'url': _url
1690 }
1605 }
1691 return tmpl % data
1606 return tmpl % data
1692
1607
1693
1608
1694 def get_active_pattern_entries(repo_name):
1609 def get_active_pattern_entries(repo_name):
1695 repo = None
1610 repo = None
1696 if repo_name:
1611 if repo_name:
1697 # Retrieving repo_name to avoid invalid repo_name to explode on
1612 # Retrieving repo_name to avoid invalid repo_name to explode on
1698 # IssueTrackerSettingsModel but still passing invalid name further down
1613 # IssueTrackerSettingsModel but still passing invalid name further down
1699 repo = Repository.get_by_repo_name(repo_name, cache=True)
1614 repo = Repository.get_by_repo_name(repo_name, cache=True)
1700
1615
1701 settings_model = IssueTrackerSettingsModel(repo=repo)
1616 settings_model = IssueTrackerSettingsModel(repo=repo)
1702 active_entries = settings_model.get_settings(cache=True)
1617 active_entries = settings_model.get_settings(cache=True)
1703 return active_entries
1618 return active_entries
1704
1619
1705
1620
1706 def process_patterns(text_string, repo_name, link_format='html',
1621 def process_patterns(text_string, repo_name, link_format='html',
1707 active_entries=None):
1622 active_entries=None):
1708
1623
1709 allowed_formats = ['html', 'rst', 'markdown']
1624 allowed_formats = ['html', 'rst', 'markdown']
1710 if link_format not in allowed_formats:
1625 if link_format not in allowed_formats:
1711 raise ValueError('Link format can be only one of:{} got {}'.format(
1626 raise ValueError('Link format can be only one of:{} got {}'.format(
1712 allowed_formats, link_format))
1627 allowed_formats, link_format))
1713
1628
1714 active_entries = active_entries or get_active_pattern_entries(repo_name)
1629 active_entries = active_entries or get_active_pattern_entries(repo_name)
1715 issues_data = []
1630 issues_data = []
1716 newtext = text_string
1631 newtext = text_string
1717
1632
1718 for uid, entry in active_entries.items():
1633 for uid, entry in active_entries.items():
1719 log.debug('found issue tracker entry with uid %s', uid)
1634 log.debug('found issue tracker entry with uid %s', uid)
1720
1635
1721 if not (entry['pat'] and entry['url']):
1636 if not (entry['pat'] and entry['url']):
1722 log.debug('skipping due to missing data')
1637 log.debug('skipping due to missing data')
1723 continue
1638 continue
1724
1639
1725 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s',
1640 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s',
1726 uid, entry['pat'], entry['url'], entry['pref'])
1641 uid, entry['pat'], entry['url'], entry['pref'])
1727
1642
1728 try:
1643 try:
1729 pattern = re.compile(r'%s' % entry['pat'])
1644 pattern = re.compile(r'%s' % entry['pat'])
1730 except re.error:
1645 except re.error:
1731 log.exception(
1646 log.exception(
1732 'issue tracker pattern: `%s` failed to compile',
1647 'issue tracker pattern: `%s` failed to compile',
1733 entry['pat'])
1648 entry['pat'])
1734 continue
1649 continue
1735
1650
1736 data_func = partial(
1651 data_func = partial(
1737 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1652 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1738 return_raw_data=True)
1653 return_raw_data=True)
1739
1654
1740 for match_obj in pattern.finditer(text_string):
1655 for match_obj in pattern.finditer(text_string):
1741 issues_data.append(data_func(match_obj))
1656 issues_data.append(data_func(match_obj))
1742
1657
1743 url_func = partial(
1658 url_func = partial(
1744 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1659 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1745 link_format=link_format)
1660 link_format=link_format)
1746
1661
1747 newtext = pattern.sub(url_func, newtext)
1662 newtext = pattern.sub(url_func, newtext)
1748 log.debug('processed prefix:uid `%s`', uid)
1663 log.debug('processed prefix:uid `%s`', uid)
1749
1664
1750 return newtext, issues_data
1665 return newtext, issues_data
1751
1666
1752
1667
1753 def urlify_commit_message(commit_text, repository=None,
1668 def urlify_commit_message(commit_text, repository=None,
1754 active_pattern_entries=None):
1669 active_pattern_entries=None):
1755 """
1670 """
1756 Parses given text message and makes proper links.
1671 Parses given text message and makes proper links.
1757 issues are linked to given issue-server, and rest is a commit link
1672 issues are linked to given issue-server, and rest is a commit link
1758
1673
1759 :param commit_text:
1674 :param commit_text:
1760 :param repository:
1675 :param repository:
1761 """
1676 """
1762 def escaper(string):
1677 def escaper(string):
1763 return string.replace('<', '&lt;').replace('>', '&gt;')
1678 return string.replace('<', '&lt;').replace('>', '&gt;')
1764
1679
1765 newtext = escaper(commit_text)
1680 newtext = escaper(commit_text)
1766
1681
1767 # extract http/https links and make them real urls
1682 # extract http/https links and make them real urls
1768 newtext = urlify_text(newtext, safe=False)
1683 newtext = urlify_text(newtext, safe=False)
1769
1684
1770 # urlify commits - extract commit ids and make link out of them, if we have
1685 # urlify commits - extract commit ids and make link out of them, if we have
1771 # the scope of repository present.
1686 # the scope of repository present.
1772 if repository:
1687 if repository:
1773 newtext = urlify_commits(newtext, repository)
1688 newtext = urlify_commits(newtext, repository)
1774
1689
1775 # process issue tracker patterns
1690 # process issue tracker patterns
1776 newtext, issues = process_patterns(newtext, repository or '',
1691 newtext, issues = process_patterns(newtext, repository or '',
1777 active_entries=active_pattern_entries)
1692 active_entries=active_pattern_entries)
1778
1693
1779 return literal(newtext)
1694 return literal(newtext)
1780
1695
1781
1696
1782 def render_binary(repo_name, file_obj):
1697 def render_binary(repo_name, file_obj):
1783 """
1698 """
1784 Choose how to render a binary file
1699 Choose how to render a binary file
1785 """
1700 """
1786
1701
1787 filename = file_obj.name
1702 filename = file_obj.name
1788
1703
1789 # images
1704 # images
1790 for ext in ['*.png', '*.jpg', '*.ico', '*.gif']:
1705 for ext in ['*.png', '*.jpg', '*.ico', '*.gif']:
1791 if fnmatch.fnmatch(filename, pat=ext):
1706 if fnmatch.fnmatch(filename, pat=ext):
1792 alt = escape(filename)
1707 alt = escape(filename)
1793 src = route_path(
1708 src = route_path(
1794 'repo_file_raw', repo_name=repo_name,
1709 'repo_file_raw', repo_name=repo_name,
1795 commit_id=file_obj.commit.raw_id,
1710 commit_id=file_obj.commit.raw_id,
1796 f_path=file_obj.path)
1711 f_path=file_obj.path)
1797 return literal(
1712 return literal(
1798 '<img class="rendered-binary" alt="{}" src="{}">'.format(alt, src))
1713 '<img class="rendered-binary" alt="{}" src="{}">'.format(alt, src))
1799
1714
1800
1715
1801 def renderer_from_filename(filename, exclude=None):
1716 def renderer_from_filename(filename, exclude=None):
1802 """
1717 """
1803 choose a renderer based on filename, this works only for text based files
1718 choose a renderer based on filename, this works only for text based files
1804 """
1719 """
1805
1720
1806 # ipython
1721 # ipython
1807 for ext in ['*.ipynb']:
1722 for ext in ['*.ipynb']:
1808 if fnmatch.fnmatch(filename, pat=ext):
1723 if fnmatch.fnmatch(filename, pat=ext):
1809 return 'jupyter'
1724 return 'jupyter'
1810
1725
1811 is_markup = MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1726 is_markup = MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1812 if is_markup:
1727 if is_markup:
1813 return is_markup
1728 return is_markup
1814 return None
1729 return None
1815
1730
1816
1731
1817 def render(source, renderer='rst', mentions=False, relative_urls=None,
1732 def render(source, renderer='rst', mentions=False, relative_urls=None,
1818 repo_name=None):
1733 repo_name=None):
1819
1734
1820 def maybe_convert_relative_links(html_source):
1735 def maybe_convert_relative_links(html_source):
1821 if relative_urls:
1736 if relative_urls:
1822 return relative_links(html_source, relative_urls)
1737 return relative_links(html_source, relative_urls)
1823 return html_source
1738 return html_source
1824
1739
1825 if renderer == 'plain':
1740 if renderer == 'plain':
1826 return literal(
1741 return literal(
1827 MarkupRenderer.plain(source, leading_newline=False))
1742 MarkupRenderer.plain(source, leading_newline=False))
1828
1743
1829 elif renderer == 'rst':
1744 elif renderer == 'rst':
1830 if repo_name:
1745 if repo_name:
1831 # process patterns on comments if we pass in repo name
1746 # process patterns on comments if we pass in repo name
1832 source, issues = process_patterns(
1747 source, issues = process_patterns(
1833 source, repo_name, link_format='rst')
1748 source, repo_name, link_format='rst')
1834
1749
1835 return literal(
1750 return literal(
1836 '<div class="rst-block">%s</div>' %
1751 '<div class="rst-block">%s</div>' %
1837 maybe_convert_relative_links(
1752 maybe_convert_relative_links(
1838 MarkupRenderer.rst(source, mentions=mentions)))
1753 MarkupRenderer.rst(source, mentions=mentions)))
1839
1754
1840 elif renderer == 'markdown':
1755 elif renderer == 'markdown':
1841 if repo_name:
1756 if repo_name:
1842 # process patterns on comments if we pass in repo name
1757 # process patterns on comments if we pass in repo name
1843 source, issues = process_patterns(
1758 source, issues = process_patterns(
1844 source, repo_name, link_format='markdown')
1759 source, repo_name, link_format='markdown')
1845
1760
1846 return literal(
1761 return literal(
1847 '<div class="markdown-block">%s</div>' %
1762 '<div class="markdown-block">%s</div>' %
1848 maybe_convert_relative_links(
1763 maybe_convert_relative_links(
1849 MarkupRenderer.markdown(source, flavored=True,
1764 MarkupRenderer.markdown(source, flavored=True,
1850 mentions=mentions)))
1765 mentions=mentions)))
1851
1766
1852 elif renderer == 'jupyter':
1767 elif renderer == 'jupyter':
1853 return literal(
1768 return literal(
1854 '<div class="ipynb">%s</div>' %
1769 '<div class="ipynb">%s</div>' %
1855 maybe_convert_relative_links(
1770 maybe_convert_relative_links(
1856 MarkupRenderer.jupyter(source)))
1771 MarkupRenderer.jupyter(source)))
1857
1772
1858 # None means just show the file-source
1773 # None means just show the file-source
1859 return None
1774 return None
1860
1775
1861
1776
1862 def commit_status(repo, commit_id):
1777 def commit_status(repo, commit_id):
1863 return ChangesetStatusModel().get_status(repo, commit_id)
1778 return ChangesetStatusModel().get_status(repo, commit_id)
1864
1779
1865
1780
1866 def commit_status_lbl(commit_status):
1781 def commit_status_lbl(commit_status):
1867 return dict(ChangesetStatus.STATUSES).get(commit_status)
1782 return dict(ChangesetStatus.STATUSES).get(commit_status)
1868
1783
1869
1784
1870 def commit_time(repo_name, commit_id):
1785 def commit_time(repo_name, commit_id):
1871 repo = Repository.get_by_repo_name(repo_name)
1786 repo = Repository.get_by_repo_name(repo_name)
1872 commit = repo.get_commit(commit_id=commit_id)
1787 commit = repo.get_commit(commit_id=commit_id)
1873 return commit.date
1788 return commit.date
1874
1789
1875
1790
1876 def get_permission_name(key):
1791 def get_permission_name(key):
1877 return dict(Permission.PERMS).get(key)
1792 return dict(Permission.PERMS).get(key)
1878
1793
1879
1794
1880 def journal_filter_help(request):
1795 def journal_filter_help(request):
1881 _ = request.translate
1796 _ = request.translate
1882 from rhodecode.lib.audit_logger import ACTIONS
1797 from rhodecode.lib.audit_logger import ACTIONS
1883 actions = '\n'.join(textwrap.wrap(', '.join(sorted(ACTIONS.keys())), 80))
1798 actions = '\n'.join(textwrap.wrap(', '.join(sorted(ACTIONS.keys())), 80))
1884
1799
1885 return _(
1800 return _(
1886 'Example filter terms:\n' +
1801 'Example filter terms:\n' +
1887 ' repository:vcs\n' +
1802 ' repository:vcs\n' +
1888 ' username:marcin\n' +
1803 ' username:marcin\n' +
1889 ' username:(NOT marcin)\n' +
1804 ' username:(NOT marcin)\n' +
1890 ' action:*push*\n' +
1805 ' action:*push*\n' +
1891 ' ip:127.0.0.1\n' +
1806 ' ip:127.0.0.1\n' +
1892 ' date:20120101\n' +
1807 ' date:20120101\n' +
1893 ' date:[20120101100000 TO 20120102]\n' +
1808 ' date:[20120101100000 TO 20120102]\n' +
1894 '\n' +
1809 '\n' +
1895 'Actions: {actions}\n' +
1810 'Actions: {actions}\n' +
1896 '\n' +
1811 '\n' +
1897 'Generate wildcards using \'*\' character:\n' +
1812 'Generate wildcards using \'*\' character:\n' +
1898 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1813 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1899 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1814 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1900 '\n' +
1815 '\n' +
1901 'Optional AND / OR operators in queries\n' +
1816 'Optional AND / OR operators in queries\n' +
1902 ' "repository:vcs OR repository:test"\n' +
1817 ' "repository:vcs OR repository:test"\n' +
1903 ' "username:test AND repository:test*"\n'
1818 ' "username:test AND repository:test*"\n'
1904 ).format(actions=actions)
1819 ).format(actions=actions)
1905
1820
1906
1821
1907 def search_filter_help(searcher, request):
1908 _ = request.translate
1909
1910 terms = ''
1911 return _(
1912 'Example filter terms for `{searcher}` search:\n' +
1913 '{terms}\n' +
1914 'Generate wildcards using \'*\' character:\n' +
1915 ' "repo_name:vcs*" - search everything starting with \'vcs\'\n' +
1916 ' "repo_name:*vcs*" - search for repository containing \'vcs\'\n' +
1917 '\n' +
1918 'Optional AND / OR operators in queries\n' +
1919 ' "repo_name:vcs OR repo_name:test"\n' +
1920 ' "owner:test AND repo_name:test*"\n' +
1921 'More: {search_doc}'
1922 ).format(searcher=searcher.name,
1923 terms=terms, search_doc=searcher.query_lang_doc)
1924
1925
1926 def not_mapped_error(repo_name):
1822 def not_mapped_error(repo_name):
1927 from rhodecode.translation import _
1823 from rhodecode.translation import _
1928 flash(_('%s repository is not mapped to db perhaps'
1824 flash(_('%s repository is not mapped to db perhaps'
1929 ' it was created or renamed from the filesystem'
1825 ' it was created or renamed from the filesystem'
1930 ' please run the application again'
1826 ' please run the application again'
1931 ' in order to rescan repositories') % repo_name, category='error')
1827 ' in order to rescan repositories') % repo_name, category='error')
1932
1828
1933
1829
1934 def ip_range(ip_addr):
1830 def ip_range(ip_addr):
1935 from rhodecode.model.db import UserIpMap
1831 from rhodecode.model.db import UserIpMap
1936 s, e = UserIpMap._get_ip_range(ip_addr)
1832 s, e = UserIpMap._get_ip_range(ip_addr)
1937 return '%s - %s' % (s, e)
1833 return '%s - %s' % (s, e)
1938
1834
1939
1835
1940 def form(url, method='post', needs_csrf_token=True, **attrs):
1836 def form(url, method='post', needs_csrf_token=True, **attrs):
1941 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1837 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1942 if method.lower() != 'get' and needs_csrf_token:
1838 if method.lower() != 'get' and needs_csrf_token:
1943 raise Exception(
1839 raise Exception(
1944 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1840 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1945 'CSRF token. If the endpoint does not require such token you can ' +
1841 'CSRF token. If the endpoint does not require such token you can ' +
1946 'explicitly set the parameter needs_csrf_token to false.')
1842 'explicitly set the parameter needs_csrf_token to false.')
1947
1843
1948 return wh_form(url, method=method, **attrs)
1844 return wh_form(url, method=method, **attrs)
1949
1845
1950
1846
1951 def secure_form(form_url, method="POST", multipart=False, **attrs):
1847 def secure_form(form_url, method="POST", multipart=False, **attrs):
1952 """Start a form tag that points the action to an url. This
1848 """Start a form tag that points the action to an url. This
1953 form tag will also include the hidden field containing
1849 form tag will also include the hidden field containing
1954 the auth token.
1850 the auth token.
1955
1851
1956 The url options should be given either as a string, or as a
1852 The url options should be given either as a string, or as a
1957 ``url()`` function. The method for the form defaults to POST.
1853 ``url()`` function. The method for the form defaults to POST.
1958
1854
1959 Options:
1855 Options:
1960
1856
1961 ``multipart``
1857 ``multipart``
1962 If set to True, the enctype is set to "multipart/form-data".
1858 If set to True, the enctype is set to "multipart/form-data".
1963 ``method``
1859 ``method``
1964 The method to use when submitting the form, usually either
1860 The method to use when submitting the form, usually either
1965 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1861 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1966 hidden input with name _method is added to simulate the verb
1862 hidden input with name _method is added to simulate the verb
1967 over POST.
1863 over POST.
1968
1864
1969 """
1865 """
1970 from webhelpers.pylonslib.secure_form import insecure_form
1866 from webhelpers.pylonslib.secure_form import insecure_form
1971
1867
1972 if 'request' in attrs:
1868 if 'request' in attrs:
1973 session = attrs['request'].session
1869 session = attrs['request'].session
1974 del attrs['request']
1870 del attrs['request']
1975 else:
1871 else:
1976 raise ValueError(
1872 raise ValueError(
1977 'Calling this form requires request= to be passed as argument')
1873 'Calling this form requires request= to be passed as argument')
1978
1874
1979 form = insecure_form(form_url, method, multipart, **attrs)
1875 form = insecure_form(form_url, method, multipart, **attrs)
1980 token = literal(
1876 token = literal(
1981 '<input type="hidden" id="{}" name="{}" value="{}">'.format(
1877 '<input type="hidden" id="{}" name="{}" value="{}">'.format(
1982 csrf_token_key, csrf_token_key, get_csrf_token(session)))
1878 csrf_token_key, csrf_token_key, get_csrf_token(session)))
1983
1879
1984 return literal("%s\n%s" % (form, token))
1880 return literal("%s\n%s" % (form, token))
1985
1881
1986
1882
1987 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1883 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1988 select_html = select(name, selected, options, **attrs)
1884 select_html = select(name, selected, options, **attrs)
1989 select2 = """
1885 select2 = """
1990 <script>
1886 <script>
1991 $(document).ready(function() {
1887 $(document).ready(function() {
1992 $('#%s').select2({
1888 $('#%s').select2({
1993 containerCssClass: 'drop-menu',
1889 containerCssClass: 'drop-menu',
1994 dropdownCssClass: 'drop-menu-dropdown',
1890 dropdownCssClass: 'drop-menu-dropdown',
1995 dropdownAutoWidth: true%s
1891 dropdownAutoWidth: true%s
1996 });
1892 });
1997 });
1893 });
1998 </script>
1894 </script>
1999 """
1895 """
2000 filter_option = """,
1896 filter_option = """,
2001 minimumResultsForSearch: -1
1897 minimumResultsForSearch: -1
2002 """
1898 """
2003 input_id = attrs.get('id') or name
1899 input_id = attrs.get('id') or name
2004 filter_enabled = "" if enable_filter else filter_option
1900 filter_enabled = "" if enable_filter else filter_option
2005 select_script = literal(select2 % (input_id, filter_enabled))
1901 select_script = literal(select2 % (input_id, filter_enabled))
2006
1902
2007 return literal(select_html+select_script)
1903 return literal(select_html+select_script)
2008
1904
2009
1905
2010 def get_visual_attr(tmpl_context_var, attr_name):
1906 def get_visual_attr(tmpl_context_var, attr_name):
2011 """
1907 """
2012 A safe way to get a variable from visual variable of template context
1908 A safe way to get a variable from visual variable of template context
2013
1909
2014 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
1910 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
2015 :param attr_name: name of the attribute we fetch from the c.visual
1911 :param attr_name: name of the attribute we fetch from the c.visual
2016 """
1912 """
2017 visual = getattr(tmpl_context_var, 'visual', None)
1913 visual = getattr(tmpl_context_var, 'visual', None)
2018 if not visual:
1914 if not visual:
2019 return
1915 return
2020 else:
1916 else:
2021 return getattr(visual, attr_name, None)
1917 return getattr(visual, attr_name, None)
2022
1918
2023
1919
2024 def get_last_path_part(file_node):
1920 def get_last_path_part(file_node):
2025 if not file_node.path:
1921 if not file_node.path:
2026 return u''
1922 return u''
2027
1923
2028 path = safe_unicode(file_node.path.split('/')[-1])
1924 path = safe_unicode(file_node.path.split('/')[-1])
2029 return u'../' + path
1925 return u'../' + path
2030
1926
2031
1927
2032 def route_url(*args, **kwargs):
1928 def route_url(*args, **kwargs):
2033 """
1929 """
2034 Wrapper around pyramids `route_url` (fully qualified url) function.
1930 Wrapper around pyramids `route_url` (fully qualified url) function.
2035 """
1931 """
2036 req = get_current_request()
1932 req = get_current_request()
2037 return req.route_url(*args, **kwargs)
1933 return req.route_url(*args, **kwargs)
2038
1934
2039
1935
2040 def route_path(*args, **kwargs):
1936 def route_path(*args, **kwargs):
2041 """
1937 """
2042 Wrapper around pyramids `route_path` function.
1938 Wrapper around pyramids `route_path` function.
2043 """
1939 """
2044 req = get_current_request()
1940 req = get_current_request()
2045 return req.route_path(*args, **kwargs)
1941 return req.route_path(*args, **kwargs)
2046
1942
2047
1943
2048 def route_path_or_none(*args, **kwargs):
1944 def route_path_or_none(*args, **kwargs):
2049 try:
1945 try:
2050 return route_path(*args, **kwargs)
1946 return route_path(*args, **kwargs)
2051 except KeyError:
1947 except KeyError:
2052 return None
1948 return None
2053
1949
2054
1950
2055 def current_route_path(request, **kw):
1951 def current_route_path(request, **kw):
2056 new_args = request.GET.mixed()
1952 new_args = request.GET.mixed()
2057 new_args.update(kw)
1953 new_args.update(kw)
2058 return request.current_route_path(_query=new_args)
1954 return request.current_route_path(_query=new_args)
2059
1955
2060
1956
2061 def api_call_example(method, args):
1957 def api_call_example(method, args):
2062 """
1958 """
2063 Generates an API call example via CURL
1959 Generates an API call example via CURL
2064 """
1960 """
2065 args_json = json.dumps(OrderedDict([
1961 args_json = json.dumps(OrderedDict([
2066 ('id', 1),
1962 ('id', 1),
2067 ('auth_token', 'SECRET'),
1963 ('auth_token', 'SECRET'),
2068 ('method', method),
1964 ('method', method),
2069 ('args', args)
1965 ('args', args)
2070 ]))
1966 ]))
2071 return literal(
1967 return literal(
2072 "curl {api_url} -X POST -H 'content-type:text/plain' --data-binary '{data}'"
1968 "curl {api_url} -X POST -H 'content-type:text/plain' --data-binary '{data}'"
2073 "<br/><br/>SECRET can be found in <a href=\"{token_url}\">auth-tokens</a> page, "
1969 "<br/><br/>SECRET can be found in <a href=\"{token_url}\">auth-tokens</a> page, "
2074 "and needs to be of `api calls` role."
1970 "and needs to be of `api calls` role."
2075 .format(
1971 .format(
2076 api_url=route_url('apiv2'),
1972 api_url=route_url('apiv2'),
2077 token_url=route_url('my_account_auth_tokens'),
1973 token_url=route_url('my_account_auth_tokens'),
2078 data=args_json))
1974 data=args_json))
2079
1975
2080
1976
2081 def notification_description(notification, request):
1977 def notification_description(notification, request):
2082 """
1978 """
2083 Generate notification human readable description based on notification type
1979 Generate notification human readable description based on notification type
2084 """
1980 """
2085 from rhodecode.model.notification import NotificationModel
1981 from rhodecode.model.notification import NotificationModel
2086 return NotificationModel().make_description(
1982 return NotificationModel().make_description(
2087 notification, translate=request.translate)
1983 notification, translate=request.translate)
2088
1984
2089
1985
2090 def go_import_header(request, db_repo=None):
1986 def go_import_header(request, db_repo=None):
2091 """
1987 """
2092 Creates a header for go-import functionality in Go Lang
1988 Creates a header for go-import functionality in Go Lang
2093 """
1989 """
2094
1990
2095 if not db_repo:
1991 if not db_repo:
2096 return
1992 return
2097 if 'go-get' not in request.GET:
1993 if 'go-get' not in request.GET:
2098 return
1994 return
2099
1995
2100 clone_url = db_repo.clone_url()
1996 clone_url = db_repo.clone_url()
2101 prefix = re.split(r'^https?:\/\/', clone_url)[-1]
1997 prefix = re.split(r'^https?:\/\/', clone_url)[-1]
2102 # we have a repo and go-get flag,
1998 # we have a repo and go-get flag,
2103 return literal('<meta name="go-import" content="{} {} {}">'.format(
1999 return literal('<meta name="go-import" content="{} {} {}">'.format(
2104 prefix, db_repo.repo_type, clone_url))
2000 prefix, db_repo.repo_type, clone_url))
2105
2001
2106
2002
2107 def reviewer_as_json(*args, **kwargs):
2003 def reviewer_as_json(*args, **kwargs):
2108 from rhodecode.apps.repository.utils import reviewer_as_json as _reviewer_as_json
2004 from rhodecode.apps.repository.utils import reviewer_as_json as _reviewer_as_json
2109 return _reviewer_as_json(*args, **kwargs)
2005 return _reviewer_as_json(*args, **kwargs)
2006
2007
2008 def get_repo_view_type(request):
2009 route_name = request.matched_route.name
2010 route_to_view_type = {
2011 'repo_changelog': 'changelog',
2012 'repo_files': 'files',
2013 'repo_summary': 'summary',
2014 'repo_commit': 'commit'
2015
2016 }
2017 return route_to_view_type.get(route_name)
@@ -1,59 +1,98 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
3 # Copyright (C) 2012-2018 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 Index schema for RhodeCode
22 Index schema for RhodeCode
23 """
23 """
24
24
25 import importlib
25 import importlib
26 import logging
26 import logging
27
27
28 from rhodecode.lib.index.search_utils import normalize_text_for_matching
29
28 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
29
31
30 # leave defaults for backward compat
32 # leave defaults for backward compat
31 default_searcher = 'rhodecode.lib.index.whoosh'
33 default_searcher = 'rhodecode.lib.index.whoosh'
32 default_location = '%(here)s/data/index'
34 default_location = '%(here)s/data/index'
33
35
36 ES_VERSION_2 = '2'
37 ES_VERSION_6 = '6'
38 # for legacy reasons we keep 2 compat as default
39 DEFAULT_ES_VERSION = ES_VERSION_2
34
40
35 class BaseSearch(object):
41 from rhodecode_tools.lib.fts_index.elasticsearch_engine_6 import \
42 ES_CONFIG # pragma: no cover
43
44
45 class BaseSearcher(object):
36 query_lang_doc = ''
46 query_lang_doc = ''
47 es_version = None
48 name = None
37
49
38 def __init__(self):
50 def __init__(self):
39 pass
51 pass
40
52
41 def cleanup(self):
53 def cleanup(self):
42 pass
54 pass
43
55
44 def search(self, query, document_type, search_user, repo_name=None,
56 def search(self, query, document_type, search_user, repo_name=None,
45 raise_on_exc=True):
57 raise_on_exc=True):
46 raise Exception('NotImplemented')
58 raise Exception('NotImplemented')
47
59
60 @staticmethod
61 def query_to_mark(query, default_field=None):
62 """
63 Formats the query to mark token for jquery.mark.js highlighting. ES could
64 have a different format optionally.
48
65
49 def searcher_from_config(config, prefix='search.'):
66 :param default_field:
67 :param query:
68 """
69 return ' '.join(normalize_text_for_matching(query).split())
70
71 @property
72 def is_es_6(self):
73 return self.es_version == ES_VERSION_6
74
75 def get_handlers(self):
76 return {}
77
78
79 def search_config(config, prefix='search.'):
50 _config = {}
80 _config = {}
51 for key in config.keys():
81 for key in config.keys():
52 if key.startswith(prefix):
82 if key.startswith(prefix):
53 _config[key[len(prefix):]] = config[key]
83 _config[key[len(prefix):]] = config[key]
84 return _config
85
86
87 def searcher_from_config(config, prefix='search.'):
88 _config = search_config(config, prefix)
54
89
55 if 'location' not in _config:
90 if 'location' not in _config:
56 _config['location'] = default_location
91 _config['location'] = default_location
92 if 'es_version' not in _config:
93 # use old legacy ES version set to 2
94 _config['es_version'] = '2'
95
57 imported = importlib.import_module(_config.get('module', default_searcher))
96 imported = importlib.import_module(_config.get('module', default_searcher))
58 searcher = imported.Search(config=_config)
97 searcher = imported.Searcher(config=_config)
59 return searcher
98 return searcher
@@ -1,280 +1,286 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
3 # Copyright (C) 2012-2018 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 Index schema for RhodeCode
22 Index schema for RhodeCode
23 """
23 """
24
24
25 from __future__ import absolute_import
25 from __future__ import absolute_import
26 import os
26 import os
27 import re
27 import re
28 import logging
28 import logging
29
29
30 from whoosh import query as query_lib
30 from whoosh import query as query_lib
31 from whoosh.highlight import HtmlFormatter, ContextFragmenter
31 from whoosh.highlight import HtmlFormatter, ContextFragmenter
32 from whoosh.index import create_in, open_dir, exists_in, EmptyIndexError
32 from whoosh.index import create_in, open_dir, exists_in, EmptyIndexError
33 from whoosh.qparser import QueryParser, QueryParserError
33 from whoosh.qparser import QueryParser, QueryParserError
34
34
35 import rhodecode.lib.helpers as h
35 import rhodecode.lib.helpers as h
36 from rhodecode.lib.index import BaseSearch
36 from rhodecode.lib.index import BaseSearcher
37 from rhodecode.lib.utils2 import safe_unicode
37 from rhodecode.lib.utils2 import safe_unicode
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 try:
42 try:
43 # we first try to import from rhodecode tools, fallback to copies if
43 # we first try to import from rhodecode tools, fallback to copies if
44 # we're unable to
44 # we're unable to
45 from rhodecode_tools.lib.fts_index.whoosh_schema import (
45 from rhodecode_tools.lib.fts_index.whoosh_schema import (
46 ANALYZER, FILE_INDEX_NAME, FILE_SCHEMA, COMMIT_INDEX_NAME,
46 ANALYZER, FILE_INDEX_NAME, FILE_SCHEMA, COMMIT_INDEX_NAME,
47 COMMIT_SCHEMA)
47 COMMIT_SCHEMA)
48 except ImportError:
48 except ImportError:
49 log.warning('rhodecode_tools schema not available, doing a fallback '
49 log.warning('rhodecode_tools schema not available, doing a fallback '
50 'import from `rhodecode.lib.index.whoosh_fallback_schema`')
50 'import from `rhodecode.lib.index.whoosh_fallback_schema`')
51 from rhodecode.lib.index.whoosh_fallback_schema import (
51 from rhodecode.lib.index.whoosh_fallback_schema import (
52 ANALYZER, FILE_INDEX_NAME, FILE_SCHEMA, COMMIT_INDEX_NAME,
52 ANALYZER, FILE_INDEX_NAME, FILE_SCHEMA, COMMIT_INDEX_NAME,
53 COMMIT_SCHEMA)
53 COMMIT_SCHEMA)
54
54
55
55
56 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
56 FORMATTER = HtmlFormatter('span', between='\n<span class="break">...</span>\n')
57 FRAGMENTER = ContextFragmenter(200)
57 FRAGMENTER = ContextFragmenter(200)
58
58
59 log = logging.getLogger(__name__)
59 log = logging.getLogger(__name__)
60
60
61
61
62 class Search(BaseSearch):
62 class WhooshSearcher(BaseSearcher):
63 # this also shows in UI
63 # this also shows in UI
64 query_lang_doc = 'http://whoosh.readthedocs.io/en/latest/querylang.html'
64 query_lang_doc = 'http://whoosh.readthedocs.io/en/latest/querylang.html'
65 name = 'whoosh'
65 name = 'whoosh'
66
66
67 def __init__(self, config):
67 def __init__(self, config):
68 super(Search, self).__init__()
68 super(Searcher, self).__init__()
69 self.config = config
69 self.config = config
70 if not os.path.isdir(self.config['location']):
70 if not os.path.isdir(self.config['location']):
71 os.makedirs(self.config['location'])
71 os.makedirs(self.config['location'])
72
72
73 opener = create_in
73 opener = create_in
74 if exists_in(self.config['location'], indexname=FILE_INDEX_NAME):
74 if exists_in(self.config['location'], indexname=FILE_INDEX_NAME):
75 opener = open_dir
75 opener = open_dir
76 file_index = opener(self.config['location'], schema=FILE_SCHEMA,
76 file_index = opener(self.config['location'], schema=FILE_SCHEMA,
77 indexname=FILE_INDEX_NAME)
77 indexname=FILE_INDEX_NAME)
78
78
79 opener = create_in
79 opener = create_in
80 if exists_in(self.config['location'], indexname=COMMIT_INDEX_NAME):
80 if exists_in(self.config['location'], indexname=COMMIT_INDEX_NAME):
81 opener = open_dir
81 opener = open_dir
82 changeset_index = opener(self.config['location'], schema=COMMIT_SCHEMA,
82 changeset_index = opener(self.config['location'], schema=COMMIT_SCHEMA,
83 indexname=COMMIT_INDEX_NAME)
83 indexname=COMMIT_INDEX_NAME)
84
84
85 self.commit_schema = COMMIT_SCHEMA
85 self.commit_schema = COMMIT_SCHEMA
86 self.commit_index = changeset_index
86 self.commit_index = changeset_index
87 self.file_schema = FILE_SCHEMA
87 self.file_schema = FILE_SCHEMA
88 self.file_index = file_index
88 self.file_index = file_index
89 self.searcher = None
89 self.searcher = None
90
90
91 def cleanup(self):
91 def cleanup(self):
92 if self.searcher:
92 if self.searcher:
93 self.searcher.close()
93 self.searcher.close()
94
94
95 def _extend_query(self, query):
95 def _extend_query(self, query):
96 hashes = re.compile('([0-9a-f]{5,40})').findall(query)
96 hashes = re.compile('([0-9a-f]{5,40})').findall(query)
97 if hashes:
97 if hashes:
98 hashes_or_query = ' OR '.join('commit_id:%s*' % h for h in hashes)
98 hashes_or_query = ' OR '.join('commit_id:%s*' % h for h in hashes)
99 query = u'(%s) OR %s' % (query, hashes_or_query)
99 query = u'(%s) OR %s' % (query, hashes_or_query)
100 return query
100 return query
101
101
102 def search(self, query, document_type, search_user,
102 def search(self, query, document_type, search_user,
103 repo_name=None, requested_page=1, page_limit=10, sort=None,
103 repo_name=None, requested_page=1, page_limit=10, sort=None,
104 raise_on_exc=True):
104 raise_on_exc=True):
105
105
106 original_query = query
106 original_query = query
107 query = self._extend_query(query)
107 query = self._extend_query(query)
108
108
109 log.debug(u'QUERY: %s on %s', query, document_type)
109 log.debug(u'QUERY: %s on %s', query, document_type)
110 result = {
110 result = {
111 'results': [],
111 'results': [],
112 'count': 0,
112 'count': 0,
113 'error': None,
113 'error': None,
114 'runtime': 0
114 'runtime': 0
115 }
115 }
116 search_type, index_name, schema_defn = self._prepare_for_search(
116 search_type, index_name, schema_defn = self._prepare_for_search(
117 document_type)
117 document_type)
118 self._init_searcher(index_name)
118 self._init_searcher(index_name)
119 try:
119 try:
120 qp = QueryParser(search_type, schema=schema_defn)
120 qp = QueryParser(search_type, schema=schema_defn)
121 allowed_repos_filter = self._get_repo_filter(
121 allowed_repos_filter = self._get_repo_filter(
122 search_user, repo_name)
122 search_user, repo_name)
123 try:
123 try:
124 query = qp.parse(safe_unicode(query))
124 query = qp.parse(safe_unicode(query))
125 log.debug('query: %s (%s)', query, repr(query))
125 log.debug('query: %s (%s)', query, repr(query))
126
126
127 reverse, sortedby = False, None
127 reverse, sortedby = False, None
128 if search_type == 'message':
128 if search_type == 'message':
129 if sort == 'oldfirst':
129 if sort == 'oldfirst':
130 sortedby = 'date'
130 sortedby = 'date'
131 reverse = False
131 reverse = False
132 elif sort == 'newfirst':
132 elif sort == 'newfirst':
133 sortedby = 'date'
133 sortedby = 'date'
134 reverse = True
134 reverse = True
135
135
136 whoosh_results = self.searcher.search(
136 whoosh_results = self.searcher.search(
137 query, filter=allowed_repos_filter, limit=None,
137 query, filter=allowed_repos_filter, limit=None,
138 sortedby=sortedby, reverse=reverse)
138 sortedby=sortedby, reverse=reverse)
139
139
140 # fixes for 32k limit that whoosh uses for highlight
140 # fixes for 32k limit that whoosh uses for highlight
141 whoosh_results.fragmenter.charlimit = None
141 whoosh_results.fragmenter.charlimit = None
142 res_ln = whoosh_results.scored_length()
142 res_ln = whoosh_results.scored_length()
143 result['runtime'] = whoosh_results.runtime
143 result['runtime'] = whoosh_results.runtime
144 result['count'] = res_ln
144 result['count'] = res_ln
145 result['results'] = WhooshResultWrapper(
145 result['results'] = WhooshResultWrapper(
146 search_type, res_ln, whoosh_results)
146 search_type, res_ln, whoosh_results)
147
147
148 except QueryParserError:
148 except QueryParserError:
149 result['error'] = 'Invalid search query. Try quoting it.'
149 result['error'] = 'Invalid search query. Try quoting it.'
150 except (EmptyIndexError, IOError, OSError):
150 except (EmptyIndexError, IOError, OSError):
151 msg = 'There is no index to search in. Please run whoosh indexer'
151 msg = 'There is no index to search in. Please run whoosh indexer'
152 log.exception(msg)
152 log.exception(msg)
153 result['error'] = msg
153 result['error'] = msg
154 except Exception:
154 except Exception:
155 msg = 'An error occurred during this search operation'
155 msg = 'An error occurred during this search operation'
156 log.exception(msg)
156 log.exception(msg)
157 result['error'] = msg
157 result['error'] = msg
158
158
159 return result
159 return result
160
160
161 def statistics(self, translator):
161 def statistics(self, translator):
162 _ = translator
162 _ = translator
163 stats = [
163 stats = [
164 {'key': _('Index Type'), 'value': 'Whoosh'},
164 {'key': _('Index Type'), 'value': 'Whoosh'},
165 {'sep': True},
166
165 {'key': _('File Index'), 'value': str(self.file_index)},
167 {'key': _('File Index'), 'value': str(self.file_index)},
166 {'key': _('Indexed documents'),
168 {'key': _('Indexed documents'), 'value': self.file_index.doc_count()},
167 'value': self.file_index.doc_count()},
169 {'key': _('Last update'), 'value': h.time_to_datetime(self.file_index.last_modified())},
168 {'key': _('Last update'),
170
169 'value': h.time_to_datetime(self.file_index.last_modified())},
171 {'sep': True},
172
170 {'key': _('Commit index'), 'value': str(self.commit_index)},
173 {'key': _('Commit index'), 'value': str(self.commit_index)},
171 {'key': _('Indexed documents'),
174 {'key': _('Indexed documents'), 'value': str(self.commit_index.doc_count())},
172 'value': str(self.commit_index.doc_count())},
175 {'key': _('Last update'), 'value': h.time_to_datetime(self.commit_index.last_modified())}
173 {'key': _('Last update'),
174 'value': h.time_to_datetime(self.commit_index.last_modified())}
175 ]
176 ]
176 return stats
177 return stats
177
178
178 def _get_repo_filter(self, auth_user, repo_name):
179 def _get_repo_filter(self, auth_user, repo_name):
179
180
180 allowed_to_search = [
181 allowed_to_search = [
181 repo for repo, perm in
182 repo for repo, perm in
182 auth_user.permissions['repositories'].items()
183 auth_user.permissions['repositories'].items()
183 if perm != 'repository.none']
184 if perm != 'repository.none']
184
185
185 if repo_name:
186 if repo_name:
186 repo_filter = [query_lib.Term('repository', repo_name)]
187 repo_filter = [query_lib.Term('repository', repo_name)]
187
188
188 elif 'hg.admin' in auth_user.permissions.get('global', []):
189 elif 'hg.admin' in auth_user.permissions.get('global', []):
189 return None
190 return None
190
191
191 else:
192 else:
192 repo_filter = [query_lib.Term('repository', _rn)
193 repo_filter = [query_lib.Term('repository', _rn)
193 for _rn in allowed_to_search]
194 for _rn in allowed_to_search]
194 # in case we're not allowed to search anywhere, it's a trick
195 # in case we're not allowed to search anywhere, it's a trick
195 # to tell whoosh we're filtering, on ALL results
196 # to tell whoosh we're filtering, on ALL results
196 repo_filter = repo_filter or [query_lib.Term('repository', '')]
197 repo_filter = repo_filter or [query_lib.Term('repository', '')]
197
198
198 return query_lib.Or(repo_filter)
199 return query_lib.Or(repo_filter)
199
200
200 def _prepare_for_search(self, cur_type):
201 def _prepare_for_search(self, cur_type):
201 search_type = {
202 search_type = {
202 'content': 'content',
203 'content': 'content',
203 'commit': 'message',
204 'commit': 'message',
204 'path': 'path',
205 'path': 'path',
205 'repository': 'repository'
206 'repository': 'repository'
206 }.get(cur_type, 'content')
207 }.get(cur_type, 'content')
207
208
208 index_name = {
209 index_name = {
209 'content': FILE_INDEX_NAME,
210 'content': FILE_INDEX_NAME,
210 'commit': COMMIT_INDEX_NAME,
211 'commit': COMMIT_INDEX_NAME,
211 'path': FILE_INDEX_NAME
212 'path': FILE_INDEX_NAME
212 }.get(cur_type, FILE_INDEX_NAME)
213 }.get(cur_type, FILE_INDEX_NAME)
213
214
214 schema_defn = {
215 schema_defn = {
215 'content': self.file_schema,
216 'content': self.file_schema,
216 'commit': self.commit_schema,
217 'commit': self.commit_schema,
217 'path': self.file_schema
218 'path': self.file_schema
218 }.get(cur_type, self.file_schema)
219 }.get(cur_type, self.file_schema)
219
220
220 log.debug('IDX: %s', index_name)
221 log.debug('IDX: %s', index_name)
221 log.debug('SCHEMA: %s', schema_defn)
222 log.debug('SCHEMA: %s', schema_defn)
222 return search_type, index_name, schema_defn
223 return search_type, index_name, schema_defn
223
224
224 def _init_searcher(self, index_name):
225 def _init_searcher(self, index_name):
225 idx = open_dir(self.config['location'], indexname=index_name)
226 idx = open_dir(self.config['location'], indexname=index_name)
226 self.searcher = idx.searcher()
227 self.searcher = idx.searcher()
227 return self.searcher
228 return self.searcher
228
229
229
230
231 Searcher = WhooshSearcher
232
233
230 class WhooshResultWrapper(object):
234 class WhooshResultWrapper(object):
231 def __init__(self, search_type, total_hits, results):
235 def __init__(self, search_type, total_hits, results):
232 self.search_type = search_type
236 self.search_type = search_type
233 self.results = results
237 self.results = results
234 self.total_hits = total_hits
238 self.total_hits = total_hits
235
239
236 def __str__(self):
240 def __str__(self):
237 return '<%s at %s>' % (self.__class__.__name__, len(self))
241 return '<%s at %s>' % (self.__class__.__name__, len(self))
238
242
239 def __repr__(self):
243 def __repr__(self):
240 return self.__str__()
244 return self.__str__()
241
245
242 def __len__(self):
246 def __len__(self):
243 return self.total_hits
247 return self.total_hits
244
248
245 def __iter__(self):
249 def __iter__(self):
246 """
250 """
247 Allows Iteration over results,and lazy generate content
251 Allows Iteration over results,and lazy generate content
248
252
249 *Requires* implementation of ``__getitem__`` method.
253 *Requires* implementation of ``__getitem__`` method.
250 """
254 """
251 for hit in self.results:
255 for hit in self.results:
252 yield self.get_full_content(hit)
256 yield self.get_full_content(hit)
253
257
254 def __getitem__(self, key):
258 def __getitem__(self, key):
255 """
259 """
256 Slicing of resultWrapper
260 Slicing of resultWrapper
257 """
261 """
258 i, j = key.start, key.stop
262 i, j = key.start, key.stop
259 for hit in self.results[i:j]:
263 for hit in self.results[i:j]:
260 yield self.get_full_content(hit)
264 yield self.get_full_content(hit)
261
265
262 def get_full_content(self, hit):
266 def get_full_content(self, hit):
263 # TODO: marcink: this feels like an overkill, there's a lot of data
267 # TODO: marcink: this feels like an overkill, there's a lot of data
264 # inside hit object, and we don't need all
268 # inside hit object, and we don't need all
265 res = dict(hit)
269 res = dict(hit)
270 # elastic search uses that, we set it empty so it fallbacks to regular HL logic
271 res['content_highlight'] = ''
266
272
267 f_path = '' # pragma: no cover
273 f_path = '' # pragma: no cover
268 if self.search_type in ['content', 'path']:
274 if self.search_type in ['content', 'path']:
269 f_path = res['path'][len(res['repository']):]
275 f_path = res['path'][len(res['repository']):]
270 f_path = f_path.lstrip(os.sep)
276 f_path = f_path.lstrip(os.sep)
271
277
272 if self.search_type == 'content':
278 if self.search_type == 'content':
273 res.update({'content_short_hl': hit.highlights('content'),
279 res.update({'content_short_hl': hit.highlights('content'),
274 'f_path': f_path})
280 'f_path': f_path})
275 elif self.search_type == 'path':
281 elif self.search_type == 'path':
276 res.update({'f_path': f_path})
282 res.update({'f_path': f_path})
277 elif self.search_type == 'message':
283 elif self.search_type == 'message':
278 res.update({'message_hl': hit.highlights('message')})
284 res.update({'message_hl': hit.highlights('message')})
279
285
280 return res
286 return res
@@ -1,1011 +1,1022 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2018 RhodeCode GmbH
3 # Copyright (C) 2011-2018 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 """
22 """
23 Some simple helper functions
23 Some simple helper functions
24 """
24 """
25
25
26 import collections
26 import collections
27 import datetime
27 import datetime
28 import dateutil.relativedelta
28 import dateutil.relativedelta
29 import hashlib
29 import hashlib
30 import logging
30 import logging
31 import re
31 import re
32 import sys
32 import sys
33 import time
33 import time
34 import urllib
34 import urllib
35 import urlobject
35 import urlobject
36 import uuid
36 import uuid
37 import getpass
37 import getpass
38
38
39 import pygments.lexers
39 import pygments.lexers
40 import sqlalchemy
40 import sqlalchemy
41 import sqlalchemy.engine.url
41 import sqlalchemy.engine.url
42 import sqlalchemy.exc
42 import sqlalchemy.exc
43 import sqlalchemy.sql
43 import sqlalchemy.sql
44 import webob
44 import webob
45 import pyramid.threadlocal
45 import pyramid.threadlocal
46
46
47 import rhodecode
47 import rhodecode
48 from rhodecode.translation import _, _pluralize
48 from rhodecode.translation import _, _pluralize
49
49
50
50
51 def md5(s):
51 def md5(s):
52 return hashlib.md5(s).hexdigest()
52 return hashlib.md5(s).hexdigest()
53
53
54
54
55 def md5_safe(s):
55 def md5_safe(s):
56 return md5(safe_str(s))
56 return md5(safe_str(s))
57
57
58
58
59 def sha1(s):
59 def sha1(s):
60 return hashlib.sha1(s).hexdigest()
60 return hashlib.sha1(s).hexdigest()
61
61
62
62
63 def sha1_safe(s):
63 def sha1_safe(s):
64 return sha1(safe_str(s))
64 return sha1(safe_str(s))
65
65
66
66
67 def __get_lem(extra_mapping=None):
67 def __get_lem(extra_mapping=None):
68 """
68 """
69 Get language extension map based on what's inside pygments lexers
69 Get language extension map based on what's inside pygments lexers
70 """
70 """
71 d = collections.defaultdict(lambda: [])
71 d = collections.defaultdict(lambda: [])
72
72
73 def __clean(s):
73 def __clean(s):
74 s = s.lstrip('*')
74 s = s.lstrip('*')
75 s = s.lstrip('.')
75 s = s.lstrip('.')
76
76
77 if s.find('[') != -1:
77 if s.find('[') != -1:
78 exts = []
78 exts = []
79 start, stop = s.find('['), s.find(']')
79 start, stop = s.find('['), s.find(']')
80
80
81 for suffix in s[start + 1:stop]:
81 for suffix in s[start + 1:stop]:
82 exts.append(s[:s.find('[')] + suffix)
82 exts.append(s[:s.find('[')] + suffix)
83 return [e.lower() for e in exts]
83 return [e.lower() for e in exts]
84 else:
84 else:
85 return [s.lower()]
85 return [s.lower()]
86
86
87 for lx, t in sorted(pygments.lexers.LEXERS.items()):
87 for lx, t in sorted(pygments.lexers.LEXERS.items()):
88 m = map(__clean, t[-2])
88 m = map(__clean, t[-2])
89 if m:
89 if m:
90 m = reduce(lambda x, y: x + y, m)
90 m = reduce(lambda x, y: x + y, m)
91 for ext in m:
91 for ext in m:
92 desc = lx.replace('Lexer', '')
92 desc = lx.replace('Lexer', '')
93 d[ext].append(desc)
93 d[ext].append(desc)
94
94
95 data = dict(d)
95 data = dict(d)
96
96
97 extra_mapping = extra_mapping or {}
97 extra_mapping = extra_mapping or {}
98 if extra_mapping:
98 if extra_mapping:
99 for k, v in extra_mapping.items():
99 for k, v in extra_mapping.items():
100 if k not in data:
100 if k not in data:
101 # register new mapping2lexer
101 # register new mapping2lexer
102 data[k] = [v]
102 data[k] = [v]
103
103
104 return data
104 return data
105
105
106
106
107 def str2bool(_str):
107 def str2bool(_str):
108 """
108 """
109 returns True/False value from given string, it tries to translate the
109 returns True/False value from given string, it tries to translate the
110 string into boolean
110 string into boolean
111
111
112 :param _str: string value to translate into boolean
112 :param _str: string value to translate into boolean
113 :rtype: boolean
113 :rtype: boolean
114 :returns: boolean from given string
114 :returns: boolean from given string
115 """
115 """
116 if _str is None:
116 if _str is None:
117 return False
117 return False
118 if _str in (True, False):
118 if _str in (True, False):
119 return _str
119 return _str
120 _str = str(_str).strip().lower()
120 _str = str(_str).strip().lower()
121 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
121 return _str in ('t', 'true', 'y', 'yes', 'on', '1')
122
122
123
123
124 def aslist(obj, sep=None, strip=True):
124 def aslist(obj, sep=None, strip=True):
125 """
125 """
126 Returns given string separated by sep as list
126 Returns given string separated by sep as list
127
127
128 :param obj:
128 :param obj:
129 :param sep:
129 :param sep:
130 :param strip:
130 :param strip:
131 """
131 """
132 if isinstance(obj, (basestring,)):
132 if isinstance(obj, (basestring,)):
133 lst = obj.split(sep)
133 lst = obj.split(sep)
134 if strip:
134 if strip:
135 lst = [v.strip() for v in lst]
135 lst = [v.strip() for v in lst]
136 return lst
136 return lst
137 elif isinstance(obj, (list, tuple)):
137 elif isinstance(obj, (list, tuple)):
138 return obj
138 return obj
139 elif obj is None:
139 elif obj is None:
140 return []
140 return []
141 else:
141 else:
142 return [obj]
142 return [obj]
143
143
144
144
145 def convert_line_endings(line, mode):
145 def convert_line_endings(line, mode):
146 """
146 """
147 Converts a given line "line end" accordingly to given mode
147 Converts a given line "line end" accordingly to given mode
148
148
149 Available modes are::
149 Available modes are::
150 0 - Unix
150 0 - Unix
151 1 - Mac
151 1 - Mac
152 2 - DOS
152 2 - DOS
153
153
154 :param line: given line to convert
154 :param line: given line to convert
155 :param mode: mode to convert to
155 :param mode: mode to convert to
156 :rtype: str
156 :rtype: str
157 :return: converted line according to mode
157 :return: converted line according to mode
158 """
158 """
159 if mode == 0:
159 if mode == 0:
160 line = line.replace('\r\n', '\n')
160 line = line.replace('\r\n', '\n')
161 line = line.replace('\r', '\n')
161 line = line.replace('\r', '\n')
162 elif mode == 1:
162 elif mode == 1:
163 line = line.replace('\r\n', '\r')
163 line = line.replace('\r\n', '\r')
164 line = line.replace('\n', '\r')
164 line = line.replace('\n', '\r')
165 elif mode == 2:
165 elif mode == 2:
166 line = re.sub('\r(?!\n)|(?<!\r)\n', '\r\n', line)
166 line = re.sub('\r(?!\n)|(?<!\r)\n', '\r\n', line)
167 return line
167 return line
168
168
169
169
170 def detect_mode(line, default):
170 def detect_mode(line, default):
171 """
171 """
172 Detects line break for given line, if line break couldn't be found
172 Detects line break for given line, if line break couldn't be found
173 given default value is returned
173 given default value is returned
174
174
175 :param line: str line
175 :param line: str line
176 :param default: default
176 :param default: default
177 :rtype: int
177 :rtype: int
178 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
178 :return: value of line end on of 0 - Unix, 1 - Mac, 2 - DOS
179 """
179 """
180 if line.endswith('\r\n'):
180 if line.endswith('\r\n'):
181 return 2
181 return 2
182 elif line.endswith('\n'):
182 elif line.endswith('\n'):
183 return 0
183 return 0
184 elif line.endswith('\r'):
184 elif line.endswith('\r'):
185 return 1
185 return 1
186 else:
186 else:
187 return default
187 return default
188
188
189
189
190 def safe_int(val, default=None):
190 def safe_int(val, default=None):
191 """
191 """
192 Returns int() of val if val is not convertable to int use default
192 Returns int() of val if val is not convertable to int use default
193 instead
193 instead
194
194
195 :param val:
195 :param val:
196 :param default:
196 :param default:
197 """
197 """
198
198
199 try:
199 try:
200 val = int(val)
200 val = int(val)
201 except (ValueError, TypeError):
201 except (ValueError, TypeError):
202 val = default
202 val = default
203
203
204 return val
204 return val
205
205
206
206
207 def safe_unicode(str_, from_encoding=None):
207 def safe_unicode(str_, from_encoding=None):
208 """
208 """
209 safe unicode function. Does few trick to turn str_ into unicode
209 safe unicode function. Does few trick to turn str_ into unicode
210
210
211 In case of UnicodeDecode error, we try to return it with encoding detected
211 In case of UnicodeDecode error, we try to return it with encoding detected
212 by chardet library if it fails fallback to unicode with errors replaced
212 by chardet library if it fails fallback to unicode with errors replaced
213
213
214 :param str_: string to decode
214 :param str_: string to decode
215 :rtype: unicode
215 :rtype: unicode
216 :returns: unicode object
216 :returns: unicode object
217 """
217 """
218 if isinstance(str_, unicode):
218 if isinstance(str_, unicode):
219 return str_
219 return str_
220
220
221 if not from_encoding:
221 if not from_encoding:
222 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
222 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
223 'utf8'), sep=',')
223 'utf8'), sep=',')
224 from_encoding = DEFAULT_ENCODINGS
224 from_encoding = DEFAULT_ENCODINGS
225
225
226 if not isinstance(from_encoding, (list, tuple)):
226 if not isinstance(from_encoding, (list, tuple)):
227 from_encoding = [from_encoding]
227 from_encoding = [from_encoding]
228
228
229 try:
229 try:
230 return unicode(str_)
230 return unicode(str_)
231 except UnicodeDecodeError:
231 except UnicodeDecodeError:
232 pass
232 pass
233
233
234 for enc in from_encoding:
234 for enc in from_encoding:
235 try:
235 try:
236 return unicode(str_, enc)
236 return unicode(str_, enc)
237 except UnicodeDecodeError:
237 except UnicodeDecodeError:
238 pass
238 pass
239
239
240 try:
240 try:
241 import chardet
241 import chardet
242 encoding = chardet.detect(str_)['encoding']
242 encoding = chardet.detect(str_)['encoding']
243 if encoding is None:
243 if encoding is None:
244 raise Exception()
244 raise Exception()
245 return str_.decode(encoding)
245 return str_.decode(encoding)
246 except (ImportError, UnicodeDecodeError, Exception):
246 except (ImportError, UnicodeDecodeError, Exception):
247 return unicode(str_, from_encoding[0], 'replace')
247 return unicode(str_, from_encoding[0], 'replace')
248
248
249
249
250 def safe_str(unicode_, to_encoding=None):
250 def safe_str(unicode_, to_encoding=None):
251 """
251 """
252 safe str function. Does few trick to turn unicode_ into string
252 safe str function. Does few trick to turn unicode_ into string
253
253
254 In case of UnicodeEncodeError, we try to return it with encoding detected
254 In case of UnicodeEncodeError, we try to return it with encoding detected
255 by chardet library if it fails fallback to string with errors replaced
255 by chardet library if it fails fallback to string with errors replaced
256
256
257 :param unicode_: unicode to encode
257 :param unicode_: unicode to encode
258 :rtype: str
258 :rtype: str
259 :returns: str object
259 :returns: str object
260 """
260 """
261
261
262 # if it's not basestr cast to str
262 # if it's not basestr cast to str
263 if not isinstance(unicode_, basestring):
263 if not isinstance(unicode_, basestring):
264 return str(unicode_)
264 return str(unicode_)
265
265
266 if isinstance(unicode_, str):
266 if isinstance(unicode_, str):
267 return unicode_
267 return unicode_
268
268
269 if not to_encoding:
269 if not to_encoding:
270 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
270 DEFAULT_ENCODINGS = aslist(rhodecode.CONFIG.get('default_encoding',
271 'utf8'), sep=',')
271 'utf8'), sep=',')
272 to_encoding = DEFAULT_ENCODINGS
272 to_encoding = DEFAULT_ENCODINGS
273
273
274 if not isinstance(to_encoding, (list, tuple)):
274 if not isinstance(to_encoding, (list, tuple)):
275 to_encoding = [to_encoding]
275 to_encoding = [to_encoding]
276
276
277 for enc in to_encoding:
277 for enc in to_encoding:
278 try:
278 try:
279 return unicode_.encode(enc)
279 return unicode_.encode(enc)
280 except UnicodeEncodeError:
280 except UnicodeEncodeError:
281 pass
281 pass
282
282
283 try:
283 try:
284 import chardet
284 import chardet
285 encoding = chardet.detect(unicode_)['encoding']
285 encoding = chardet.detect(unicode_)['encoding']
286 if encoding is None:
286 if encoding is None:
287 raise UnicodeEncodeError()
287 raise UnicodeEncodeError()
288
288
289 return unicode_.encode(encoding)
289 return unicode_.encode(encoding)
290 except (ImportError, UnicodeEncodeError):
290 except (ImportError, UnicodeEncodeError):
291 return unicode_.encode(to_encoding[0], 'replace')
291 return unicode_.encode(to_encoding[0], 'replace')
292
292
293
293
294 def remove_suffix(s, suffix):
294 def remove_suffix(s, suffix):
295 if s.endswith(suffix):
295 if s.endswith(suffix):
296 s = s[:-1 * len(suffix)]
296 s = s[:-1 * len(suffix)]
297 return s
297 return s
298
298
299
299
300 def remove_prefix(s, prefix):
300 def remove_prefix(s, prefix):
301 if s.startswith(prefix):
301 if s.startswith(prefix):
302 s = s[len(prefix):]
302 s = s[len(prefix):]
303 return s
303 return s
304
304
305
305
306 def find_calling_context(ignore_modules=None):
306 def find_calling_context(ignore_modules=None):
307 """
307 """
308 Look through the calling stack and return the frame which called
308 Look through the calling stack and return the frame which called
309 this function and is part of core module ( ie. rhodecode.* )
309 this function and is part of core module ( ie. rhodecode.* )
310
310
311 :param ignore_modules: list of modules to ignore eg. ['rhodecode.lib']
311 :param ignore_modules: list of modules to ignore eg. ['rhodecode.lib']
312 """
312 """
313
313
314 ignore_modules = ignore_modules or []
314 ignore_modules = ignore_modules or []
315
315
316 f = sys._getframe(2)
316 f = sys._getframe(2)
317 while f.f_back is not None:
317 while f.f_back is not None:
318 name = f.f_globals.get('__name__')
318 name = f.f_globals.get('__name__')
319 if name and name.startswith(__name__.split('.')[0]):
319 if name and name.startswith(__name__.split('.')[0]):
320 if name not in ignore_modules:
320 if name not in ignore_modules:
321 return f
321 return f
322 f = f.f_back
322 f = f.f_back
323 return None
323 return None
324
324
325
325
326 def ping_connection(connection, branch):
326 def ping_connection(connection, branch):
327 if branch:
327 if branch:
328 # "branch" refers to a sub-connection of a connection,
328 # "branch" refers to a sub-connection of a connection,
329 # we don't want to bother pinging on these.
329 # we don't want to bother pinging on these.
330 return
330 return
331
331
332 # turn off "close with result". This flag is only used with
332 # turn off "close with result". This flag is only used with
333 # "connectionless" execution, otherwise will be False in any case
333 # "connectionless" execution, otherwise will be False in any case
334 save_should_close_with_result = connection.should_close_with_result
334 save_should_close_with_result = connection.should_close_with_result
335 connection.should_close_with_result = False
335 connection.should_close_with_result = False
336
336
337 try:
337 try:
338 # run a SELECT 1. use a core select() so that
338 # run a SELECT 1. use a core select() so that
339 # the SELECT of a scalar value without a table is
339 # the SELECT of a scalar value without a table is
340 # appropriately formatted for the backend
340 # appropriately formatted for the backend
341 connection.scalar(sqlalchemy.sql.select([1]))
341 connection.scalar(sqlalchemy.sql.select([1]))
342 except sqlalchemy.exc.DBAPIError as err:
342 except sqlalchemy.exc.DBAPIError as err:
343 # catch SQLAlchemy's DBAPIError, which is a wrapper
343 # catch SQLAlchemy's DBAPIError, which is a wrapper
344 # for the DBAPI's exception. It includes a .connection_invalidated
344 # for the DBAPI's exception. It includes a .connection_invalidated
345 # attribute which specifies if this connection is a "disconnect"
345 # attribute which specifies if this connection is a "disconnect"
346 # condition, which is based on inspection of the original exception
346 # condition, which is based on inspection of the original exception
347 # by the dialect in use.
347 # by the dialect in use.
348 if err.connection_invalidated:
348 if err.connection_invalidated:
349 # run the same SELECT again - the connection will re-validate
349 # run the same SELECT again - the connection will re-validate
350 # itself and establish a new connection. The disconnect detection
350 # itself and establish a new connection. The disconnect detection
351 # here also causes the whole connection pool to be invalidated
351 # here also causes the whole connection pool to be invalidated
352 # so that all stale connections are discarded.
352 # so that all stale connections are discarded.
353 connection.scalar(sqlalchemy.sql.select([1]))
353 connection.scalar(sqlalchemy.sql.select([1]))
354 else:
354 else:
355 raise
355 raise
356 finally:
356 finally:
357 # restore "close with result"
357 # restore "close with result"
358 connection.should_close_with_result = save_should_close_with_result
358 connection.should_close_with_result = save_should_close_with_result
359
359
360
360
361 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
361 def engine_from_config(configuration, prefix='sqlalchemy.', **kwargs):
362 """Custom engine_from_config functions."""
362 """Custom engine_from_config functions."""
363 log = logging.getLogger('sqlalchemy.engine')
363 log = logging.getLogger('sqlalchemy.engine')
364 _ping_connection = configuration.pop('sqlalchemy.db1.ping_connection', None)
364 _ping_connection = configuration.pop('sqlalchemy.db1.ping_connection', None)
365
365
366 engine = sqlalchemy.engine_from_config(configuration, prefix, **kwargs)
366 engine = sqlalchemy.engine_from_config(configuration, prefix, **kwargs)
367
367
368 def color_sql(sql):
368 def color_sql(sql):
369 color_seq = '\033[1;33m' # This is yellow: code 33
369 color_seq = '\033[1;33m' # This is yellow: code 33
370 normal = '\x1b[0m'
370 normal = '\x1b[0m'
371 return ''.join([color_seq, sql, normal])
371 return ''.join([color_seq, sql, normal])
372
372
373 if configuration['debug'] or _ping_connection:
373 if configuration['debug'] or _ping_connection:
374 sqlalchemy.event.listen(engine, "engine_connect", ping_connection)
374 sqlalchemy.event.listen(engine, "engine_connect", ping_connection)
375
375
376 if configuration['debug']:
376 if configuration['debug']:
377 # attach events only for debug configuration
377 # attach events only for debug configuration
378
378
379 def before_cursor_execute(conn, cursor, statement,
379 def before_cursor_execute(conn, cursor, statement,
380 parameters, context, executemany):
380 parameters, context, executemany):
381 setattr(conn, 'query_start_time', time.time())
381 setattr(conn, 'query_start_time', time.time())
382 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
382 log.info(color_sql(">>>>> STARTING QUERY >>>>>"))
383 calling_context = find_calling_context(ignore_modules=[
383 calling_context = find_calling_context(ignore_modules=[
384 'rhodecode.lib.caching_query',
384 'rhodecode.lib.caching_query',
385 'rhodecode.model.settings',
385 'rhodecode.model.settings',
386 ])
386 ])
387 if calling_context:
387 if calling_context:
388 log.info(color_sql('call context %s:%s' % (
388 log.info(color_sql('call context %s:%s' % (
389 calling_context.f_code.co_filename,
389 calling_context.f_code.co_filename,
390 calling_context.f_lineno,
390 calling_context.f_lineno,
391 )))
391 )))
392
392
393 def after_cursor_execute(conn, cursor, statement,
393 def after_cursor_execute(conn, cursor, statement,
394 parameters, context, executemany):
394 parameters, context, executemany):
395 delattr(conn, 'query_start_time')
395 delattr(conn, 'query_start_time')
396
396
397 sqlalchemy.event.listen(engine, "before_cursor_execute",
397 sqlalchemy.event.listen(engine, "before_cursor_execute",
398 before_cursor_execute)
398 before_cursor_execute)
399 sqlalchemy.event.listen(engine, "after_cursor_execute",
399 sqlalchemy.event.listen(engine, "after_cursor_execute",
400 after_cursor_execute)
400 after_cursor_execute)
401
401
402 return engine
402 return engine
403
403
404
404
405 def get_encryption_key(config):
405 def get_encryption_key(config):
406 secret = config.get('rhodecode.encrypted_values.secret')
406 secret = config.get('rhodecode.encrypted_values.secret')
407 default = config['beaker.session.secret']
407 default = config['beaker.session.secret']
408 return secret or default
408 return secret or default
409
409
410
410
411 def age(prevdate, now=None, show_short_version=False, show_suffix=True,
411 def age(prevdate, now=None, show_short_version=False, show_suffix=True,
412 short_format=False):
412 short_format=False):
413 """
413 """
414 Turns a datetime into an age string.
414 Turns a datetime into an age string.
415 If show_short_version is True, this generates a shorter string with
415 If show_short_version is True, this generates a shorter string with
416 an approximate age; ex. '1 day ago', rather than '1 day and 23 hours ago'.
416 an approximate age; ex. '1 day ago', rather than '1 day and 23 hours ago'.
417
417
418 * IMPORTANT*
418 * IMPORTANT*
419 Code of this function is written in special way so it's easier to
419 Code of this function is written in special way so it's easier to
420 backport it to javascript. If you mean to update it, please also update
420 backport it to javascript. If you mean to update it, please also update
421 `jquery.timeago-extension.js` file
421 `jquery.timeago-extension.js` file
422
422
423 :param prevdate: datetime object
423 :param prevdate: datetime object
424 :param now: get current time, if not define we use
424 :param now: get current time, if not define we use
425 `datetime.datetime.now()`
425 `datetime.datetime.now()`
426 :param show_short_version: if it should approximate the date and
426 :param show_short_version: if it should approximate the date and
427 return a shorter string
427 return a shorter string
428 :param show_suffix:
428 :param show_suffix:
429 :param short_format: show short format, eg 2D instead of 2 days
429 :param short_format: show short format, eg 2D instead of 2 days
430 :rtype: unicode
430 :rtype: unicode
431 :returns: unicode words describing age
431 :returns: unicode words describing age
432 """
432 """
433
433
434 def _get_relative_delta(now, prevdate):
434 def _get_relative_delta(now, prevdate):
435 base = dateutil.relativedelta.relativedelta(now, prevdate)
435 base = dateutil.relativedelta.relativedelta(now, prevdate)
436 return {
436 return {
437 'year': base.years,
437 'year': base.years,
438 'month': base.months,
438 'month': base.months,
439 'day': base.days,
439 'day': base.days,
440 'hour': base.hours,
440 'hour': base.hours,
441 'minute': base.minutes,
441 'minute': base.minutes,
442 'second': base.seconds,
442 'second': base.seconds,
443 }
443 }
444
444
445 def _is_leap_year(year):
445 def _is_leap_year(year):
446 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
446 return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)
447
447
448 def get_month(prevdate):
448 def get_month(prevdate):
449 return prevdate.month
449 return prevdate.month
450
450
451 def get_year(prevdate):
451 def get_year(prevdate):
452 return prevdate.year
452 return prevdate.year
453
453
454 now = now or datetime.datetime.now()
454 now = now or datetime.datetime.now()
455 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
455 order = ['year', 'month', 'day', 'hour', 'minute', 'second']
456 deltas = {}
456 deltas = {}
457 future = False
457 future = False
458
458
459 if prevdate > now:
459 if prevdate > now:
460 now_old = now
460 now_old = now
461 now = prevdate
461 now = prevdate
462 prevdate = now_old
462 prevdate = now_old
463 future = True
463 future = True
464 if future:
464 if future:
465 prevdate = prevdate.replace(microsecond=0)
465 prevdate = prevdate.replace(microsecond=0)
466 # Get date parts deltas
466 # Get date parts deltas
467 for part in order:
467 for part in order:
468 rel_delta = _get_relative_delta(now, prevdate)
468 rel_delta = _get_relative_delta(now, prevdate)
469 deltas[part] = rel_delta[part]
469 deltas[part] = rel_delta[part]
470
470
471 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
471 # Fix negative offsets (there is 1 second between 10:59:59 and 11:00:00,
472 # not 1 hour, -59 minutes and -59 seconds)
472 # not 1 hour, -59 minutes and -59 seconds)
473 offsets = [[5, 60], [4, 60], [3, 24]]
473 offsets = [[5, 60], [4, 60], [3, 24]]
474 for element in offsets: # seconds, minutes, hours
474 for element in offsets: # seconds, minutes, hours
475 num = element[0]
475 num = element[0]
476 length = element[1]
476 length = element[1]
477
477
478 part = order[num]
478 part = order[num]
479 carry_part = order[num - 1]
479 carry_part = order[num - 1]
480
480
481 if deltas[part] < 0:
481 if deltas[part] < 0:
482 deltas[part] += length
482 deltas[part] += length
483 deltas[carry_part] -= 1
483 deltas[carry_part] -= 1
484
484
485 # Same thing for days except that the increment depends on the (variable)
485 # Same thing for days except that the increment depends on the (variable)
486 # number of days in the month
486 # number of days in the month
487 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
487 month_lengths = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
488 if deltas['day'] < 0:
488 if deltas['day'] < 0:
489 if get_month(prevdate) == 2 and _is_leap_year(get_year(prevdate)):
489 if get_month(prevdate) == 2 and _is_leap_year(get_year(prevdate)):
490 deltas['day'] += 29
490 deltas['day'] += 29
491 else:
491 else:
492 deltas['day'] += month_lengths[get_month(prevdate) - 1]
492 deltas['day'] += month_lengths[get_month(prevdate) - 1]
493
493
494 deltas['month'] -= 1
494 deltas['month'] -= 1
495
495
496 if deltas['month'] < 0:
496 if deltas['month'] < 0:
497 deltas['month'] += 12
497 deltas['month'] += 12
498 deltas['year'] -= 1
498 deltas['year'] -= 1
499
499
500 # Format the result
500 # Format the result
501 if short_format:
501 if short_format:
502 fmt_funcs = {
502 fmt_funcs = {
503 'year': lambda d: u'%dy' % d,
503 'year': lambda d: u'%dy' % d,
504 'month': lambda d: u'%dm' % d,
504 'month': lambda d: u'%dm' % d,
505 'day': lambda d: u'%dd' % d,
505 'day': lambda d: u'%dd' % d,
506 'hour': lambda d: u'%dh' % d,
506 'hour': lambda d: u'%dh' % d,
507 'minute': lambda d: u'%dmin' % d,
507 'minute': lambda d: u'%dmin' % d,
508 'second': lambda d: u'%dsec' % d,
508 'second': lambda d: u'%dsec' % d,
509 }
509 }
510 else:
510 else:
511 fmt_funcs = {
511 fmt_funcs = {
512 'year': lambda d: _pluralize(u'${num} year', u'${num} years', d, mapping={'num': d}).interpolate(),
512 'year': lambda d: _pluralize(u'${num} year', u'${num} years', d, mapping={'num': d}).interpolate(),
513 'month': lambda d: _pluralize(u'${num} month', u'${num} months', d, mapping={'num': d}).interpolate(),
513 'month': lambda d: _pluralize(u'${num} month', u'${num} months', d, mapping={'num': d}).interpolate(),
514 'day': lambda d: _pluralize(u'${num} day', u'${num} days', d, mapping={'num': d}).interpolate(),
514 'day': lambda d: _pluralize(u'${num} day', u'${num} days', d, mapping={'num': d}).interpolate(),
515 'hour': lambda d: _pluralize(u'${num} hour', u'${num} hours', d, mapping={'num': d}).interpolate(),
515 'hour': lambda d: _pluralize(u'${num} hour', u'${num} hours', d, mapping={'num': d}).interpolate(),
516 'minute': lambda d: _pluralize(u'${num} minute', u'${num} minutes', d, mapping={'num': d}).interpolate(),
516 'minute': lambda d: _pluralize(u'${num} minute', u'${num} minutes', d, mapping={'num': d}).interpolate(),
517 'second': lambda d: _pluralize(u'${num} second', u'${num} seconds', d, mapping={'num': d}).interpolate(),
517 'second': lambda d: _pluralize(u'${num} second', u'${num} seconds', d, mapping={'num': d}).interpolate(),
518 }
518 }
519
519
520 i = 0
520 i = 0
521 for part in order:
521 for part in order:
522 value = deltas[part]
522 value = deltas[part]
523 if value != 0:
523 if value != 0:
524
524
525 if i < 5:
525 if i < 5:
526 sub_part = order[i + 1]
526 sub_part = order[i + 1]
527 sub_value = deltas[sub_part]
527 sub_value = deltas[sub_part]
528 else:
528 else:
529 sub_value = 0
529 sub_value = 0
530
530
531 if sub_value == 0 or show_short_version:
531 if sub_value == 0 or show_short_version:
532 _val = fmt_funcs[part](value)
532 _val = fmt_funcs[part](value)
533 if future:
533 if future:
534 if show_suffix:
534 if show_suffix:
535 return _(u'in ${ago}', mapping={'ago': _val})
535 return _(u'in ${ago}', mapping={'ago': _val})
536 else:
536 else:
537 return _(_val)
537 return _(_val)
538
538
539 else:
539 else:
540 if show_suffix:
540 if show_suffix:
541 return _(u'${ago} ago', mapping={'ago': _val})
541 return _(u'${ago} ago', mapping={'ago': _val})
542 else:
542 else:
543 return _(_val)
543 return _(_val)
544
544
545 val = fmt_funcs[part](value)
545 val = fmt_funcs[part](value)
546 val_detail = fmt_funcs[sub_part](sub_value)
546 val_detail = fmt_funcs[sub_part](sub_value)
547 mapping = {'val': val, 'detail': val_detail}
547 mapping = {'val': val, 'detail': val_detail}
548
548
549 if short_format:
549 if short_format:
550 datetime_tmpl = _(u'${val}, ${detail}', mapping=mapping)
550 datetime_tmpl = _(u'${val}, ${detail}', mapping=mapping)
551 if show_suffix:
551 if show_suffix:
552 datetime_tmpl = _(u'${val}, ${detail} ago', mapping=mapping)
552 datetime_tmpl = _(u'${val}, ${detail} ago', mapping=mapping)
553 if future:
553 if future:
554 datetime_tmpl = _(u'in ${val}, ${detail}', mapping=mapping)
554 datetime_tmpl = _(u'in ${val}, ${detail}', mapping=mapping)
555 else:
555 else:
556 datetime_tmpl = _(u'${val} and ${detail}', mapping=mapping)
556 datetime_tmpl = _(u'${val} and ${detail}', mapping=mapping)
557 if show_suffix:
557 if show_suffix:
558 datetime_tmpl = _(u'${val} and ${detail} ago', mapping=mapping)
558 datetime_tmpl = _(u'${val} and ${detail} ago', mapping=mapping)
559 if future:
559 if future:
560 datetime_tmpl = _(u'in ${val} and ${detail}', mapping=mapping)
560 datetime_tmpl = _(u'in ${val} and ${detail}', mapping=mapping)
561
561
562 return datetime_tmpl
562 return datetime_tmpl
563 i += 1
563 i += 1
564 return _(u'just now')
564 return _(u'just now')
565
565
566
566
567 def cleaned_uri(uri):
567 def cleaned_uri(uri):
568 """
568 """
569 Quotes '[' and ']' from uri if there is only one of them.
569 Quotes '[' and ']' from uri if there is only one of them.
570 according to RFC3986 we cannot use such chars in uri
570 according to RFC3986 we cannot use such chars in uri
571 :param uri:
571 :param uri:
572 :return: uri without this chars
572 :return: uri without this chars
573 """
573 """
574 return urllib.quote(uri, safe='@$:/')
574 return urllib.quote(uri, safe='@$:/')
575
575
576
576
577 def uri_filter(uri):
577 def uri_filter(uri):
578 """
578 """
579 Removes user:password from given url string
579 Removes user:password from given url string
580
580
581 :param uri:
581 :param uri:
582 :rtype: unicode
582 :rtype: unicode
583 :returns: filtered list of strings
583 :returns: filtered list of strings
584 """
584 """
585 if not uri:
585 if not uri:
586 return ''
586 return ''
587
587
588 proto = ''
588 proto = ''
589
589
590 for pat in ('https://', 'http://'):
590 for pat in ('https://', 'http://'):
591 if uri.startswith(pat):
591 if uri.startswith(pat):
592 uri = uri[len(pat):]
592 uri = uri[len(pat):]
593 proto = pat
593 proto = pat
594 break
594 break
595
595
596 # remove passwords and username
596 # remove passwords and username
597 uri = uri[uri.find('@') + 1:]
597 uri = uri[uri.find('@') + 1:]
598
598
599 # get the port
599 # get the port
600 cred_pos = uri.find(':')
600 cred_pos = uri.find(':')
601 if cred_pos == -1:
601 if cred_pos == -1:
602 host, port = uri, None
602 host, port = uri, None
603 else:
603 else:
604 host, port = uri[:cred_pos], uri[cred_pos + 1:]
604 host, port = uri[:cred_pos], uri[cred_pos + 1:]
605
605
606 return filter(None, [proto, host, port])
606 return filter(None, [proto, host, port])
607
607
608
608
609 def credentials_filter(uri):
609 def credentials_filter(uri):
610 """
610 """
611 Returns a url with removed credentials
611 Returns a url with removed credentials
612
612
613 :param uri:
613 :param uri:
614 """
614 """
615
615
616 uri = uri_filter(uri)
616 uri = uri_filter(uri)
617 # check if we have port
617 # check if we have port
618 if len(uri) > 2 and uri[2]:
618 if len(uri) > 2 and uri[2]:
619 uri[2] = ':' + uri[2]
619 uri[2] = ':' + uri[2]
620
620
621 return ''.join(uri)
621 return ''.join(uri)
622
622
623
623
624 def get_clone_url(request, uri_tmpl, repo_name, repo_id, **override):
624 def get_clone_url(request, uri_tmpl, repo_name, repo_id, **override):
625 qualifed_home_url = request.route_url('home')
625 qualifed_home_url = request.route_url('home')
626 parsed_url = urlobject.URLObject(qualifed_home_url)
626 parsed_url = urlobject.URLObject(qualifed_home_url)
627 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
627 decoded_path = safe_unicode(urllib.unquote(parsed_url.path.rstrip('/')))
628
628
629 args = {
629 args = {
630 'scheme': parsed_url.scheme,
630 'scheme': parsed_url.scheme,
631 'user': '',
631 'user': '',
632 'sys_user': getpass.getuser(),
632 'sys_user': getpass.getuser(),
633 # path if we use proxy-prefix
633 # path if we use proxy-prefix
634 'netloc': parsed_url.netloc+decoded_path,
634 'netloc': parsed_url.netloc+decoded_path,
635 'hostname': parsed_url.hostname,
635 'hostname': parsed_url.hostname,
636 'prefix': decoded_path,
636 'prefix': decoded_path,
637 'repo': repo_name,
637 'repo': repo_name,
638 'repoid': str(repo_id)
638 'repoid': str(repo_id)
639 }
639 }
640 args.update(override)
640 args.update(override)
641 args['user'] = urllib.quote(safe_str(args['user']))
641 args['user'] = urllib.quote(safe_str(args['user']))
642
642
643 for k, v in args.items():
643 for k, v in args.items():
644 uri_tmpl = uri_tmpl.replace('{%s}' % k, v)
644 uri_tmpl = uri_tmpl.replace('{%s}' % k, v)
645
645
646 # remove leading @ sign if it's present. Case of empty user
646 # remove leading @ sign if it's present. Case of empty user
647 url_obj = urlobject.URLObject(uri_tmpl)
647 url_obj = urlobject.URLObject(uri_tmpl)
648 url = url_obj.with_netloc(url_obj.netloc.lstrip('@'))
648 url = url_obj.with_netloc(url_obj.netloc.lstrip('@'))
649
649
650 return safe_unicode(url)
650 return safe_unicode(url)
651
651
652
652
653 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None):
653 def get_commit_safe(repo, commit_id=None, commit_idx=None, pre_load=None):
654 """
654 """
655 Safe version of get_commit if this commit doesn't exists for a
655 Safe version of get_commit if this commit doesn't exists for a
656 repository it returns a Dummy one instead
656 repository it returns a Dummy one instead
657
657
658 :param repo: repository instance
658 :param repo: repository instance
659 :param commit_id: commit id as str
659 :param commit_id: commit id as str
660 :param pre_load: optional list of commit attributes to load
660 :param pre_load: optional list of commit attributes to load
661 """
661 """
662 # TODO(skreft): remove these circular imports
662 # TODO(skreft): remove these circular imports
663 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
663 from rhodecode.lib.vcs.backends.base import BaseRepository, EmptyCommit
664 from rhodecode.lib.vcs.exceptions import RepositoryError
664 from rhodecode.lib.vcs.exceptions import RepositoryError
665 if not isinstance(repo, BaseRepository):
665 if not isinstance(repo, BaseRepository):
666 raise Exception('You must pass an Repository '
666 raise Exception('You must pass an Repository '
667 'object as first argument got %s', type(repo))
667 'object as first argument got %s', type(repo))
668
668
669 try:
669 try:
670 commit = repo.get_commit(
670 commit = repo.get_commit(
671 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
671 commit_id=commit_id, commit_idx=commit_idx, pre_load=pre_load)
672 except (RepositoryError, LookupError):
672 except (RepositoryError, LookupError):
673 commit = EmptyCommit()
673 commit = EmptyCommit()
674 return commit
674 return commit
675
675
676
676
677 def datetime_to_time(dt):
677 def datetime_to_time(dt):
678 if dt:
678 if dt:
679 return time.mktime(dt.timetuple())
679 return time.mktime(dt.timetuple())
680
680
681
681
682 def time_to_datetime(tm):
682 def time_to_datetime(tm):
683 if tm:
683 if tm:
684 if isinstance(tm, basestring):
684 if isinstance(tm, basestring):
685 try:
685 try:
686 tm = float(tm)
686 tm = float(tm)
687 except ValueError:
687 except ValueError:
688 return
688 return
689 return datetime.datetime.fromtimestamp(tm)
689 return datetime.datetime.fromtimestamp(tm)
690
690
691
691
692 def time_to_utcdatetime(tm):
692 def time_to_utcdatetime(tm):
693 if tm:
693 if tm:
694 if isinstance(tm, basestring):
694 if isinstance(tm, basestring):
695 try:
695 try:
696 tm = float(tm)
696 tm = float(tm)
697 except ValueError:
697 except ValueError:
698 return
698 return
699 return datetime.datetime.utcfromtimestamp(tm)
699 return datetime.datetime.utcfromtimestamp(tm)
700
700
701
701
702 MENTIONS_REGEX = re.compile(
702 MENTIONS_REGEX = re.compile(
703 # ^@ or @ without any special chars in front
703 # ^@ or @ without any special chars in front
704 r'(?:^@|[^a-zA-Z0-9\-\_\.]@)'
704 r'(?:^@|[^a-zA-Z0-9\-\_\.]@)'
705 # main body starts with letter, then can be . - _
705 # main body starts with letter, then can be . - _
706 r'([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)',
706 r'([a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+)',
707 re.VERBOSE | re.MULTILINE)
707 re.VERBOSE | re.MULTILINE)
708
708
709
709
710 def extract_mentioned_users(s):
710 def extract_mentioned_users(s):
711 """
711 """
712 Returns unique usernames from given string s that have @mention
712 Returns unique usernames from given string s that have @mention
713
713
714 :param s: string to get mentions
714 :param s: string to get mentions
715 """
715 """
716 usrs = set()
716 usrs = set()
717 for username in MENTIONS_REGEX.findall(s):
717 for username in MENTIONS_REGEX.findall(s):
718 usrs.add(username)
718 usrs.add(username)
719
719
720 return sorted(list(usrs), key=lambda k: k.lower())
720 return sorted(list(usrs), key=lambda k: k.lower())
721
721
722
722
723 class AttributeDictBase(dict):
723 class AttributeDictBase(dict):
724 def __getstate__(self):
724 def __getstate__(self):
725 odict = self.__dict__ # get attribute dictionary
725 odict = self.__dict__ # get attribute dictionary
726 return odict
726 return odict
727
727
728 def __setstate__(self, dict):
728 def __setstate__(self, dict):
729 self.__dict__ = dict
729 self.__dict__ = dict
730
730
731 __setattr__ = dict.__setitem__
731 __setattr__ = dict.__setitem__
732 __delattr__ = dict.__delitem__
732 __delattr__ = dict.__delitem__
733
733
734
734
735 class StrictAttributeDict(AttributeDictBase):
735 class StrictAttributeDict(AttributeDictBase):
736 """
736 """
737 Strict Version of Attribute dict which raises an Attribute error when
737 Strict Version of Attribute dict which raises an Attribute error when
738 requested attribute is not set
738 requested attribute is not set
739 """
739 """
740 def __getattr__(self, attr):
740 def __getattr__(self, attr):
741 try:
741 try:
742 return self[attr]
742 return self[attr]
743 except KeyError:
743 except KeyError:
744 raise AttributeError('%s object has no attribute %s' % (
744 raise AttributeError('%s object has no attribute %s' % (
745 self.__class__, attr))
745 self.__class__, attr))
746
746
747
747
748 class AttributeDict(AttributeDictBase):
748 class AttributeDict(AttributeDictBase):
749 def __getattr__(self, attr):
749 def __getattr__(self, attr):
750 return self.get(attr, None)
750 return self.get(attr, None)
751
751
752
752
753
753
754 class OrderedDefaultDict(collections.OrderedDict, collections.defaultdict):
754 class OrderedDefaultDict(collections.OrderedDict, collections.defaultdict):
755 def __init__(self, default_factory=None, *args, **kwargs):
755 def __init__(self, default_factory=None, *args, **kwargs):
756 # in python3 you can omit the args to super
756 # in python3 you can omit the args to super
757 super(OrderedDefaultDict, self).__init__(*args, **kwargs)
757 super(OrderedDefaultDict, self).__init__(*args, **kwargs)
758 self.default_factory = default_factory
758 self.default_factory = default_factory
759
759
760
760
761 def fix_PATH(os_=None):
761 def fix_PATH(os_=None):
762 """
762 """
763 Get current active python path, and append it to PATH variable to fix
763 Get current active python path, and append it to PATH variable to fix
764 issues of subprocess calls and different python versions
764 issues of subprocess calls and different python versions
765 """
765 """
766 if os_ is None:
766 if os_ is None:
767 import os
767 import os
768 else:
768 else:
769 os = os_
769 os = os_
770
770
771 cur_path = os.path.split(sys.executable)[0]
771 cur_path = os.path.split(sys.executable)[0]
772 if not os.environ['PATH'].startswith(cur_path):
772 if not os.environ['PATH'].startswith(cur_path):
773 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
773 os.environ['PATH'] = '%s:%s' % (cur_path, os.environ['PATH'])
774
774
775
775
776 def obfuscate_url_pw(engine):
776 def obfuscate_url_pw(engine):
777 _url = engine or ''
777 _url = engine or ''
778 try:
778 try:
779 _url = sqlalchemy.engine.url.make_url(engine)
779 _url = sqlalchemy.engine.url.make_url(engine)
780 if _url.password:
780 if _url.password:
781 _url.password = 'XXXXX'
781 _url.password = 'XXXXX'
782 except Exception:
782 except Exception:
783 pass
783 pass
784 return unicode(_url)
784 return unicode(_url)
785
785
786
786
787 def get_server_url(environ):
787 def get_server_url(environ):
788 req = webob.Request(environ)
788 req = webob.Request(environ)
789 return req.host_url + req.script_name
789 return req.host_url + req.script_name
790
790
791
791
792 def unique_id(hexlen=32):
792 def unique_id(hexlen=32):
793 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
793 alphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjklmnpqrstuvwxyz"
794 return suuid(truncate_to=hexlen, alphabet=alphabet)
794 return suuid(truncate_to=hexlen, alphabet=alphabet)
795
795
796
796
797 def suuid(url=None, truncate_to=22, alphabet=None):
797 def suuid(url=None, truncate_to=22, alphabet=None):
798 """
798 """
799 Generate and return a short URL safe UUID.
799 Generate and return a short URL safe UUID.
800
800
801 If the url parameter is provided, set the namespace to the provided
801 If the url parameter is provided, set the namespace to the provided
802 URL and generate a UUID.
802 URL and generate a UUID.
803
803
804 :param url to get the uuid for
804 :param url to get the uuid for
805 :truncate_to: truncate the basic 22 UUID to shorter version
805 :truncate_to: truncate the basic 22 UUID to shorter version
806
806
807 The IDs won't be universally unique any longer, but the probability of
807 The IDs won't be universally unique any longer, but the probability of
808 a collision will still be very low.
808 a collision will still be very low.
809 """
809 """
810 # Define our alphabet.
810 # Define our alphabet.
811 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
811 _ALPHABET = alphabet or "23456789ABCDEFGHJKLMNPQRSTUVWXYZ"
812
812
813 # If no URL is given, generate a random UUID.
813 # If no URL is given, generate a random UUID.
814 if url is None:
814 if url is None:
815 unique_id = uuid.uuid4().int
815 unique_id = uuid.uuid4().int
816 else:
816 else:
817 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
817 unique_id = uuid.uuid3(uuid.NAMESPACE_URL, url).int
818
818
819 alphabet_length = len(_ALPHABET)
819 alphabet_length = len(_ALPHABET)
820 output = []
820 output = []
821 while unique_id > 0:
821 while unique_id > 0:
822 digit = unique_id % alphabet_length
822 digit = unique_id % alphabet_length
823 output.append(_ALPHABET[digit])
823 output.append(_ALPHABET[digit])
824 unique_id = int(unique_id / alphabet_length)
824 unique_id = int(unique_id / alphabet_length)
825 return "".join(output)[:truncate_to]
825 return "".join(output)[:truncate_to]
826
826
827
827
828 def get_current_rhodecode_user(request=None):
828 def get_current_rhodecode_user(request=None):
829 """
829 """
830 Gets rhodecode user from request
830 Gets rhodecode user from request
831 """
831 """
832 pyramid_request = request or pyramid.threadlocal.get_current_request()
832 pyramid_request = request or pyramid.threadlocal.get_current_request()
833
833
834 # web case
834 # web case
835 if pyramid_request and hasattr(pyramid_request, 'user'):
835 if pyramid_request and hasattr(pyramid_request, 'user'):
836 return pyramid_request.user
836 return pyramid_request.user
837
837
838 # api case
838 # api case
839 if pyramid_request and hasattr(pyramid_request, 'rpc_user'):
839 if pyramid_request and hasattr(pyramid_request, 'rpc_user'):
840 return pyramid_request.rpc_user
840 return pyramid_request.rpc_user
841
841
842 return None
842 return None
843
843
844
844
845 def action_logger_generic(action, namespace=''):
845 def action_logger_generic(action, namespace=''):
846 """
846 """
847 A generic logger for actions useful to the system overview, tries to find
847 A generic logger for actions useful to the system overview, tries to find
848 an acting user for the context of the call otherwise reports unknown user
848 an acting user for the context of the call otherwise reports unknown user
849
849
850 :param action: logging message eg 'comment 5 deleted'
850 :param action: logging message eg 'comment 5 deleted'
851 :param type: string
851 :param type: string
852
852
853 :param namespace: namespace of the logging message eg. 'repo.comments'
853 :param namespace: namespace of the logging message eg. 'repo.comments'
854 :param type: string
854 :param type: string
855
855
856 """
856 """
857
857
858 logger_name = 'rhodecode.actions'
858 logger_name = 'rhodecode.actions'
859
859
860 if namespace:
860 if namespace:
861 logger_name += '.' + namespace
861 logger_name += '.' + namespace
862
862
863 log = logging.getLogger(logger_name)
863 log = logging.getLogger(logger_name)
864
864
865 # get a user if we can
865 # get a user if we can
866 user = get_current_rhodecode_user()
866 user = get_current_rhodecode_user()
867
867
868 logfunc = log.info
868 logfunc = log.info
869
869
870 if not user:
870 if not user:
871 user = '<unknown user>'
871 user = '<unknown user>'
872 logfunc = log.warning
872 logfunc = log.warning
873
873
874 logfunc('Logging action by {}: {}'.format(user, action))
874 logfunc('Logging action by {}: {}'.format(user, action))
875
875
876
876
877 def escape_split(text, sep=',', maxsplit=-1):
877 def escape_split(text, sep=',', maxsplit=-1):
878 r"""
878 r"""
879 Allows for escaping of the separator: e.g. arg='foo\, bar'
879 Allows for escaping of the separator: e.g. arg='foo\, bar'
880
880
881 It should be noted that the way bash et. al. do command line parsing, those
881 It should be noted that the way bash et. al. do command line parsing, those
882 single quotes are required.
882 single quotes are required.
883 """
883 """
884 escaped_sep = r'\%s' % sep
884 escaped_sep = r'\%s' % sep
885
885
886 if escaped_sep not in text:
886 if escaped_sep not in text:
887 return text.split(sep, maxsplit)
887 return text.split(sep, maxsplit)
888
888
889 before, _mid, after = text.partition(escaped_sep)
889 before, _mid, after = text.partition(escaped_sep)
890 startlist = before.split(sep, maxsplit) # a regular split is fine here
890 startlist = before.split(sep, maxsplit) # a regular split is fine here
891 unfinished = startlist[-1]
891 unfinished = startlist[-1]
892 startlist = startlist[:-1]
892 startlist = startlist[:-1]
893
893
894 # recurse because there may be more escaped separators
894 # recurse because there may be more escaped separators
895 endlist = escape_split(after, sep, maxsplit)
895 endlist = escape_split(after, sep, maxsplit)
896
896
897 # finish building the escaped value. we use endlist[0] becaue the first
897 # finish building the escaped value. we use endlist[0] becaue the first
898 # part of the string sent in recursion is the rest of the escaped value.
898 # part of the string sent in recursion is the rest of the escaped value.
899 unfinished += sep + endlist[0]
899 unfinished += sep + endlist[0]
900
900
901 return startlist + [unfinished] + endlist[1:] # put together all the parts
901 return startlist + [unfinished] + endlist[1:] # put together all the parts
902
902
903
903
904 class OptionalAttr(object):
904 class OptionalAttr(object):
905 """
905 """
906 Special Optional Option that defines other attribute. Example::
906 Special Optional Option that defines other attribute. Example::
907
907
908 def test(apiuser, userid=Optional(OAttr('apiuser')):
908 def test(apiuser, userid=Optional(OAttr('apiuser')):
909 user = Optional.extract(userid)
909 user = Optional.extract(userid)
910 # calls
910 # calls
911
911
912 """
912 """
913
913
914 def __init__(self, attr_name):
914 def __init__(self, attr_name):
915 self.attr_name = attr_name
915 self.attr_name = attr_name
916
916
917 def __repr__(self):
917 def __repr__(self):
918 return '<OptionalAttr:%s>' % self.attr_name
918 return '<OptionalAttr:%s>' % self.attr_name
919
919
920 def __call__(self):
920 def __call__(self):
921 return self
921 return self
922
922
923
923
924 # alias
924 # alias
925 OAttr = OptionalAttr
925 OAttr = OptionalAttr
926
926
927
927
928 class Optional(object):
928 class Optional(object):
929 """
929 """
930 Defines an optional parameter::
930 Defines an optional parameter::
931
931
932 param = param.getval() if isinstance(param, Optional) else param
932 param = param.getval() if isinstance(param, Optional) else param
933 param = param() if isinstance(param, Optional) else param
933 param = param() if isinstance(param, Optional) else param
934
934
935 is equivalent of::
935 is equivalent of::
936
936
937 param = Optional.extract(param)
937 param = Optional.extract(param)
938
938
939 """
939 """
940
940
941 def __init__(self, type_):
941 def __init__(self, type_):
942 self.type_ = type_
942 self.type_ = type_
943
943
944 def __repr__(self):
944 def __repr__(self):
945 return '<Optional:%s>' % self.type_.__repr__()
945 return '<Optional:%s>' % self.type_.__repr__()
946
946
947 def __call__(self):
947 def __call__(self):
948 return self.getval()
948 return self.getval()
949
949
950 def getval(self):
950 def getval(self):
951 """
951 """
952 returns value from this Optional instance
952 returns value from this Optional instance
953 """
953 """
954 if isinstance(self.type_, OAttr):
954 if isinstance(self.type_, OAttr):
955 # use params name
955 # use params name
956 return self.type_.attr_name
956 return self.type_.attr_name
957 return self.type_
957 return self.type_
958
958
959 @classmethod
959 @classmethod
960 def extract(cls, val):
960 def extract(cls, val):
961 """
961 """
962 Extracts value from Optional() instance
962 Extracts value from Optional() instance
963
963
964 :param val:
964 :param val:
965 :return: original value if it's not Optional instance else
965 :return: original value if it's not Optional instance else
966 value of instance
966 value of instance
967 """
967 """
968 if isinstance(val, cls):
968 if isinstance(val, cls):
969 return val.getval()
969 return val.getval()
970 return val
970 return val
971
971
972
972
973 def glob2re(pat):
973 def glob2re(pat):
974 """
974 """
975 Translate a shell PATTERN to a regular expression.
975 Translate a shell PATTERN to a regular expression.
976
976
977 There is no way to quote meta-characters.
977 There is no way to quote meta-characters.
978 """
978 """
979
979
980 i, n = 0, len(pat)
980 i, n = 0, len(pat)
981 res = ''
981 res = ''
982 while i < n:
982 while i < n:
983 c = pat[i]
983 c = pat[i]
984 i = i+1
984 i = i+1
985 if c == '*':
985 if c == '*':
986 #res = res + '.*'
986 #res = res + '.*'
987 res = res + '[^/]*'
987 res = res + '[^/]*'
988 elif c == '?':
988 elif c == '?':
989 #res = res + '.'
989 #res = res + '.'
990 res = res + '[^/]'
990 res = res + '[^/]'
991 elif c == '[':
991 elif c == '[':
992 j = i
992 j = i
993 if j < n and pat[j] == '!':
993 if j < n and pat[j] == '!':
994 j = j+1
994 j = j+1
995 if j < n and pat[j] == ']':
995 if j < n and pat[j] == ']':
996 j = j+1
996 j = j+1
997 while j < n and pat[j] != ']':
997 while j < n and pat[j] != ']':
998 j = j+1
998 j = j+1
999 if j >= n:
999 if j >= n:
1000 res = res + '\\['
1000 res = res + '\\['
1001 else:
1001 else:
1002 stuff = pat[i:j].replace('\\','\\\\')
1002 stuff = pat[i:j].replace('\\','\\\\')
1003 i = j+1
1003 i = j+1
1004 if stuff[0] == '!':
1004 if stuff[0] == '!':
1005 stuff = '^' + stuff[1:]
1005 stuff = '^' + stuff[1:]
1006 elif stuff[0] == '^':
1006 elif stuff[0] == '^':
1007 stuff = '\\' + stuff
1007 stuff = '\\' + stuff
1008 res = '%s[%s]' % (res, stuff)
1008 res = '%s[%s]' % (res, stuff)
1009 else:
1009 else:
1010 res = res + re.escape(c)
1010 res = res + re.escape(c)
1011 return res + '\Z(?ms)'
1011 return res + '\Z(?ms)'
1012
1013
1014 def parse_byte_string(size_str):
1015 match = re.match(r'(\d+)(MB|KB)', size_str, re.IGNORECASE)
1016 if not match:
1017 raise ValueError('Given size:%s is invalid, please make sure '
1018 'to use format of <num>(MB|KB)' % size_str)
1019
1020 _parts = match.groups()
1021 num, type_ = _parts
1022 return long(num) * {'mb': 1024*1024, 'kb': 1024}[type_.lower()]
@@ -1,65 +1,65 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 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 This module provides some useful tools for ``vcs`` like annotate/diff html
22 This module provides some useful tools for ``vcs`` like annotate/diff html
23 output. It also includes some internal helpers.
23 output. It also includes some internal helpers.
24 """
24 """
25
25
26
26
27 def author_email(author):
27 def author_email(author):
28 """
28 """
29 returns email address of given author.
29 returns email address of given author.
30 If any of <,> sign are found, it fallbacks to regex findall()
30 If any of <,> sign are found, it fallbacks to regex findall()
31 and returns first found result or empty string
31 and returns first found result or empty string
32
32
33 Regex taken from http://www.regular-expressions.info/email.html
33 Regex taken from http://www.regular-expressions.info/email.html
34 """
34 """
35 import re
35 import re
36 if not author:
36 if not author:
37 return ''
37 return ''
38
38
39 r = author.find('>')
39 r = author.find('>')
40 l = author.find('<')
40 l = author.find('<')
41
41
42 if l == -1 or r == -1:
42 if l == -1 or r == -1:
43 # fallback to regex match of email out of a string
43 # fallback to regex match of email out of a string
44 email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
44 email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
45 r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
45 r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
46 r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
46 r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
47 r"""*[a-z0-9])?""", re.IGNORECASE)
47 r"""*[a-z0-9])?""", re.IGNORECASE)
48 m = re.findall(email_re, author)
48 m = re.findall(email_re, author)
49 return m[0] if m else ''
49 return m[0] if m else ''
50
50
51 return author[l + 1:r].strip()
51 return author[l + 1:r].strip()
52
52
53
53
54 def author_name(author):
54 def author_name(author):
55 """
55 """
56 get name of author, or else username.
56 get name of author, or else username.
57 It'll try to find an email in the author string and just cut it off
57 It'll try to find an email in the author string and just cut it off
58 to get the username
58 to get the username
59 """
59 """
60
60
61 if not author or not '@' in author:
61 if not author or '@' not in author:
62 return author
62 return author
63 else:
63 else:
64 return author.replace(author_email(author), '').replace('<', '')\
64 return author.replace(author_email(author), '').replace('<', '')\
65 .replace('>', '').strip()
65 .replace('>', '').strip()
@@ -1,43 +1,46 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2018 RhodeCode GmbH
3 # Copyright (C) 2016-2018 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 import colander
22 import colander
23
23
24
24
25 class SearchParamsSchema(colander.MappingSchema):
25 class SearchParamsSchema(colander.MappingSchema):
26 search_query = colander.SchemaNode(
26 search_query = colander.SchemaNode(
27 colander.String(),
27 colander.String(),
28 missing='')
28 missing='')
29 search_type = colander.SchemaNode(
29 search_type = colander.SchemaNode(
30 colander.String(),
30 colander.String(),
31 missing='content',
31 missing='content',
32 validator=colander.OneOf(['content', 'path', 'commit', 'repository']))
32 validator=colander.OneOf(['content', 'path', 'commit', 'repository']))
33 search_sort = colander.SchemaNode(
33 search_sort = colander.SchemaNode(
34 colander.String(),
34 colander.String(),
35 missing='newfirst',
35 missing='newfirst',
36 validator=colander.OneOf(['oldfirst', 'newfirst']))
36 validator=colander.OneOf(['oldfirst', 'newfirst']))
37 search_max_lines = colander.SchemaNode(
38 colander.Integer(),
39 missing=10)
37 page_limit = colander.SchemaNode(
40 page_limit = colander.SchemaNode(
38 colander.Integer(),
41 colander.Integer(),
39 missing=10,
42 missing=10,
40 validator=colander.Range(1, 500))
43 validator=colander.Range(1, 500))
41 requested_page = colander.SchemaNode(
44 requested_page = colander.SchemaNode(
42 colander.Integer(),
45 colander.Integer(),
43 missing=1)
46 missing=1)
@@ -1,1257 +1,1259 b''
1 // Default styles
1 // Default styles
2
2
3 .diff-collapse {
3 .diff-collapse {
4 margin: @padding 0;
4 margin: @padding 0;
5 text-align: right;
5 text-align: right;
6 }
6 }
7
7
8 .diff-container {
8 .diff-container {
9 margin-bottom: @space;
9 margin-bottom: @space;
10
10
11 .diffblock {
11 .diffblock {
12 margin-bottom: @space;
12 margin-bottom: @space;
13 }
13 }
14
14
15 &.hidden {
15 &.hidden {
16 display: none;
16 display: none;
17 overflow: hidden;
17 overflow: hidden;
18 }
18 }
19 }
19 }
20
20
21
21
22 div.diffblock .sidebyside {
22 div.diffblock .sidebyside {
23 background: #ffffff;
23 background: #ffffff;
24 }
24 }
25
25
26 div.diffblock {
26 div.diffblock {
27 overflow-x: auto;
27 overflow-x: auto;
28 overflow-y: hidden;
28 overflow-y: hidden;
29 clear: both;
29 clear: both;
30 padding: 0px;
30 padding: 0px;
31 background: @grey6;
31 background: @grey6;
32 border: @border-thickness solid @grey5;
32 border: @border-thickness solid @grey5;
33 -webkit-border-radius: @border-radius @border-radius 0px 0px;
33 -webkit-border-radius: @border-radius @border-radius 0px 0px;
34 border-radius: @border-radius @border-radius 0px 0px;
34 border-radius: @border-radius @border-radius 0px 0px;
35
35
36
36
37 .comments-number {
37 .comments-number {
38 float: right;
38 float: right;
39 }
39 }
40
40
41 // BEGIN CODE-HEADER STYLES
41 // BEGIN CODE-HEADER STYLES
42
42
43 .code-header {
43 .code-header {
44 background: @grey6;
44 background: @grey6;
45 padding: 10px 0 10px 0;
45 padding: 10px 0 10px 0;
46 height: auto;
46 height: auto;
47 width: 100%;
47 width: 100%;
48
48
49 .hash {
49 .hash {
50 float: left;
50 float: left;
51 padding: 2px 0 0 2px;
51 padding: 2px 0 0 2px;
52 }
52 }
53
53
54 .date {
54 .date {
55 float: left;
55 float: left;
56 text-transform: uppercase;
56 text-transform: uppercase;
57 padding: 4px 0px 0px 2px;
57 padding: 4px 0px 0px 2px;
58 }
58 }
59
59
60 div {
60 div {
61 margin-left: 4px;
61 margin-left: 4px;
62 }
62 }
63
63
64 div.compare_header {
64 div.compare_header {
65 min-height: 40px;
65 min-height: 40px;
66 margin: 0;
66 margin: 0;
67 padding: 0 @padding;
67 padding: 0 @padding;
68
68
69 .drop-menu {
69 .drop-menu {
70 float:left;
70 float:left;
71 display: block;
71 display: block;
72 margin:0 0 @padding 0;
72 margin:0 0 @padding 0;
73 }
73 }
74
74
75 .compare-label {
75 .compare-label {
76 float: left;
76 float: left;
77 clear: both;
77 clear: both;
78 display: inline-block;
78 display: inline-block;
79 min-width: 5em;
79 min-width: 5em;
80 margin: 0;
80 margin: 0;
81 padding: @button-padding @button-padding @button-padding 0;
81 padding: @button-padding @button-padding @button-padding 0;
82 font-weight: @text-semibold-weight;
82 font-weight: @text-semibold-weight;
83 font-family: @text-semibold;
83 font-family: @text-semibold;
84 }
84 }
85
85
86 .compare-buttons {
86 .compare-buttons {
87 float: left;
87 float: left;
88 margin: 0;
88 margin: 0;
89 padding: 0 0 @padding;
89 padding: 0 0 @padding;
90
90
91 .btn {
91 .btn {
92 margin: 0 @padding 0 0;
92 margin: 0 @padding 0 0;
93 }
93 }
94 }
94 }
95 }
95 }
96
96
97 }
97 }
98
98
99 .parents {
99 .parents {
100 float: left;
100 float: left;
101 width: 100px;
101 width: 100px;
102 font-weight: 400;
102 font-weight: 400;
103 vertical-align: middle;
103 vertical-align: middle;
104 padding: 0px 2px 0px 2px;
104 padding: 0px 2px 0px 2px;
105 background-color: @grey6;
105 background-color: @grey6;
106
106
107 #parent_link {
107 #parent_link {
108 margin: 00px 2px;
108 margin: 00px 2px;
109
109
110 &.double {
110 &.double {
111 margin: 0px 2px;
111 margin: 0px 2px;
112 }
112 }
113
113
114 &.disabled{
114 &.disabled{
115 margin-right: @padding;
115 margin-right: @padding;
116 }
116 }
117 }
117 }
118 }
118 }
119
119
120 .children {
120 .children {
121 float: right;
121 float: right;
122 width: 100px;
122 width: 100px;
123 font-weight: 400;
123 font-weight: 400;
124 vertical-align: middle;
124 vertical-align: middle;
125 text-align: right;
125 text-align: right;
126 padding: 0px 2px 0px 2px;
126 padding: 0px 2px 0px 2px;
127 background-color: @grey6;
127 background-color: @grey6;
128
128
129 #child_link {
129 #child_link {
130 margin: 0px 2px;
130 margin: 0px 2px;
131
131
132 &.double {
132 &.double {
133 margin: 0px 2px;
133 margin: 0px 2px;
134 }
134 }
135
135
136 &.disabled{
136 &.disabled{
137 margin-right: @padding;
137 margin-right: @padding;
138 }
138 }
139 }
139 }
140 }
140 }
141
141
142 .changeset_header {
142 .changeset_header {
143 height: 16px;
143 height: 16px;
144
144
145 & > div{
145 & > div{
146 margin-right: @padding;
146 margin-right: @padding;
147 }
147 }
148 }
148 }
149
149
150 .changeset_file {
150 .changeset_file {
151 text-align: left;
151 text-align: left;
152 float: left;
152 float: left;
153 padding: 0;
153 padding: 0;
154
154
155 a{
155 a{
156 display: inline-block;
156 display: inline-block;
157 margin-right: 0.5em;
157 margin-right: 0.5em;
158 }
158 }
159
159
160 #selected_mode{
160 #selected_mode{
161 margin-left: 0;
161 margin-left: 0;
162 }
162 }
163 }
163 }
164
164
165 .diff-menu-wrapper {
165 .diff-menu-wrapper {
166 float: left;
166 float: left;
167 }
167 }
168
168
169 .diff-menu {
169 .diff-menu {
170 position: absolute;
170 position: absolute;
171 background: none repeat scroll 0 0 #FFFFFF;
171 background: none repeat scroll 0 0 #FFFFFF;
172 border-color: #003367 @grey3 @grey3;
172 border-color: #003367 @grey3 @grey3;
173 border-right: 1px solid @grey3;
173 border-right: 1px solid @grey3;
174 border-style: solid solid solid;
174 border-style: solid solid solid;
175 border-width: @border-thickness;
175 border-width: @border-thickness;
176 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
176 box-shadow: 2px 8px 4px rgba(0, 0, 0, 0.2);
177 margin-top: 5px;
177 margin-top: 5px;
178 margin-left: 1px;
178 margin-left: 1px;
179 }
179 }
180
180
181 .diff-actions, .editor-actions {
181 .diff-actions, .editor-actions {
182 float: left;
182 float: left;
183
183
184 input{
184 input{
185 margin: 0 0.5em 0 0;
185 margin: 0 0.5em 0 0;
186 }
186 }
187 }
187 }
188
188
189 // END CODE-HEADER STYLES
189 // END CODE-HEADER STYLES
190
190
191 // BEGIN CODE-BODY STYLES
191 // BEGIN CODE-BODY STYLES
192
192
193 .code-body {
193 .code-body {
194 padding: 0;
194 padding: 0;
195 background-color: #ffffff;
195 background-color: #ffffff;
196 position: relative;
196 position: relative;
197 max-width: none;
197 max-width: none;
198 box-sizing: border-box;
198 box-sizing: border-box;
199 // TODO: johbo: Parent has overflow: auto, this forces the child here
199 // TODO: johbo: Parent has overflow: auto, this forces the child here
200 // to have the intended size and to scroll. Should be simplified.
200 // to have the intended size and to scroll. Should be simplified.
201 width: 100%;
201 width: 100%;
202 overflow-x: auto;
202 overflow-x: auto;
203 }
203 }
204
204
205 pre.raw {
205 pre.raw {
206 background: white;
206 background: white;
207 color: @grey1;
207 color: @grey1;
208 }
208 }
209 // END CODE-BODY STYLES
209 // END CODE-BODY STYLES
210
210
211 }
211 }
212
212
213
213
214 table.code-difftable {
214 table.code-difftable {
215 border-collapse: collapse;
215 border-collapse: collapse;
216 width: 99%;
216 width: 99%;
217 border-radius: 0px !important;
217 border-radius: 0px !important;
218
218
219 td {
219 td {
220 padding: 0 !important;
220 padding: 0 !important;
221 background: none !important;
221 background: none !important;
222 border: 0 !important;
222 border: 0 !important;
223 }
223 }
224
224
225 .context {
225 .context {
226 background: none repeat scroll 0 0 #DDE7EF;
226 background: none repeat scroll 0 0 #DDE7EF;
227 }
227 }
228
228
229 .add {
229 .add {
230 background: none repeat scroll 0 0 #DDFFDD;
230 background: none repeat scroll 0 0 #DDFFDD;
231
231
232 ins {
232 ins {
233 background: none repeat scroll 0 0 #AAFFAA;
233 background: none repeat scroll 0 0 #AAFFAA;
234 text-decoration: none;
234 text-decoration: none;
235 }
235 }
236 }
236 }
237
237
238 .del {
238 .del {
239 background: none repeat scroll 0 0 #FFDDDD;
239 background: none repeat scroll 0 0 #FFDDDD;
240
240
241 del {
241 del {
242 background: none repeat scroll 0 0 #FFAAAA;
242 background: none repeat scroll 0 0 #FFAAAA;
243 text-decoration: none;
243 text-decoration: none;
244 }
244 }
245 }
245 }
246
246
247 /** LINE NUMBERS **/
247 /** LINE NUMBERS **/
248 .lineno {
248 .lineno {
249 padding-left: 2px !important;
249 padding-left: 2px !important;
250 padding-right: 2px;
250 padding-right: 2px;
251 text-align: right;
251 text-align: right;
252 width: 32px;
252 width: 32px;
253 -moz-user-select: none;
253 -moz-user-select: none;
254 -webkit-user-select: none;
254 -webkit-user-select: none;
255 border-right: @border-thickness solid @grey5 !important;
255 border-right: @border-thickness solid @grey5 !important;
256 border-left: 0px solid #CCC !important;
256 border-left: 0px solid #CCC !important;
257 border-top: 0px solid #CCC !important;
257 border-top: 0px solid #CCC !important;
258 border-bottom: none !important;
258 border-bottom: none !important;
259
259
260 a {
260 a {
261 &:extend(pre);
261 &:extend(pre);
262 text-align: right;
262 text-align: right;
263 padding-right: 2px;
263 padding-right: 2px;
264 cursor: pointer;
264 cursor: pointer;
265 display: block;
265 display: block;
266 width: 32px;
266 width: 32px;
267 }
267 }
268 }
268 }
269
269
270 .context {
270 .context {
271 cursor: auto;
271 cursor: auto;
272 &:extend(pre);
272 &:extend(pre);
273 }
273 }
274
274
275 .lineno-inline {
275 .lineno-inline {
276 background: none repeat scroll 0 0 #FFF !important;
276 background: none repeat scroll 0 0 #FFF !important;
277 padding-left: 2px;
277 padding-left: 2px;
278 padding-right: 2px;
278 padding-right: 2px;
279 text-align: right;
279 text-align: right;
280 width: 30px;
280 width: 30px;
281 -moz-user-select: none;
281 -moz-user-select: none;
282 -webkit-user-select: none;
282 -webkit-user-select: none;
283 }
283 }
284
284
285 /** CODE **/
285 /** CODE **/
286 .code {
286 .code {
287 display: block;
287 display: block;
288 width: 100%;
288 width: 100%;
289
289
290 td {
290 td {
291 margin: 0;
291 margin: 0;
292 padding: 0;
292 padding: 0;
293 }
293 }
294
294
295 pre {
295 pre {
296 margin: 0;
296 margin: 0;
297 padding: 0;
297 padding: 0;
298 margin-left: .5em;
298 margin-left: .5em;
299 }
299 }
300 }
300 }
301 }
301 }
302
302
303
303
304 // Comments
304 // Comments
305
305
306 div.comment:target {
306 div.comment:target {
307 border-left: 6px solid @comment-highlight-color !important;
307 border-left: 6px solid @comment-highlight-color !important;
308 padding-left: 3px;
308 padding-left: 3px;
309 margin-left: -9px;
309 margin-left: -9px;
310 }
310 }
311
311
312 //TODO: anderson: can't get an absolute number out of anything, so had to put the
312 //TODO: anderson: can't get an absolute number out of anything, so had to put the
313 //current values that might change. But to make it clear I put as a calculation
313 //current values that might change. But to make it clear I put as a calculation
314 @comment-max-width: 1065px;
314 @comment-max-width: 1065px;
315 @pr-extra-margin: 34px;
315 @pr-extra-margin: 34px;
316 @pr-border-spacing: 4px;
316 @pr-border-spacing: 4px;
317 @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing;
317 @pr-comment-width: @comment-max-width - @pr-extra-margin - @pr-border-spacing;
318
318
319 // Pull Request
319 // Pull Request
320 .cs_files .code-difftable {
320 .cs_files .code-difftable {
321 border: @border-thickness solid @grey5; //borders only on PRs
321 border: @border-thickness solid @grey5; //borders only on PRs
322
322
323 .comment-inline-form,
323 .comment-inline-form,
324 div.comment {
324 div.comment {
325 width: @pr-comment-width;
325 width: @pr-comment-width;
326 }
326 }
327 }
327 }
328
328
329 // Changeset
329 // Changeset
330 .code-difftable {
330 .code-difftable {
331 .comment-inline-form,
331 .comment-inline-form,
332 div.comment {
332 div.comment {
333 width: @comment-max-width;
333 width: @comment-max-width;
334 }
334 }
335 }
335 }
336
336
337 //Style page
337 //Style page
338 @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding;
338 @style-extra-margin: @sidebar-width + (@sidebarpadding * 3) + @padding;
339 #style-page .code-difftable{
339 #style-page .code-difftable{
340 .comment-inline-form,
340 .comment-inline-form,
341 div.comment {
341 div.comment {
342 width: @comment-max-width - @style-extra-margin;
342 width: @comment-max-width - @style-extra-margin;
343 }
343 }
344 }
344 }
345
345
346 #context-bar > h2 {
346 #context-bar > h2 {
347 font-size: 20px;
347 font-size: 20px;
348 }
348 }
349
349
350 #context-bar > h2> a {
350 #context-bar > h2> a {
351 font-size: 20px;
351 font-size: 20px;
352 }
352 }
353 // end of defaults
353 // end of defaults
354
354
355 .file_diff_buttons {
355 .file_diff_buttons {
356 padding: 0 0 @padding;
356 padding: 0 0 @padding;
357
357
358 .drop-menu {
358 .drop-menu {
359 float: left;
359 float: left;
360 margin: 0 @padding 0 0;
360 margin: 0 @padding 0 0;
361 }
361 }
362 .btn {
362 .btn {
363 margin: 0 @padding 0 0;
363 margin: 0 @padding 0 0;
364 }
364 }
365 }
365 }
366
366
367 .code-body.textarea.editor {
367 .code-body.textarea.editor {
368 max-width: none;
368 max-width: none;
369 padding: 15px;
369 padding: 15px;
370 }
370 }
371
371
372 td.injected_diff{
372 td.injected_diff{
373 max-width: 1178px;
373 max-width: 1178px;
374 overflow-x: auto;
374 overflow-x: auto;
375 overflow-y: hidden;
375 overflow-y: hidden;
376
376
377 div.diff-container,
377 div.diff-container,
378 div.diffblock{
378 div.diffblock{
379 max-width: 100%;
379 max-width: 100%;
380 }
380 }
381
381
382 div.code-body {
382 div.code-body {
383 max-width: 1124px;
383 max-width: 1124px;
384 overflow-x: auto;
384 overflow-x: auto;
385 overflow-y: hidden;
385 overflow-y: hidden;
386 padding: 0;
386 padding: 0;
387 }
387 }
388 div.diffblock {
388 div.diffblock {
389 border: none;
389 border: none;
390 }
390 }
391
391
392 &.inline-form {
392 &.inline-form {
393 width: 99%
393 width: 99%
394 }
394 }
395 }
395 }
396
396
397
397
398 table.code-difftable {
398 table.code-difftable {
399 width: 100%;
399 width: 100%;
400 }
400 }
401
401
402 /** PYGMENTS COLORING **/
402 /** PYGMENTS COLORING **/
403 div.codeblock {
403 div.codeblock {
404
404
405 // TODO: johbo: Added interim to get rid of the margin around
405 // TODO: johbo: Added interim to get rid of the margin around
406 // Select2 widgets. This needs further cleanup.
406 // Select2 widgets. This needs further cleanup.
407 margin-top: @padding;
407 margin-top: @padding;
408
408
409 overflow: auto;
409 overflow: auto;
410 padding: 0px;
410 padding: 0px;
411 border: @border-thickness solid @grey5;
411 border: @border-thickness solid @grey5;
412 background: @grey6;
412 background: @grey6;
413 .border-radius(@border-radius);
413 .border-radius(@border-radius);
414
414
415 #remove_gist {
415 #remove_gist {
416 float: right;
416 float: right;
417 }
417 }
418
418
419 .gist_url {
419 .gist_url {
420 padding: 0px 0px 10px 0px;
420 padding: 0px 0px 10px 0px;
421 }
421 }
422
422
423 .author {
423 .author {
424 clear: both;
424 clear: both;
425 vertical-align: middle;
425 vertical-align: middle;
426 font-weight: @text-bold-weight;
426 font-weight: @text-bold-weight;
427 font-family: @text-bold;
427 font-family: @text-bold;
428 }
428 }
429
429
430 .btn-mini {
430 .btn-mini {
431 float: left;
431 float: left;
432 margin: 0 5px 0 0;
432 margin: 0 5px 0 0;
433 }
433 }
434
434
435 .code-header {
435 .code-header {
436 padding: @padding;
436 padding: @padding;
437 border-bottom: @border-thickness solid @grey5;
437 border-bottom: @border-thickness solid @grey5;
438
438
439 .rc-user {
439 .rc-user {
440 min-width: 0;
440 min-width: 0;
441 margin-right: .5em;
441 margin-right: .5em;
442 }
442 }
443
443
444 .stats {
444 .stats {
445 clear: both;
445 clear: both;
446 margin: 0 0 @padding 0;
446 margin: 0 0 @padding 0;
447 padding: 0;
447 padding: 0;
448 .left {
448 .left {
449 float: left;
449 float: left;
450 clear: left;
450 clear: left;
451 max-width: 75%;
451 max-width: 75%;
452 margin: 0 0 @padding 0;
452 margin: 0 0 @padding 0;
453
453
454 &.item {
454 &.item {
455 margin-right: @padding;
455 margin-right: @padding;
456 &.last { border-right: none; }
456 &.last { border-right: none; }
457 }
457 }
458 }
458 }
459 .buttons { float: right; }
459 .buttons { float: right; }
460 .author {
460 .author {
461 height: 25px; margin-left: 15px; font-weight: bold;
461 height: 25px; margin-left: 15px; font-weight: bold;
462 }
462 }
463 }
463 }
464
464
465 .commit {
465 .commit {
466 margin: 5px 0 0 26px;
466 margin: 5px 0 0 26px;
467 font-weight: normal;
467 font-weight: normal;
468 white-space: pre-wrap;
468 white-space: pre-wrap;
469 }
469 }
470 }
470 }
471
471
472 .message {
472 .message {
473 position: relative;
473 position: relative;
474 margin: @padding;
474 margin: @padding;
475
475
476 .codeblock-label {
476 .codeblock-label {
477 margin: 0 0 1em 0;
477 margin: 0 0 1em 0;
478 }
478 }
479 }
479 }
480
480
481 .code-body {
481 .code-body {
482 padding: @padding;
482 padding: @padding;
483 background-color: #ffffff;
483 background-color: #ffffff;
484 min-width: 100%;
484 min-width: 100%;
485 box-sizing: border-box;
485 box-sizing: border-box;
486 // TODO: johbo: Parent has overflow: auto, this forces the child here
486 // TODO: johbo: Parent has overflow: auto, this forces the child here
487 // to have the intended size and to scroll. Should be simplified.
487 // to have the intended size and to scroll. Should be simplified.
488 width: 100%;
488 width: 100%;
489 overflow-x: auto;
489 overflow-x: auto;
490
490
491 img.rendered-binary {
491 img.rendered-binary {
492 height: auto;
492 height: auto;
493 width: 100%;
493 width: 100%;
494 }
494 }
495 }
495 }
496 }
496 }
497
497
498 .code-highlighttable,
498 .code-highlighttable,
499 div.codeblock {
499 div.codeblock {
500
500
501 &.readme {
501 &.readme {
502 background-color: white;
502 background-color: white;
503 }
503 }
504
504
505 .markdown-block table {
505 .markdown-block table {
506 border-collapse: collapse;
506 border-collapse: collapse;
507
507
508 th,
508 th,
509 td {
509 td {
510 padding: .5em;
510 padding: .5em;
511 border: @border-thickness solid @border-default-color;
511 border: @border-thickness solid @border-default-color;
512 }
512 }
513 }
513 }
514
514
515 table {
515 table {
516 border: 0px;
516 border: 0px;
517 margin: 0;
517 margin: 0;
518 letter-spacing: normal;
518 letter-spacing: normal;
519
519
520
520
521 td {
521 td {
522 border: 0px;
522 border: 0px;
523 vertical-align: top;
523 vertical-align: top;
524 }
524 }
525 }
525 }
526 }
526 }
527
527
528 div.codeblock .code-header .search-path { padding: 0 0 0 10px; }
528 div.codeblock .code-header .search-path { padding: 0 0 0 10px; }
529 div.search-code-body {
529 div.search-code-body {
530 background-color: #ffffff; padding: 5px 0 5px 10px;
530 background-color: #ffffff; padding: 5px 0 5px 10px;
531 pre {
531 pre {
532 .match { background-color: #faffa6;}
532 .match { background-color: #faffa6;}
533 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
533 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
534 }
534 }
535 .code-highlighttable {
535 .code-highlighttable {
536 border-collapse: collapse;
536 border-collapse: collapse;
537
537
538 tr:hover {
538 tr:hover {
539 background: #fafafa;
539 background: #fafafa;
540 }
540 }
541 td.code {
541 td.code {
542 padding-left: 10px;
542 padding-left: 10px;
543 }
543 }
544 td.line {
544 td.line {
545 border-right: 1px solid #ccc !important;
545 border-right: 1px solid #ccc !important;
546 padding-right: 10px;
546 padding-right: 10px;
547 text-align: right;
547 text-align: right;
548 font-family: @text-monospace;
548 font-family: @text-monospace;
549 span {
549 span {
550 white-space: pre-wrap;
550 white-space: pre-wrap;
551 color: #666666;
551 color: #666666;
552 }
552 }
553 }
553 }
554 }
554 }
555 }
555 }
556
556
557 div.annotatediv { margin-left: 2px; margin-right: 4px; }
557 div.annotatediv { margin-left: 2px; margin-right: 4px; }
558 .code-highlight {
558 .code-highlight {
559 margin: 0; padding: 0; border-left: @border-thickness solid @grey5;
559 margin: 0; padding: 0; border-left: @border-thickness solid @grey5;
560 pre, .linenodiv pre { padding: 0 5px; margin: 0; }
560 pre, .linenodiv pre { padding: 0 5px; margin: 0; }
561 pre div:target {background-color: @comment-highlight-color !important;}
561 pre div:target {background-color: @comment-highlight-color !important;}
562 }
562 }
563
563
564 .linenos a { text-decoration: none; }
564 .linenos a { text-decoration: none; }
565
565
566 .CodeMirror-selected { background: @rchighlightblue; }
566 .CodeMirror-selected { background: @rchighlightblue; }
567 .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; }
567 .CodeMirror-focused .CodeMirror-selected { background: @rchighlightblue; }
568 .CodeMirror ::selection { background: @rchighlightblue; }
568 .CodeMirror ::selection { background: @rchighlightblue; }
569 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
569 .CodeMirror ::-moz-selection { background: @rchighlightblue; }
570
570
571 .code { display: block; border:0px !important; }
571 .code { display: block; border:0px !important; }
572 .code-highlight, /* TODO: dan: merge codehilite into code-highlight */
572 .code-highlight, /* TODO: dan: merge codehilite into code-highlight */
573 /* This can be generated with `pygmentize -S default -f html` */
573 /* This can be generated with `pygmentize -S default -f html` */
574 .codehilite {
574 .codehilite {
575 .c-ElasticMatch { background-color: #faffa6; padding: 0.2em;}
575 .hll { background-color: #ffffcc }
576 .hll { background-color: #ffffcc }
576 .c { color: #408080; font-style: italic } /* Comment */
577 .c { color: #408080; font-style: italic } /* Comment */
577 .err, .codehilite .err { border: none } /* Error */
578 .err, .codehilite .err { border: none } /* Error */
578 .k { color: #008000; font-weight: bold } /* Keyword */
579 .k { color: #008000; font-weight: bold } /* Keyword */
579 .o { color: #666666 } /* Operator */
580 .o { color: #666666 } /* Operator */
580 .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
581 .ch { color: #408080; font-style: italic } /* Comment.Hashbang */
581 .cm { color: #408080; font-style: italic } /* Comment.Multiline */
582 .cm { color: #408080; font-style: italic } /* Comment.Multiline */
582 .cp { color: #BC7A00 } /* Comment.Preproc */
583 .cp { color: #BC7A00 } /* Comment.Preproc */
583 .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
584 .cpf { color: #408080; font-style: italic } /* Comment.PreprocFile */
584 .c1 { color: #408080; font-style: italic } /* Comment.Single */
585 .c1 { color: #408080; font-style: italic } /* Comment.Single */
585 .cs { color: #408080; font-style: italic } /* Comment.Special */
586 .cs { color: #408080; font-style: italic } /* Comment.Special */
586 .gd { color: #A00000 } /* Generic.Deleted */
587 .gd { color: #A00000 } /* Generic.Deleted */
587 .ge { font-style: italic } /* Generic.Emph */
588 .ge { font-style: italic } /* Generic.Emph */
588 .gr { color: #FF0000 } /* Generic.Error */
589 .gr { color: #FF0000 } /* Generic.Error */
589 .gh { color: #000080; font-weight: bold } /* Generic.Heading */
590 .gh { color: #000080; font-weight: bold } /* Generic.Heading */
590 .gi { color: #00A000 } /* Generic.Inserted */
591 .gi { color: #00A000 } /* Generic.Inserted */
591 .go { color: #888888 } /* Generic.Output */
592 .go { color: #888888 } /* Generic.Output */
592 .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
593 .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
593 .gs { font-weight: bold } /* Generic.Strong */
594 .gs { font-weight: bold } /* Generic.Strong */
594 .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
595 .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
595 .gt { color: #0044DD } /* Generic.Traceback */
596 .gt { color: #0044DD } /* Generic.Traceback */
596 .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
597 .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
597 .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
598 .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
598 .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
599 .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
599 .kp { color: #008000 } /* Keyword.Pseudo */
600 .kp { color: #008000 } /* Keyword.Pseudo */
600 .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
601 .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
601 .kt { color: #B00040 } /* Keyword.Type */
602 .kt { color: #B00040 } /* Keyword.Type */
602 .m { color: #666666 } /* Literal.Number */
603 .m { color: #666666 } /* Literal.Number */
603 .s { color: #BA2121 } /* Literal.String */
604 .s { color: #BA2121 } /* Literal.String */
604 .na { color: #7D9029 } /* Name.Attribute */
605 .na { color: #7D9029 } /* Name.Attribute */
605 .nb { color: #008000 } /* Name.Builtin */
606 .nb { color: #008000 } /* Name.Builtin */
606 .nc { color: #0000FF; font-weight: bold } /* Name.Class */
607 .nc { color: #0000FF; font-weight: bold } /* Name.Class */
607 .no { color: #880000 } /* Name.Constant */
608 .no { color: #880000 } /* Name.Constant */
608 .nd { color: #AA22FF } /* Name.Decorator */
609 .nd { color: #AA22FF } /* Name.Decorator */
609 .ni { color: #999999; font-weight: bold } /* Name.Entity */
610 .ni { color: #999999; font-weight: bold } /* Name.Entity */
610 .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
611 .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
611 .nf { color: #0000FF } /* Name.Function */
612 .nf { color: #0000FF } /* Name.Function */
612 .nl { color: #A0A000 } /* Name.Label */
613 .nl { color: #A0A000 } /* Name.Label */
613 .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
614 .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
614 .nt { color: #008000; font-weight: bold } /* Name.Tag */
615 .nt { color: #008000; font-weight: bold } /* Name.Tag */
615 .nv { color: #19177C } /* Name.Variable */
616 .nv { color: #19177C } /* Name.Variable */
616 .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
617 .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
617 .w { color: #bbbbbb } /* Text.Whitespace */
618 .w { color: #bbbbbb } /* Text.Whitespace */
618 .mb { color: #666666 } /* Literal.Number.Bin */
619 .mb { color: #666666 } /* Literal.Number.Bin */
619 .mf { color: #666666 } /* Literal.Number.Float */
620 .mf { color: #666666 } /* Literal.Number.Float */
620 .mh { color: #666666 } /* Literal.Number.Hex */
621 .mh { color: #666666 } /* Literal.Number.Hex */
621 .mi { color: #666666 } /* Literal.Number.Integer */
622 .mi { color: #666666 } /* Literal.Number.Integer */
622 .mo { color: #666666 } /* Literal.Number.Oct */
623 .mo { color: #666666 } /* Literal.Number.Oct */
623 .sa { color: #BA2121 } /* Literal.String.Affix */
624 .sa { color: #BA2121 } /* Literal.String.Affix */
624 .sb { color: #BA2121 } /* Literal.String.Backtick */
625 .sb { color: #BA2121 } /* Literal.String.Backtick */
625 .sc { color: #BA2121 } /* Literal.String.Char */
626 .sc { color: #BA2121 } /* Literal.String.Char */
626 .dl { color: #BA2121 } /* Literal.String.Delimiter */
627 .dl { color: #BA2121 } /* Literal.String.Delimiter */
627 .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
628 .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
628 .s2 { color: #BA2121 } /* Literal.String.Double */
629 .s2 { color: #BA2121 } /* Literal.String.Double */
629 .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
630 .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
630 .sh { color: #BA2121 } /* Literal.String.Heredoc */
631 .sh { color: #BA2121 } /* Literal.String.Heredoc */
631 .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
632 .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
632 .sx { color: #008000 } /* Literal.String.Other */
633 .sx { color: #008000 } /* Literal.String.Other */
633 .sr { color: #BB6688 } /* Literal.String.Regex */
634 .sr { color: #BB6688 } /* Literal.String.Regex */
634 .s1 { color: #BA2121 } /* Literal.String.Single */
635 .s1 { color: #BA2121 } /* Literal.String.Single */
635 .ss { color: #19177C } /* Literal.String.Symbol */
636 .ss { color: #19177C } /* Literal.String.Symbol */
636 .bp { color: #008000 } /* Name.Builtin.Pseudo */
637 .bp { color: #008000 } /* Name.Builtin.Pseudo */
637 .fm { color: #0000FF } /* Name.Function.Magic */
638 .fm { color: #0000FF } /* Name.Function.Magic */
638 .vc { color: #19177C } /* Name.Variable.Class */
639 .vc { color: #19177C } /* Name.Variable.Class */
639 .vg { color: #19177C } /* Name.Variable.Global */
640 .vg { color: #19177C } /* Name.Variable.Global */
640 .vi { color: #19177C } /* Name.Variable.Instance */
641 .vi { color: #19177C } /* Name.Variable.Instance */
641 .vm { color: #19177C } /* Name.Variable.Magic */
642 .vm { color: #19177C } /* Name.Variable.Magic */
642 .il { color: #666666 } /* Literal.Number.Integer.Long */
643 .il { color: #666666 } /* Literal.Number.Integer.Long */
644
643 }
645 }
644
646
645 /* customized pre blocks for markdown/rst */
647 /* customized pre blocks for markdown/rst */
646 pre.literal-block, .codehilite pre{
648 pre.literal-block, .codehilite pre{
647 padding: @padding;
649 padding: @padding;
648 border: 1px solid @grey6;
650 border: 1px solid @grey6;
649 .border-radius(@border-radius);
651 .border-radius(@border-radius);
650 background-color: @grey7;
652 background-color: @grey7;
651 }
653 }
652
654
653
655
654 /* START NEW CODE BLOCK CSS */
656 /* START NEW CODE BLOCK CSS */
655
657
656 @cb-line-height: 18px;
658 @cb-line-height: 18px;
657 @cb-line-code-padding: 10px;
659 @cb-line-code-padding: 10px;
658 @cb-text-padding: 5px;
660 @cb-text-padding: 5px;
659
661
660 @pill-padding: 2px 7px;
662 @pill-padding: 2px 7px;
661 @pill-padding-small: 2px 2px 1px 2px;
663 @pill-padding-small: 2px 2px 1px 2px;
662
664
663 input.filediff-collapse-state {
665 input.filediff-collapse-state {
664 display: none;
666 display: none;
665
667
666 &:checked + .filediff { /* file diff is collapsed */
668 &:checked + .filediff { /* file diff is collapsed */
667 .cb {
669 .cb {
668 display: none
670 display: none
669 }
671 }
670 .filediff-collapse-indicator {
672 .filediff-collapse-indicator {
671 width: 0;
673 width: 0;
672 height: 0;
674 height: 0;
673 border-style: solid;
675 border-style: solid;
674 border-width: 4.5px 0 4.5px 9.3px;
676 border-width: 4.5px 0 4.5px 9.3px;
675 border-color: transparent transparent transparent #aaa;
677 border-color: transparent transparent transparent #aaa;
676 margin: 6px 0px;
678 margin: 6px 0px;
677 }
679 }
678 .filediff-menu {
680 .filediff-menu {
679 display: none;
681 display: none;
680 }
682 }
681
683
682 }
684 }
683
685
684 &+ .filediff { /* file diff is expanded */
686 &+ .filediff { /* file diff is expanded */
685 .filediff-collapse-indicator {
687 .filediff-collapse-indicator {
686 width: 0;
688 width: 0;
687 height: 0;
689 height: 0;
688 border-style: solid;
690 border-style: solid;
689 border-width: 9.3px 4.5px 0 4.5px;
691 border-width: 9.3px 4.5px 0 4.5px;
690 border-color: #aaa transparent transparent transparent;
692 border-color: #aaa transparent transparent transparent;
691 margin: 6px 0px;
693 margin: 6px 0px;
692
694
693 }
695 }
694 .filediff-menu {
696 .filediff-menu {
695 display: block;
697 display: block;
696 }
698 }
697 margin: 10px 0;
699 margin: 10px 0;
698 &:nth-child(2) {
700 &:nth-child(2) {
699 margin: 0;
701 margin: 0;
700 }
702 }
701 }
703 }
702 }
704 }
703
705
704 .filediffs .anchor {
706 .filediffs .anchor {
705 display: block;
707 display: block;
706 height: 40px;
708 height: 40px;
707 margin-top: -40px;
709 margin-top: -40px;
708 visibility: hidden;
710 visibility: hidden;
709 }
711 }
710
712
711 .filediffs .anchor:nth-of-type(1) {
713 .filediffs .anchor:nth-of-type(1) {
712 display: block;
714 display: block;
713 height: 80px;
715 height: 80px;
714 margin-top: -80px;
716 margin-top: -80px;
715 visibility: hidden;
717 visibility: hidden;
716 }
718 }
717
719
718 .cs_files {
720 .cs_files {
719 clear: both;
721 clear: both;
720 }
722 }
721
723
722 #diff-file-sticky{
724 #diff-file-sticky{
723 will-change: min-height;
725 will-change: min-height;
724 }
726 }
725
727
726 .sidebar__inner{
728 .sidebar__inner{
727 transform: translate(0, 0); /* For browsers don't support translate3d. */
729 transform: translate(0, 0); /* For browsers don't support translate3d. */
728 transform: translate3d(0, 0, 0);
730 transform: translate3d(0, 0, 0);
729 will-change: position, transform;
731 will-change: position, transform;
730 height: 70px;
732 height: 70px;
731 z-index: 30;
733 z-index: 30;
732 background-color: #fff;
734 background-color: #fff;
733 padding: 5px 0px;
735 padding: 5px 0px;
734 }
736 }
735
737
736 .sidebar__bar {
738 .sidebar__bar {
737 padding: 5px 0px 0px 0px
739 padding: 5px 0px 0px 0px
738 }
740 }
739
741
740 .fpath-placeholder {
742 .fpath-placeholder {
741 clear: both;
743 clear: both;
742 visibility: hidden
744 visibility: hidden
743 }
745 }
744
746
745 .is-affixed {
747 .is-affixed {
746 .sidebar_inner_shadow {
748 .sidebar_inner_shadow {
747 position: fixed;
749 position: fixed;
748 top: 75px;
750 top: 75px;
749 right: -100%;
751 right: -100%;
750 left: -100%;
752 left: -100%;
751 z-index: 28;
753 z-index: 28;
752 display: block;
754 display: block;
753 height: 5px;
755 height: 5px;
754 content: "";
756 content: "";
755 background: linear-gradient(rgba(0, 0, 0, 0.075), rgba(0, 0, 0, 0.001)) repeat-x 0 0;
757 background: linear-gradient(rgba(0, 0, 0, 0.075), rgba(0, 0, 0, 0.001)) repeat-x 0 0;
756 border-top: 1px solid rgba(0, 0, 0, 0.15);
758 border-top: 1px solid rgba(0, 0, 0, 0.15);
757 }
759 }
758 .fpath-placeholder {
760 .fpath-placeholder {
759 visibility: visible !important;
761 visibility: visible !important;
760 }
762 }
761 }
763 }
762
764
763 .diffset-menu {
765 .diffset-menu {
764 margin-bottom: 20px;
766 margin-bottom: 20px;
765 }
767 }
766 .diffset {
768 .diffset {
767 margin: 20px auto;
769 margin: 20px auto;
768 .diffset-heading {
770 .diffset-heading {
769 border: 1px solid @grey5;
771 border: 1px solid @grey5;
770 margin-bottom: -1px;
772 margin-bottom: -1px;
771 // margin-top: 20px;
773 // margin-top: 20px;
772 h2 {
774 h2 {
773 margin: 0;
775 margin: 0;
774 line-height: 38px;
776 line-height: 38px;
775 padding-left: 10px;
777 padding-left: 10px;
776 }
778 }
777 .btn {
779 .btn {
778 margin: 0;
780 margin: 0;
779 }
781 }
780 background: @grey6;
782 background: @grey6;
781 display: block;
783 display: block;
782 padding: 5px;
784 padding: 5px;
783 }
785 }
784 .diffset-heading-warning {
786 .diffset-heading-warning {
785 background: @alert3-inner;
787 background: @alert3-inner;
786 border: 1px solid @alert3;
788 border: 1px solid @alert3;
787 }
789 }
788 &.diffset-comments-disabled {
790 &.diffset-comments-disabled {
789 .cb-comment-box-opener, .comment-inline-form, .cb-comment-add-button {
791 .cb-comment-box-opener, .comment-inline-form, .cb-comment-add-button {
790 display: none !important;
792 display: none !important;
791 }
793 }
792 }
794 }
793 }
795 }
794
796
795 .filelist {
797 .filelist {
796 .pill {
798 .pill {
797 display: block;
799 display: block;
798 float: left;
800 float: left;
799 padding: @pill-padding-small;
801 padding: @pill-padding-small;
800 }
802 }
801 }
803 }
802
804
803 .pill {
805 .pill {
804 display: block;
806 display: block;
805 float: left;
807 float: left;
806 padding: @pill-padding;
808 padding: @pill-padding;
807 }
809 }
808
810
809 .pill-group {
811 .pill-group {
810 .pill {
812 .pill {
811 opacity: .8;
813 opacity: .8;
812 margin-right: 3px;
814 margin-right: 3px;
813 font-size: 12px;
815 font-size: 12px;
814 font-weight: normal;
816 font-weight: normal;
815
817
816 &:first-child {
818 &:first-child {
817 border-radius: @border-radius 0 0 @border-radius;
819 border-radius: @border-radius 0 0 @border-radius;
818 }
820 }
819 &:last-child {
821 &:last-child {
820 border-radius: 0 @border-radius @border-radius 0;
822 border-radius: 0 @border-radius @border-radius 0;
821 }
823 }
822 &:only-child {
824 &:only-child {
823 border-radius: @border-radius;
825 border-radius: @border-radius;
824 margin-right: 0;
826 margin-right: 0;
825 }
827 }
826 }
828 }
827 }
829 }
828
830
829 /* Main comments*/
831 /* Main comments*/
830 #comments {
832 #comments {
831 .comment-selected {
833 .comment-selected {
832 border-left: 6px solid @comment-highlight-color;
834 border-left: 6px solid @comment-highlight-color;
833 padding-left: 3px;
835 padding-left: 3px;
834 margin-left: -9px;
836 margin-left: -9px;
835 }
837 }
836 }
838 }
837
839
838 .filediff {
840 .filediff {
839 border: 1px solid @grey5;
841 border: 1px solid @grey5;
840
842
841 /* START OVERRIDES */
843 /* START OVERRIDES */
842 .code-highlight {
844 .code-highlight {
843 border: none; // TODO: remove this border from the global
845 border: none; // TODO: remove this border from the global
844 // .code-highlight, it doesn't belong there
846 // .code-highlight, it doesn't belong there
845 }
847 }
846 label {
848 label {
847 margin: 0; // TODO: remove this margin definition from global label
849 margin: 0; // TODO: remove this margin definition from global label
848 // it doesn't belong there - if margin on labels
850 // it doesn't belong there - if margin on labels
849 // are needed for a form they should be defined
851 // are needed for a form they should be defined
850 // in the form's class
852 // in the form's class
851 }
853 }
852 /* END OVERRIDES */
854 /* END OVERRIDES */
853
855
854 * {
856 * {
855 box-sizing: border-box;
857 box-sizing: border-box;
856 }
858 }
857 .filediff-anchor {
859 .filediff-anchor {
858 visibility: hidden;
860 visibility: hidden;
859 }
861 }
860 &:hover {
862 &:hover {
861 .filediff-anchor {
863 .filediff-anchor {
862 visibility: visible;
864 visibility: visible;
863 }
865 }
864 }
866 }
865
867
866 .filediff-collapse-indicator {
868 .filediff-collapse-indicator {
867 border-style: solid;
869 border-style: solid;
868 float: left;
870 float: left;
869 margin: 4px 0px 0 0;
871 margin: 4px 0px 0 0;
870 cursor: pointer;
872 cursor: pointer;
871 }
873 }
872
874
873 .filediff-heading {
875 .filediff-heading {
874 background: @grey7;
876 background: @grey7;
875 cursor: pointer;
877 cursor: pointer;
876 display: block;
878 display: block;
877 padding: 5px 10px;
879 padding: 5px 10px;
878 }
880 }
879 .filediff-heading:after {
881 .filediff-heading:after {
880 content: "";
882 content: "";
881 display: table;
883 display: table;
882 clear: both;
884 clear: both;
883 }
885 }
884 .filediff-heading:hover {
886 .filediff-heading:hover {
885 background: #e1e9f4 !important;
887 background: #e1e9f4 !important;
886 }
888 }
887
889
888 .filediff-menu {
890 .filediff-menu {
889 float: right;
891 float: right;
890 text-align: right;
892 text-align: right;
891 padding: 5px 5px 5px 0px;
893 padding: 5px 5px 5px 0px;
892
894
893 &> a,
895 &> a,
894 &> span {
896 &> span {
895 padding: 1px;
897 padding: 1px;
896 }
898 }
897 }
899 }
898
900
899 .filediff-collapse-button, .filediff-expand-button {
901 .filediff-collapse-button, .filediff-expand-button {
900 cursor: pointer;
902 cursor: pointer;
901 }
903 }
902 .filediff-collapse-button {
904 .filediff-collapse-button {
903 display: inline;
905 display: inline;
904 }
906 }
905 .filediff-expand-button {
907 .filediff-expand-button {
906 display: none;
908 display: none;
907 }
909 }
908 .filediff-collapsed .filediff-collapse-button {
910 .filediff-collapsed .filediff-collapse-button {
909 display: none;
911 display: none;
910 }
912 }
911 .filediff-collapsed .filediff-expand-button {
913 .filediff-collapsed .filediff-expand-button {
912 display: inline;
914 display: inline;
913 }
915 }
914
916
915 /**** COMMENTS ****/
917 /**** COMMENTS ****/
916
918
917 .filediff-menu {
919 .filediff-menu {
918 .show-comment-button {
920 .show-comment-button {
919 display: none;
921 display: none;
920 }
922 }
921 }
923 }
922 &.hide-comments {
924 &.hide-comments {
923 .inline-comments {
925 .inline-comments {
924 display: none;
926 display: none;
925 }
927 }
926 .filediff-menu {
928 .filediff-menu {
927 .show-comment-button {
929 .show-comment-button {
928 display: inline;
930 display: inline;
929 }
931 }
930 .hide-comment-button {
932 .hide-comment-button {
931 display: none;
933 display: none;
932 }
934 }
933 }
935 }
934 }
936 }
935
937
936 .hide-line-comments {
938 .hide-line-comments {
937 .inline-comments {
939 .inline-comments {
938 display: none;
940 display: none;
939 }
941 }
940 }
942 }
941
943
942 /**** END COMMENTS ****/
944 /**** END COMMENTS ****/
943
945
944 }
946 }
945
947
946
948
947
949
948 .filediff, .filelist {
950 .filediff, .filelist {
949 .pill {
951 .pill {
950 &[op="name"] {
952 &[op="name"] {
951 background: none;
953 background: none;
952 opacity: 1;
954 opacity: 1;
953 color: white;
955 color: white;
954 }
956 }
955 &[op="limited"] {
957 &[op="limited"] {
956 background: @grey2;
958 background: @grey2;
957 color: white;
959 color: white;
958 }
960 }
959 &[op="binary"] {
961 &[op="binary"] {
960 background: @color7;
962 background: @color7;
961 color: white;
963 color: white;
962 }
964 }
963 &[op="modified"] {
965 &[op="modified"] {
964 background: @alert1;
966 background: @alert1;
965 color: white;
967 color: white;
966 }
968 }
967 &[op="renamed"] {
969 &[op="renamed"] {
968 background: @color4;
970 background: @color4;
969 color: white;
971 color: white;
970 }
972 }
971 &[op="copied"] {
973 &[op="copied"] {
972 background: @color4;
974 background: @color4;
973 color: white;
975 color: white;
974 }
976 }
975 &[op="mode"] {
977 &[op="mode"] {
976 background: @grey3;
978 background: @grey3;
977 color: white;
979 color: white;
978 }
980 }
979 &[op="symlink"] {
981 &[op="symlink"] {
980 background: @color8;
982 background: @color8;
981 color: white;
983 color: white;
982 }
984 }
983
985
984 &[op="added"] { /* added lines */
986 &[op="added"] { /* added lines */
985 background: @alert1;
987 background: @alert1;
986 color: white;
988 color: white;
987 }
989 }
988 &[op="deleted"] { /* deleted lines */
990 &[op="deleted"] { /* deleted lines */
989 background: @alert2;
991 background: @alert2;
990 color: white;
992 color: white;
991 }
993 }
992
994
993 &[op="created"] { /* created file */
995 &[op="created"] { /* created file */
994 background: @alert1;
996 background: @alert1;
995 color: white;
997 color: white;
996 }
998 }
997 &[op="removed"] { /* deleted file */
999 &[op="removed"] { /* deleted file */
998 background: @color5;
1000 background: @color5;
999 color: white;
1001 color: white;
1000 }
1002 }
1001 }
1003 }
1002 }
1004 }
1003
1005
1004
1006
1005 .filediff-outdated {
1007 .filediff-outdated {
1006 padding: 8px 0;
1008 padding: 8px 0;
1007
1009
1008 .filediff-heading {
1010 .filediff-heading {
1009 opacity: .5;
1011 opacity: .5;
1010 }
1012 }
1011 }
1013 }
1012
1014
1013 table.cb {
1015 table.cb {
1014 width: 100%;
1016 width: 100%;
1015 border-collapse: collapse;
1017 border-collapse: collapse;
1016
1018
1017 .cb-text {
1019 .cb-text {
1018 padding: @cb-text-padding;
1020 padding: @cb-text-padding;
1019 }
1021 }
1020 .cb-hunk {
1022 .cb-hunk {
1021 padding: @cb-text-padding;
1023 padding: @cb-text-padding;
1022 }
1024 }
1023 .cb-expand {
1025 .cb-expand {
1024 display: none;
1026 display: none;
1025 }
1027 }
1026 .cb-collapse {
1028 .cb-collapse {
1027 display: inline;
1029 display: inline;
1028 }
1030 }
1029 &.cb-collapsed {
1031 &.cb-collapsed {
1030 .cb-line {
1032 .cb-line {
1031 display: none;
1033 display: none;
1032 }
1034 }
1033 .cb-expand {
1035 .cb-expand {
1034 display: inline;
1036 display: inline;
1035 }
1037 }
1036 .cb-collapse {
1038 .cb-collapse {
1037 display: none;
1039 display: none;
1038 }
1040 }
1039 .cb-hunk {
1041 .cb-hunk {
1040 display: none;
1042 display: none;
1041 }
1043 }
1042 }
1044 }
1043
1045
1044 /* intentionally general selector since .cb-line-selected must override it
1046 /* intentionally general selector since .cb-line-selected must override it
1045 and they both use !important since the td itself may have a random color
1047 and they both use !important since the td itself may have a random color
1046 generated by annotation blocks. TLDR: if you change it, make sure
1048 generated by annotation blocks. TLDR: if you change it, make sure
1047 annotated block selection and line selection in file view still work */
1049 annotated block selection and line selection in file view still work */
1048 .cb-line-fresh .cb-content {
1050 .cb-line-fresh .cb-content {
1049 background: white !important;
1051 background: white !important;
1050 }
1052 }
1051 .cb-warning {
1053 .cb-warning {
1052 background: #fff4dd;
1054 background: #fff4dd;
1053 }
1055 }
1054
1056
1055 &.cb-diff-sideside {
1057 &.cb-diff-sideside {
1056 td {
1058 td {
1057 &.cb-content {
1059 &.cb-content {
1058 width: 50%;
1060 width: 50%;
1059 }
1061 }
1060 }
1062 }
1061 }
1063 }
1062
1064
1063 tr {
1065 tr {
1064 &.cb-annotate {
1066 &.cb-annotate {
1065 border-top: 1px solid #eee;
1067 border-top: 1px solid #eee;
1066 }
1068 }
1067
1069
1068 &.cb-comment-info {
1070 &.cb-comment-info {
1069 border-top: 1px solid #eee;
1071 border-top: 1px solid #eee;
1070 color: rgba(0, 0, 0, 0.3);
1072 color: rgba(0, 0, 0, 0.3);
1071 background: #edf2f9;
1073 background: #edf2f9;
1072
1074
1073 td {
1075 td {
1074
1076
1075 }
1077 }
1076 }
1078 }
1077
1079
1078 &.cb-hunk {
1080 &.cb-hunk {
1079 font-family: @text-monospace;
1081 font-family: @text-monospace;
1080 color: rgba(0, 0, 0, 0.3);
1082 color: rgba(0, 0, 0, 0.3);
1081
1083
1082 td {
1084 td {
1083 &:first-child {
1085 &:first-child {
1084 background: #edf2f9;
1086 background: #edf2f9;
1085 }
1087 }
1086 &:last-child {
1088 &:last-child {
1087 background: #f4f7fb;
1089 background: #f4f7fb;
1088 }
1090 }
1089 }
1091 }
1090 }
1092 }
1091 }
1093 }
1092
1094
1093
1095
1094 td {
1096 td {
1095 vertical-align: top;
1097 vertical-align: top;
1096 padding: 0;
1098 padding: 0;
1097
1099
1098 &.cb-content {
1100 &.cb-content {
1099 font-size: 12.35px;
1101 font-size: 12.35px;
1100
1102
1101 &.cb-line-selected .cb-code {
1103 &.cb-line-selected .cb-code {
1102 background: @comment-highlight-color !important;
1104 background: @comment-highlight-color !important;
1103 }
1105 }
1104
1106
1105 span.cb-code {
1107 span.cb-code {
1106 line-height: @cb-line-height;
1108 line-height: @cb-line-height;
1107 padding-left: @cb-line-code-padding;
1109 padding-left: @cb-line-code-padding;
1108 padding-right: @cb-line-code-padding;
1110 padding-right: @cb-line-code-padding;
1109 display: block;
1111 display: block;
1110 white-space: pre-wrap;
1112 white-space: pre-wrap;
1111 font-family: @text-monospace;
1113 font-family: @text-monospace;
1112 word-break: break-all;
1114 word-break: break-all;
1113 .nonl {
1115 .nonl {
1114 color: @color5;
1116 color: @color5;
1115 }
1117 }
1116 .cb-action {
1118 .cb-action {
1117 &:before {
1119 &:before {
1118 content: " ";
1120 content: " ";
1119 }
1121 }
1120 &.cb-deletion:before {
1122 &.cb-deletion:before {
1121 content: "- ";
1123 content: "- ";
1122 }
1124 }
1123 &.cb-addition:before {
1125 &.cb-addition:before {
1124 content: "+ ";
1126 content: "+ ";
1125 }
1127 }
1126 }
1128 }
1127 }
1129 }
1128
1130
1129 &> button.cb-comment-box-opener {
1131 &> button.cb-comment-box-opener {
1130
1132
1131 padding: 2px 2px 1px 3px;
1133 padding: 2px 2px 1px 3px;
1132 margin-left: -6px;
1134 margin-left: -6px;
1133 margin-top: -1px;
1135 margin-top: -1px;
1134
1136
1135 border-radius: @border-radius;
1137 border-radius: @border-radius;
1136 position: absolute;
1138 position: absolute;
1137 display: none;
1139 display: none;
1138 }
1140 }
1139 .cb-comment {
1141 .cb-comment {
1140 margin-top: 10px;
1142 margin-top: 10px;
1141 white-space: normal;
1143 white-space: normal;
1142 }
1144 }
1143 }
1145 }
1144 &:hover {
1146 &:hover {
1145 button.cb-comment-box-opener {
1147 button.cb-comment-box-opener {
1146 display: block;
1148 display: block;
1147 }
1149 }
1148 &+ td button.cb-comment-box-opener {
1150 &+ td button.cb-comment-box-opener {
1149 display: block
1151 display: block
1150 }
1152 }
1151 }
1153 }
1152
1154
1153 &.cb-data {
1155 &.cb-data {
1154 text-align: right;
1156 text-align: right;
1155 width: 30px;
1157 width: 30px;
1156 font-family: @text-monospace;
1158 font-family: @text-monospace;
1157
1159
1158 .icon-comment {
1160 .icon-comment {
1159 cursor: pointer;
1161 cursor: pointer;
1160 }
1162 }
1161 &.cb-line-selected {
1163 &.cb-line-selected {
1162 background: @comment-highlight-color !important;
1164 background: @comment-highlight-color !important;
1163 }
1165 }
1164 &.cb-line-selected > div {
1166 &.cb-line-selected > div {
1165 display: block;
1167 display: block;
1166 background: @comment-highlight-color !important;
1168 background: @comment-highlight-color !important;
1167 line-height: @cb-line-height;
1169 line-height: @cb-line-height;
1168 color: rgba(0, 0, 0, 0.3);
1170 color: rgba(0, 0, 0, 0.3);
1169 }
1171 }
1170 }
1172 }
1171
1173
1172 &.cb-lineno {
1174 &.cb-lineno {
1173 padding: 0;
1175 padding: 0;
1174 width: 50px;
1176 width: 50px;
1175 color: rgba(0, 0, 0, 0.3);
1177 color: rgba(0, 0, 0, 0.3);
1176 text-align: right;
1178 text-align: right;
1177 border-right: 1px solid #eee;
1179 border-right: 1px solid #eee;
1178 font-family: @text-monospace;
1180 font-family: @text-monospace;
1179 -webkit-user-select: none;
1181 -webkit-user-select: none;
1180 -moz-user-select: none;
1182 -moz-user-select: none;
1181 user-select: none;
1183 user-select: none;
1182
1184
1183 a::before {
1185 a::before {
1184 content: attr(data-line-no);
1186 content: attr(data-line-no);
1185 }
1187 }
1186 &.cb-line-selected {
1188 &.cb-line-selected {
1187 background: @comment-highlight-color !important;
1189 background: @comment-highlight-color !important;
1188 }
1190 }
1189
1191
1190 a {
1192 a {
1191 display: block;
1193 display: block;
1192 padding-right: @cb-line-code-padding;
1194 padding-right: @cb-line-code-padding;
1193 padding-left: @cb-line-code-padding;
1195 padding-left: @cb-line-code-padding;
1194 line-height: @cb-line-height;
1196 line-height: @cb-line-height;
1195 color: rgba(0, 0, 0, 0.3);
1197 color: rgba(0, 0, 0, 0.3);
1196 }
1198 }
1197 }
1199 }
1198
1200
1199 &.cb-empty {
1201 &.cb-empty {
1200 background: @grey7;
1202 background: @grey7;
1201 }
1203 }
1202
1204
1203 ins {
1205 ins {
1204 color: black;
1206 color: black;
1205 background: #a6f3a6;
1207 background: #a6f3a6;
1206 text-decoration: none;
1208 text-decoration: none;
1207 }
1209 }
1208 del {
1210 del {
1209 color: black;
1211 color: black;
1210 background: #f8cbcb;
1212 background: #f8cbcb;
1211 text-decoration: none;
1213 text-decoration: none;
1212 }
1214 }
1213 &.cb-addition {
1215 &.cb-addition {
1214 background: #ecffec;
1216 background: #ecffec;
1215
1217
1216 &.blob-lineno {
1218 &.blob-lineno {
1217 background: #ddffdd;
1219 background: #ddffdd;
1218 }
1220 }
1219 }
1221 }
1220 &.cb-deletion {
1222 &.cb-deletion {
1221 background: #ffecec;
1223 background: #ffecec;
1222
1224
1223 &.blob-lineno {
1225 &.blob-lineno {
1224 background: #ffdddd;
1226 background: #ffdddd;
1225 }
1227 }
1226 }
1228 }
1227 &.cb-annotate-message-spacer {
1229 &.cb-annotate-message-spacer {
1228 width:8px;
1230 width:8px;
1229 padding: 1px 0px 0px 3px;
1231 padding: 1px 0px 0px 3px;
1230 }
1232 }
1231 &.cb-annotate-info {
1233 &.cb-annotate-info {
1232 width: 320px;
1234 width: 320px;
1233 min-width: 320px;
1235 min-width: 320px;
1234 max-width: 320px;
1236 max-width: 320px;
1235 padding: 5px 2px;
1237 padding: 5px 2px;
1236 font-size: 13px;
1238 font-size: 13px;
1237
1239
1238 .cb-annotate-message {
1240 .cb-annotate-message {
1239 padding: 2px 0px 0px 0px;
1241 padding: 2px 0px 0px 0px;
1240 white-space: pre-line;
1242 white-space: pre-line;
1241 overflow: hidden;
1243 overflow: hidden;
1242 }
1244 }
1243 .rc-user {
1245 .rc-user {
1244 float: none;
1246 float: none;
1245 padding: 0 6px 0 17px;
1247 padding: 0 6px 0 17px;
1246 min-width: unset;
1248 min-width: unset;
1247 min-height: unset;
1249 min-height: unset;
1248 }
1250 }
1249 }
1251 }
1250
1252
1251 &.cb-annotate-revision {
1253 &.cb-annotate-revision {
1252 cursor: pointer;
1254 cursor: pointer;
1253 text-align: right;
1255 text-align: right;
1254 padding: 1px 3px 0px 3px;
1256 padding: 1px 3px 0px 3px;
1255 }
1257 }
1256 }
1258 }
1257 }
1259 }
@@ -1,545 +1,544 b''
1 //
1 //
2 // Typography
2 // Typography
3 // modified from Bootstrap
3 // modified from Bootstrap
4 // --------------------------------------------------
4 // --------------------------------------------------
5
5
6 // Base
6 // Base
7 body {
7 body {
8 font-size: @basefontsize;
8 font-size: @basefontsize;
9 font-family: @text-light;
9 font-family: @text-light;
10 letter-spacing: .02em;
10 letter-spacing: .02em;
11 color: @grey2;
11 color: @grey2;
12 }
12 }
13
13
14 #content, label{
14 #content, label{
15 font-size: @basefontsize;
15 font-size: @basefontsize;
16 }
16 }
17
17
18 label {
18 label {
19 color: @grey2;
19 color: @grey2;
20 }
20 }
21
21
22 ::selection { background: @rchighlightblue; }
22 ::selection { background: @rchighlightblue; }
23
23
24 // Headings
24 // Headings
25 // -------------------------
25 // -------------------------
26
26
27 h1, h2, h3, h4, h5, h6,
27 h1, h2, h3, h4, h5, h6,
28 .h1, .h2, .h3, .h4, .h5, .h6 {
28 .h1, .h2, .h3, .h4, .h5, .h6 {
29 margin: 0 0 @textmargin 0;
29 margin: 0 0 @textmargin 0;
30 padding: 0;
30 padding: 0;
31 line-height: 1.8em;
31 line-height: 1.8em;
32 color: @text-color;
32 color: @text-color;
33 a {
33 a {
34 color: @rcblue;
34 color: @rcblue;
35 }
35 }
36 }
36 }
37
37
38 h1, .h1 { font-size: 1.54em; font-weight: @text-bold-weight; font-family: @text-bold; }
38 h1, .h1 { font-size: 1.54em; font-weight: @text-bold-weight; font-family: @text-bold; }
39 h2, .h2 { font-size: 1.23em; font-weight: @text-semibold-weight; font-family: @text-semibold; }
39 h2, .h2 { font-size: 1.23em; font-weight: @text-semibold-weight; font-family: @text-semibold; }
40 h3, .h3 { font-size: 1.23em; font-family: @text-regular; }
40 h3, .h3 { font-size: 1.23em; font-family: @text-regular; }
41 h4, .h4 { font-size: 1em; font-weight: @text-bold-weight; font-family: @text-bold; }
41 h4, .h4 { font-size: 1em; font-weight: @text-bold-weight; font-family: @text-bold; }
42 h5, .h5 { font-size: 1em; font-weight: @text-bold-weight; font-family: @text-bold; }
42 h5, .h5 { font-size: 1em; font-weight: @text-bold-weight; font-family: @text-bold; }
43 h6, .h6 { font-size: 1em; font-weight: @text-bold-weight; font-family: @text-bold; }
43 h6, .h6 { font-size: 1em; font-weight: @text-bold-weight; font-family: @text-bold; }
44
44
45 // Breadcrumbs
45 // Breadcrumbs
46 .breadcrumbs {
46 .breadcrumbs {
47 font-size: @repo-title-fontsize;
47 font-size: @repo-title-fontsize;
48 margin: 0;
48 margin: 0;
49 }
49 }
50
50
51 .breadcrumbs_light {
51 .breadcrumbs_light {
52 float:left;
52 float:left;
53 font-size: 1.3em;
53 font-size: 1.3em;
54 line-height: 38px;
54 line-height: 38px;
55 }
55 }
56
56
57 // Body text
57 // Body text
58 // -------------------------
58 // -------------------------
59
59
60 p {
60 p {
61 margin: 0 0 @textmargin 0;
61 margin: 0 0 @textmargin 0;
62 padding: 0;
62 padding: 0;
63 line-height: 2em;
63 line-height: 2em;
64 }
64 }
65
65
66 .lead {
66 .lead {
67 margin-bottom: @textmargin;
67 margin-bottom: @textmargin;
68 font-weight: 300;
68 font-weight: 300;
69 line-height: 1.4;
69 line-height: 1.4;
70
70
71 @media (min-width: @screen-sm-min) {
71 @media (min-width: @screen-sm-min) {
72 font-size: (@basefontsize * 1.5);
72 font-size: (@basefontsize * 1.5);
73 }
73 }
74 }
74 }
75
75
76 a,
76 a,
77 .link {
77 .link {
78 color: @rcblue;
78 color: @rcblue;
79 text-decoration: none;
79 text-decoration: none;
80 outline: none;
80 outline: none;
81 cursor: pointer;
81 cursor: pointer;
82
82
83 &:focus {
83 &:focus {
84 outline: none;
84 outline: none;
85 }
85 }
86
86
87 &:hover {
87 &:hover {
88 color: @rcdarkblue;
88 color: @rcdarkblue;
89 }
89 }
90 }
90 }
91
91
92 img {
92 img {
93 border: none;
93 border: none;
94 outline: none;
94 outline: none;
95 }
95 }
96
96
97 strong {
97 strong {
98 font-weight: @text-bold-weight;
98 font-weight: @text-bold-weight;
99 font-family: @text-bold;
99 font-family: @text-bold;
100 }
100 }
101
101
102 em {
102 em {
103 font-family: @text-italic;
103 font-family: @text-italic;
104 font-style: italic;
104 font-style: italic;
105 }
105 }
106
106
107 strong em,
107 strong em,
108 em strong {
108 em strong {
109 font-style: italic;
109 font-style: italic;
110 font-weight: @text-bold-italic-weight;
110 font-weight: @text-bold-italic-weight;
111 font-family: @text-bold-italic;
111 font-family: @text-bold-italic;
112 }
112 }
113
113
114 //TODO: lisa: b and i are depreciated, but we are still using them in places.
114 //TODO: lisa: b and i are depreciated, but we are still using them in places.
115 // Should probably make some decision whether to keep or lose these.
115 // Should probably make some decision whether to keep or lose these.
116 b {
116 b {
117
117
118 }
118 }
119
119
120 i {
120 i {
121 font-style: normal;
121 font-style: normal;
122 }
122 }
123
123
124 label {
124 label {
125 color: @text-color;
125 color: @text-color;
126
126
127 input[type="checkbox"] {
127 input[type="checkbox"] {
128 margin-right: 1em;
128 margin-right: 1em;
129 }
129 }
130 input[type="radio"] {
130 input[type="radio"] {
131 margin-right: 1em;
131 margin-right: 1em;
132 }
132 }
133 }
133 }
134
134
135 code,
135 code,
136 .code {
136 .code {
137 font-size: .95em;
137 font-size: .95em;
138 font-family: @text-code;
138 font-family: @text-code;
139 color: @grey3;
139 color: @grey3;
140
140
141 a {
141 a {
142 color: lighten(@rcblue,10%)
142 color: lighten(@rcblue,10%)
143 }
143 }
144 }
144 }
145
145
146 pre {
146 pre {
147 margin: 0;
147 margin: 0;
148 padding: 0;
148 padding: 0;
149 border: 0;
149 border: 0;
150 outline: 0;
150 outline: 0;
151 font-size: @basefontsize*.95;
151 font-size: @basefontsize*.95;
152 line-height: 1.4em;
152 line-height: 1.4em;
153 font-family: @text-code;
153 font-family: @text-code;
154 color: @grey3;
154 color: @grey3;
155 }
155 }
156
156
157 // Emphasis & misc
157 // Emphasis & misc
158 // -------------------------
158 // -------------------------
159
159
160 small,
160 small,
161 .small {
161 .small {
162 font-size: 75%;
162 font-size: 75%;
163 font-weight: normal;
163 font-weight: normal;
164 line-height: 1em;
164 line-height: 1em;
165 }
165 }
166
166
167 mark,
167 mark,
168 .mark {
168 .mark {
169 background-color: @rclightblue;
170 padding: .2em;
169 padding: .2em;
171 }
170 }
172
171
173 // Alignment
172 // Alignment
174 .text-left { text-align: left; }
173 .text-left { text-align: left; }
175 .text-right { text-align: right; }
174 .text-right { text-align: right; }
176 .text-center { text-align: center; }
175 .text-center { text-align: center; }
177 .text-justify { text-align: justify; }
176 .text-justify { text-align: justify; }
178 .text-nowrap { white-space: nowrap; }
177 .text-nowrap { white-space: nowrap; }
179
178
180 // Transformation
179 // Transformation
181 .text-lowercase { text-transform: lowercase; }
180 .text-lowercase { text-transform: lowercase; }
182 .text-uppercase { text-transform: uppercase; }
181 .text-uppercase { text-transform: uppercase; }
183 .text-capitalize { text-transform: capitalize; }
182 .text-capitalize { text-transform: capitalize; }
184
183
185 // Contextual colors
184 // Contextual colors
186 .text-muted {
185 .text-muted {
187 color: @grey4;
186 color: @grey4;
188 }
187 }
189 .text-primary {
188 .text-primary {
190 color: @rcblue;
189 color: @rcblue;
191 }
190 }
192 .text-success {
191 .text-success {
193 color: @alert1;
192 color: @alert1;
194 }
193 }
195 .text-info {
194 .text-info {
196 color: @alert4;
195 color: @alert4;
197 }
196 }
198 .text-warning {
197 .text-warning {
199 color: @alert3;
198 color: @alert3;
200 }
199 }
201 .text-danger {
200 .text-danger {
202 color: @alert2;
201 color: @alert2;
203 }
202 }
204
203
205 // Contextual backgrounds
204 // Contextual backgrounds
206 .bg-primary {
205 .bg-primary {
207 background-color: white;
206 background-color: white;
208 }
207 }
209 .bg-success {
208 .bg-success {
210 background-color: @alert1;
209 background-color: @alert1;
211 }
210 }
212 .bg-info {
211 .bg-info {
213 background-color: @alert4;
212 background-color: @alert4;
214 }
213 }
215 .bg-warning {
214 .bg-warning {
216 background-color: @alert3;
215 background-color: @alert3;
217 }
216 }
218 .bg-danger {
217 .bg-danger {
219 background-color: @alert2;
218 background-color: @alert2;
220 }
219 }
221
220
222
221
223 // Page header
222 // Page header
224 // -------------------------
223 // -------------------------
225
224
226 .page-header {
225 .page-header {
227 margin: @pagepadding 0 @textmargin;
226 margin: @pagepadding 0 @textmargin;
228 border-bottom: @border-thickness solid @grey5;
227 border-bottom: @border-thickness solid @grey5;
229 }
228 }
230
229
231 .title {
230 .title {
232 clear: both;
231 clear: both;
233 float: left;
232 float: left;
234 width: 100%;
233 width: 100%;
235 margin: @pagepadding/2 0 @pagepadding;
234 margin: @pagepadding/2 0 @pagepadding;
236
235
237 .breadcrumbs {
236 .breadcrumbs {
238 float: left;
237 float: left;
239 clear: both;
238 clear: both;
240 width: 700px;
239 width: 700px;
241 margin: 0;
240 margin: 0;
242
241
243 .q_filter_box {
242 .q_filter_box {
244 margin-right: @padding;
243 margin-right: @padding;
245 }
244 }
246 }
245 }
247
246
248 h1 a {
247 h1 a {
249 color: @rcblue;
248 color: @rcblue;
250 }
249 }
251
250
252 input{
251 input{
253 margin-right: @padding;
252 margin-right: @padding;
254 }
253 }
255
254
256 h5, .h5 {
255 h5, .h5 {
257 color: @grey1;
256 color: @grey1;
258 margin-bottom: @space;
257 margin-bottom: @space;
259
258
260 span {
259 span {
261 display: inline-block;
260 display: inline-block;
262 }
261 }
263 }
262 }
264
263
265 p {
264 p {
266 margin-bottom: 0;
265 margin-bottom: 0;
267 }
266 }
268
267
269 .links {
268 .links {
270 float: right;
269 float: right;
271 display: inline;
270 display: inline;
272 margin: 0;
271 margin: 0;
273 padding-left: 0;
272 padding-left: 0;
274 list-style: none;
273 list-style: none;
275 text-align: right;
274 text-align: right;
276
275
277 li {
276 li {
278 float: right;
277 float: right;
279 list-style-type: none;
278 list-style-type: none;
280 }
279 }
281
280
282 a {
281 a {
283 display: inline-block;
282 display: inline-block;
284 margin-left: @textmargin/2;
283 margin-left: @textmargin/2;
285 }
284 }
286 }
285 }
287
286
288 .title-content {
287 .title-content {
289 float: left;
288 float: left;
290 margin: 0;
289 margin: 0;
291 padding: 0;
290 padding: 0;
292
291
293 & + .breadcrumbs {
292 & + .breadcrumbs {
294 margin-top: @padding;
293 margin-top: @padding;
295 }
294 }
296
295
297 & + .links {
296 & + .links {
298 margin-top: -@button-padding;
297 margin-top: -@button-padding;
299
298
300 & + .breadcrumbs {
299 & + .breadcrumbs {
301 margin-top: @padding;
300 margin-top: @padding;
302 }
301 }
303 }
302 }
304 }
303 }
305
304
306 .title-main {
305 .title-main {
307 font-size: @repo-title-fontsize;
306 font-size: @repo-title-fontsize;
308 }
307 }
309
308
310 .title-description {
309 .title-description {
311 margin-top: .5em;
310 margin-top: .5em;
312 }
311 }
313
312
314 .q_filter_box {
313 .q_filter_box {
315 width: 200px;
314 width: 200px;
316 }
315 }
317
316
318 }
317 }
319
318
320 #readme .title {
319 #readme .title {
321 text-transform: none;
320 text-transform: none;
322 }
321 }
323
322
324 // Lists
323 // Lists
325 // -------------------------
324 // -------------------------
326
325
327 // Unordered and Ordered lists
326 // Unordered and Ordered lists
328 ul,
327 ul,
329 ol {
328 ol {
330 margin-top: 0;
329 margin-top: 0;
331 margin-bottom: @textmargin;
330 margin-bottom: @textmargin;
332 ul,
331 ul,
333 ol {
332 ol {
334 margin-bottom: 0;
333 margin-bottom: 0;
335 }
334 }
336 }
335 }
337
336
338 li {
337 li {
339 line-height: 2em;
338 line-height: 2em;
340 }
339 }
341
340
342 ul li {
341 ul li {
343 position: relative;
342 position: relative;
344 list-style-type: disc;
343 list-style-type: disc;
345
344
346 p:first-child {
345 p:first-child {
347 display:inline;
346 display:inline;
348 }
347 }
349 }
348 }
350
349
351 // List options
350 // List options
352
351
353 // Unstyled keeps list items block level, just removes default browser padding and list-style
352 // Unstyled keeps list items block level, just removes default browser padding and list-style
354 .list-unstyled {
353 .list-unstyled {
355 padding-left: 0;
354 padding-left: 0;
356 list-style: none;
355 list-style: none;
357 li:before { content: none; }
356 li:before { content: none; }
358 }
357 }
359
358
360 // Inline turns list items into inline-block
359 // Inline turns list items into inline-block
361 .list-inline {
360 .list-inline {
362 .list-unstyled();
361 .list-unstyled();
363 margin-left: -5px;
362 margin-left: -5px;
364
363
365 > li {
364 > li {
366 display: inline-block;
365 display: inline-block;
367 padding-left: 5px;
366 padding-left: 5px;
368 padding-right: 5px;
367 padding-right: 5px;
369 }
368 }
370 }
369 }
371
370
372 // Description Lists
371 // Description Lists
373
372
374 dl {
373 dl {
375 margin-top: 0; // Remove browser default
374 margin-top: 0; // Remove browser default
376 margin-bottom: @textmargin;
375 margin-bottom: @textmargin;
377 }
376 }
378
377
379 dt,
378 dt,
380 dd {
379 dd {
381 line-height: 1.4em;
380 line-height: 1.4em;
382 }
381 }
383
382
384 dt {
383 dt {
385 margin: @textmargin 0 0 0;
384 margin: @textmargin 0 0 0;
386 font-weight: @text-bold-weight;
385 font-weight: @text-bold-weight;
387 font-family: @text-bold;
386 font-family: @text-bold;
388 }
387 }
389
388
390 dd {
389 dd {
391 margin-left: 0; // Undo browser default
390 margin-left: 0; // Undo browser default
392 }
391 }
393
392
394 // Horizontal description lists
393 // Horizontal description lists
395 // Defaults to being stacked without any of the below styles applied, until the
394 // Defaults to being stacked without any of the below styles applied, until the
396 // grid breakpoint is reached (default of ~768px).
395 // grid breakpoint is reached (default of ~768px).
397 // These are used in forms as well; see style guide.
396 // These are used in forms as well; see style guide.
398 // TODO: lisa: These should really not be used in forms.
397 // TODO: lisa: These should really not be used in forms.
399
398
400 .dl-horizontal {
399 .dl-horizontal {
401
400
402 overflow: hidden;
401 overflow: hidden;
403 margin-bottom: @space;
402 margin-bottom: @space;
404
403
405 dt, dd {
404 dt, dd {
406 float: left;
405 float: left;
407 margin: 5px 0 5px 0;
406 margin: 5px 0 5px 0;
408 }
407 }
409
408
410 dt {
409 dt {
411 clear: left;
410 clear: left;
412 width: @label-width - @form-vertical-margin;
411 width: @label-width - @form-vertical-margin;
413 }
412 }
414
413
415 dd {
414 dd {
416 &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present
415 &:extend(.clearfix all); // Clear the floated `dt` if an empty `dd` is present
417 margin-left: @form-vertical-margin;
416 margin-left: @form-vertical-margin;
418 max-width: @form-max-width - (@label-width - @form-vertical-margin) - @form-vertical-margin;
417 max-width: @form-max-width - (@label-width - @form-vertical-margin) - @form-vertical-margin;
419 }
418 }
420
419
421 pre {
420 pre {
422 margin: 0;
421 margin: 0;
423 }
422 }
424
423
425 &.settings {
424 &.settings {
426 dt {
425 dt {
427 text-align: left;
426 text-align: left;
428 }
427 }
429 }
428 }
430
429
431 @media (min-width: 768px) {
430 @media (min-width: 768px) {
432 dt {
431 dt {
433 float: left;
432 float: left;
434 width: 185px;
433 width: 185px;
435 clear: left;
434 clear: left;
436 text-align: right;
435 text-align: right;
437 }
436 }
438 dd {
437 dd {
439 margin-left: 20px;
438 margin-left: 20px;
440 }
439 }
441 }
440 }
442 }
441 }
443
442
444
443
445 // Misc
444 // Misc
446 // -------------------------
445 // -------------------------
447
446
448 // Abbreviations and acronyms
447 // Abbreviations and acronyms
449 abbr[title],
448 abbr[title],
450 abbr[data-original-title] {
449 abbr[data-original-title] {
451 cursor: help;
450 cursor: help;
452 border-bottom: @border-thickness dotted @grey4;
451 border-bottom: @border-thickness dotted @grey4;
453 }
452 }
454 .initialism {
453 .initialism {
455 font-size: 90%;
454 font-size: 90%;
456 text-transform: uppercase;
455 text-transform: uppercase;
457 }
456 }
458
457
459 // Blockquotes
458 // Blockquotes
460 blockquote {
459 blockquote {
461 padding: 1em 2em;
460 padding: 1em 2em;
462 margin: 0 0 2em;
461 margin: 0 0 2em;
463 font-size: @basefontsize;
462 font-size: @basefontsize;
464 border-left: 2px solid @grey6;
463 border-left: 2px solid @grey6;
465
464
466 p,
465 p,
467 ul,
466 ul,
468 ol {
467 ol {
469 &:last-child {
468 &:last-child {
470 margin-bottom: 0;
469 margin-bottom: 0;
471 }
470 }
472 }
471 }
473
472
474 footer,
473 footer,
475 small,
474 small,
476 .small {
475 .small {
477 display: block;
476 display: block;
478 font-size: 80%;
477 font-size: 80%;
479
478
480 &:before {
479 &:before {
481 content: '\2014 \00A0'; // em dash, nbsp
480 content: '\2014 \00A0'; // em dash, nbsp
482 }
481 }
483 }
482 }
484 }
483 }
485
484
486 // Opposite alignment of blockquote
485 // Opposite alignment of blockquote
487 //
486 //
488 .blockquote-reverse,
487 .blockquote-reverse,
489 blockquote.pull-right {
488 blockquote.pull-right {
490 padding-right: 15px;
489 padding-right: 15px;
491 padding-left: 0;
490 padding-left: 0;
492 border-right: 5px solid @grey6;
491 border-right: 5px solid @grey6;
493 border-left: 0;
492 border-left: 0;
494 text-align: right;
493 text-align: right;
495
494
496 // Account for citation
495 // Account for citation
497 footer,
496 footer,
498 small,
497 small,
499 .small {
498 .small {
500 &:before { content: ''; }
499 &:before { content: ''; }
501 &:after {
500 &:after {
502 content: '\00A0 \2014'; // nbsp, em dash
501 content: '\00A0 \2014'; // nbsp, em dash
503 }
502 }
504 }
503 }
505 }
504 }
506
505
507 // Addresses
506 // Addresses
508 address {
507 address {
509 margin-bottom: 2em;
508 margin-bottom: 2em;
510 font-style: normal;
509 font-style: normal;
511 line-height: 1.8em;
510 line-height: 1.8em;
512 }
511 }
513
512
514 .error-message {
513 .error-message {
515 display: block;
514 display: block;
516 margin: @padding/3 0;
515 margin: @padding/3 0;
517 color: @alert2;
516 color: @alert2;
518 }
517 }
519
518
520 .issue-tracker-link {
519 .issue-tracker-link {
521 color: @rcblue;
520 color: @rcblue;
522 }
521 }
523
522
524 .info_text{
523 .info_text{
525 font-size: @basefontsize;
524 font-size: @basefontsize;
526 color: @grey4;
525 color: @grey4;
527 font-family: @text-regular;
526 font-family: @text-regular;
528 }
527 }
529
528
530 // help block text
529 // help block text
531 .help-block {
530 .help-block {
532 display: block;
531 display: block;
533 margin: 0 0 @padding;
532 margin: 0 0 @padding;
534 color: @grey4;
533 color: @grey4;
535 font-family: @text-light;
534 font-family: @text-light;
536 &.pre-formatting {
535 &.pre-formatting {
537 white-space: pre-wrap;
536 white-space: pre-wrap;
538 }
537 }
539 }
538 }
540
539
541 .error-message {
540 .error-message {
542 display: block;
541 display: block;
543 margin: @padding/3 0;
542 margin: @padding/3 0;
544 color: @alert2;
543 color: @alert2;
545 }
544 }
@@ -1,13 +1,18 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('RhodeCode Full Text Search')}</h3>
3 <h3 class="panel-title">${_('RhodeCode Full Text Search')}</h3>
4 </div>
4 </div>
5 <div class="panel-body">
5 <div class="panel-body">
6 <dl class="dl-horizontal">
6 <dl class="dl-horizontal">
7 % for stat in c.statistics:
7 % for stat in c.statistics:
8 <dt>${stat['key']}</dt>
8 % if stat.get('sep'):
9 <dd>${stat['value']}</dd>
9 <dt></dt>
10 <dd>--</dd>
11 % else:
12 <dt>${stat['key']}</dt>
13 <dd>${stat['value']}</dd>
14 % endif
10 % endfor
15 % endfor
11 </dl>
16 </dl>
12 </div>
17 </div>
13 </div>
18 </div>
@@ -1,149 +1,158 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html>
2 <!DOCTYPE html>
3
3
4 <%
4 <%
5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
6 go_import_header = ''
6 go_import_header = ''
7 if hasattr(c, 'rhodecode_db_repo'):
7 if hasattr(c, 'rhodecode_db_repo'):
8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
10 ## check repo context
11 c.template_context['repo_view_type'] = h.get_repo_view_type(request)
10
12
11 if getattr(c, 'repo_group', None):
13 if getattr(c, 'repo_group', None):
12 c.template_context['repo_group_id'] = c.repo_group.group_id
14 c.template_context['repo_group_id'] = c.repo_group.group_id
15 c.template_context['repo_group_name'] = c.repo_group.group_name
13
16
14 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
17 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
15 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
18 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
16 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
19 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
17 c.template_context['rhodecode_user']['notification_status'] = c.rhodecode_user.get_instance().user_data.get('notification_status', True)
20 c.template_context['rhodecode_user']['notification_status'] = c.rhodecode_user.get_instance().user_data.get('notification_status', True)
18 c.template_context['rhodecode_user']['first_name'] = c.rhodecode_user.first_name
21 c.template_context['rhodecode_user']['first_name'] = c.rhodecode_user.first_name
19 c.template_context['rhodecode_user']['last_name'] = c.rhodecode_user.last_name
22 c.template_context['rhodecode_user']['last_name'] = c.rhodecode_user.last_name
20
23
21 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
24 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
22 c.template_context['default_user'] = {
25 c.template_context['default_user'] = {
23 'username': h.DEFAULT_USER,
26 'username': h.DEFAULT_USER,
24 'user_id': 1
27 'user_id': 1
25 }
28 }
29 c.template_context['search_context'] = {
30 'repo_group_id': c.template_context.get('repo_group_id'),
31 'repo_group_name': c.template_context.get('repo_group_name'),
32 'repo_name': c.template_context.get('repo_name'),
33 'repo_view_type': c.template_context.get('repo_view_type'),
34 }
26
35
27 %>
36 %>
28 <html xmlns="http://www.w3.org/1999/xhtml">
37 <html xmlns="http://www.w3.org/1999/xhtml">
29 <head>
38 <head>
30 <title>${self.title()}</title>
39 <title>${self.title()}</title>
31 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
40 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
32
41
33 ${h.go_import_header(request, getattr(c, 'rhodecode_db_repo', None))}
42 ${h.go_import_header(request, getattr(c, 'rhodecode_db_repo', None))}
34
43
35 % if 'safari' in (request.user_agent or '').lower():
44 % if 'safari' in (request.user_agent or '').lower():
36 <meta name="referrer" content="origin">
45 <meta name="referrer" content="origin">
37 % else:
46 % else:
38 <meta name="referrer" content="origin-when-cross-origin">
47 <meta name="referrer" content="origin-when-cross-origin">
39 % endif
48 % endif
40
49
41 <%def name="robots()">
50 <%def name="robots()">
42 <meta name="robots" content="index, nofollow"/>
51 <meta name="robots" content="index, nofollow"/>
43 </%def>
52 </%def>
44 ${self.robots()}
53 ${self.robots()}
45 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
54 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
46 <script src="${h.asset('js/vendors/webcomponentsjs/custom-elements-es5-adapter.js', ver=c.rhodecode_version_hash)}"></script>
55 <script src="${h.asset('js/vendors/webcomponentsjs/custom-elements-es5-adapter.js', ver=c.rhodecode_version_hash)}"></script>
47 <script src="${h.asset('js/vendors/webcomponentsjs/webcomponents-bundle.js', ver=c.rhodecode_version_hash)}"></script>
56 <script src="${h.asset('js/vendors/webcomponentsjs/webcomponents-bundle.js', ver=c.rhodecode_version_hash)}"></script>
48
57
49 ## CSS definitions
58 ## CSS definitions
50 <%def name="css()">
59 <%def name="css()">
51 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
60 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
52 ## EXTRA FOR CSS
61 ## EXTRA FOR CSS
53 ${self.css_extra()}
62 ${self.css_extra()}
54 </%def>
63 </%def>
55 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
64 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
56 <%def name="css_extra()">
65 <%def name="css_extra()">
57 </%def>
66 </%def>
58
67
59 ${self.css()}
68 ${self.css()}
60
69
61 ## JAVASCRIPT
70 ## JAVASCRIPT
62 <%def name="js()">
71 <%def name="js()">
63
72
64 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
73 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
65 <script type="text/javascript">
74 <script type="text/javascript">
66 // register templateContext to pass template variables to JS
75 // register templateContext to pass template variables to JS
67 var templateContext = ${h.json.dumps(c.template_context)|n};
76 var templateContext = ${h.json.dumps(c.template_context)|n};
68
77
69 var APPLICATION_URL = "${h.route_path('home').rstrip('/')}";
78 var APPLICATION_URL = "${h.route_path('home').rstrip('/')}";
70 var APPLICATION_PLUGINS = [];
79 var APPLICATION_PLUGINS = [];
71 var ASSET_URL = "${h.asset('')}";
80 var ASSET_URL = "${h.asset('')}";
72 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
81 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
73 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
82 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
74
83
75 var APPENLIGHT = {
84 var APPENLIGHT = {
76 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
85 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
77 key: '${getattr(c, "appenlight_api_public_key", "")}',
86 key: '${getattr(c, "appenlight_api_public_key", "")}',
78 % if getattr(c, 'appenlight_server_url', None):
87 % if getattr(c, 'appenlight_server_url', None):
79 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
88 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
80 % endif
89 % endif
81 requestInfo: {
90 requestInfo: {
82 % if getattr(c, 'rhodecode_user', None):
91 % if getattr(c, 'rhodecode_user', None):
83 ip: '${c.rhodecode_user.ip_addr}',
92 ip: '${c.rhodecode_user.ip_addr}',
84 username: '${c.rhodecode_user.username}'
93 username: '${c.rhodecode_user.username}'
85 % endif
94 % endif
86 },
95 },
87 tags: {
96 tags: {
88 rhodecode_version: '${c.rhodecode_version}',
97 rhodecode_version: '${c.rhodecode_version}',
89 rhodecode_edition: '${c.rhodecode_edition}'
98 rhodecode_edition: '${c.rhodecode_edition}'
90 }
99 }
91 };
100 };
92
101
93 </script>
102 </script>
94 <%include file="/base/plugins_base.mako"/>
103 <%include file="/base/plugins_base.mako"/>
95 <!--[if lt IE 9]>
104 <!--[if lt IE 9]>
96 <script language="javascript" type="text/javascript" src="${h.asset('js/src/excanvas.min.js')}"></script>
105 <script language="javascript" type="text/javascript" src="${h.asset('js/src/excanvas.min.js')}"></script>
97 <![endif]-->
106 <![endif]-->
98 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
107 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
99 <script> var alertMessagePayloads = ${h.flash.json_alerts(request=request)|n}; </script>
108 <script> var alertMessagePayloads = ${h.flash.json_alerts(request=request)|n}; </script>
100 ## avoide escaping the %N
109 ## avoide escaping the %N
101 <script language="javascript" type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
110 <script language="javascript" type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
102 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js?ver='+c.rhodecode_version_hash}";</script>
111 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js?ver='+c.rhodecode_version_hash}";</script>
103
112
104
113
105 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
114 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
106 ${self.js_extra()}
115 ${self.js_extra()}
107
116
108 <script type="text/javascript">
117 <script type="text/javascript">
109 Rhodecode = (function() {
118 Rhodecode = (function() {
110 function _Rhodecode() {
119 function _Rhodecode() {
111 this.comments = new CommentsController();
120 this.comments = new CommentsController();
112 }
121 }
113 return new _Rhodecode();
122 return new _Rhodecode();
114 })();
123 })();
115
124
116 $(document).ready(function(){
125 $(document).ready(function(){
117 show_more_event();
126 show_more_event();
118 timeagoActivate();
127 timeagoActivate();
119 clipboardActivate();
128 clipboardActivate();
120 })
129 })
121 </script>
130 </script>
122
131
123 </%def>
132 </%def>
124
133
125 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
134 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
126 <%def name="js_extra()"></%def>
135 <%def name="js_extra()"></%def>
127 ${self.js()}
136 ${self.js()}
128
137
129 <%def name="head_extra()"></%def>
138 <%def name="head_extra()"></%def>
130 ${self.head_extra()}
139 ${self.head_extra()}
131 ## extra stuff
140 ## extra stuff
132 %if c.pre_code:
141 %if c.pre_code:
133 ${c.pre_code|n}
142 ${c.pre_code|n}
134 %endif
143 %endif
135 </head>
144 </head>
136 <body id="body">
145 <body id="body">
137 <noscript>
146 <noscript>
138 <div class="noscript-error">
147 <div class="noscript-error">
139 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
148 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
140 </div>
149 </div>
141 </noscript>
150 </noscript>
142
151
143 ${next.body()}
152 ${next.body()}
144 %if c.post_code:
153 %if c.post_code:
145 ${c.post_code|n}
154 ${c.post_code|n}
146 %endif
155 %endif
147 <rhodecode-app></rhodecode-app>
156 <rhodecode-app></rhodecode-app>
148 </body>
157 </body>
149 </html>
158 </html>
@@ -1,107 +1,152 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 %if c.repo_name:
5 %if c.repo_name:
6 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
6 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
7 %else:
7 %else:
8 ${_('Search inside all accessible repositories')}
8 ${_('Search inside all accessible repositories')}
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.repo_name:
16 %if c.repo_name:
17 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
17 ${_('Search inside repository %(repo_name)s') % {'repo_name': c.repo_name}}
18 %else:
18 %else:
19 ${_('Search inside all accessible repositories')}
19 ${_('Search inside all accessible repositories')}
20 %endif
20 %endif
21 %if c.cur_query:
21
22 &raquo;
23 ${c.cur_query}
24 %endif
25 </%def>
22 </%def>
26
23
27 <%def name="menu_bar_nav()">
24 <%def name="menu_bar_nav()">
28 %if c.repo_name:
25 %if c.repo_name:
29 ${self.menu_items(active='repositories')}
26 ${self.menu_items(active='repositories')}
30 %else:
27 %else:
31 ${self.menu_items(active='search')}
28 ${self.menu_items(active='search')}
32 %endif
29 %endif
33 </%def>
30 </%def>
34
31
35 <%def name="menu_bar_subnav()">
32 <%def name="menu_bar_subnav()">
36 %if c.repo_name:
33 %if c.repo_name:
37 ${self.repo_menu(active='options')}
34 ${self.repo_menu(active='options')}
38 %endif
35 %endif
39 </%def>
36 </%def>
40
37
41 <%def name="main()">
38 <%def name="main()">
42 <div class="box">
39 <div class="box">
43 %if c.repo_name:
40 %if c.repo_name:
44 <!-- box / title -->
41 <!-- box / title -->
45 <div class="title">
42 <div class="title">
46 ${self.repo_page_title(c.rhodecode_db_repo)}
43 ${self.repo_page_title(c.rhodecode_db_repo)}
47 </div>
44 </div>
48 ${h.form(h.route_path('search_repo',repo_name=c.repo_name),method='get')}
45 ${h.form(h.route_path('search_repo',repo_name=c.repo_name),method='get')}
49 %else:
46 %else:
50 <!-- box / title -->
47 <!-- box / title -->
51 <div class="title">
48 <div class="title">
52 ${self.breadcrumbs()}
49 ${self.breadcrumbs()}
53 <ul class="links">&nbsp;</ul>
50 <ul class="links">&nbsp;</ul>
54 </div>
51 </div>
55 <!-- end box / title -->
52 <!-- end box / title -->
56 ${h.form(h.route_path('search'), method='get')}
53 ${h.form(h.route_path('search'), method='get')}
57 %endif
54 %endif
58 <div class="form search-form">
55 <div class="form search-form">
59 <div class="fields">
56 <div class="fields">
60 ${h.text('q', c.cur_query, placeholder="Enter query...")}
57 ${h.text('q', c.cur_query, placeholder="Enter query...")}
61
58
62 ${h.select('type',c.search_type,[('content',_('File contents')), ('commit',_('Commit messages')), ('path',_('File names')),],id='id_search_type')}
59 ${h.select('type',c.search_type,[('content',_('Files')), ('path',_('File path')),('commit',_('Commits'))],id='id_search_type')}
60 ${h.hidden('max_lines', '10')}
63 <input type="submit" value="${_('Search')}" class="btn"/>
61 <input type="submit" value="${_('Search')}" class="btn"/>
64 <br/>
62 <br/>
65
63
66 <div class="search-feedback-items">
64 <div class="search-feedback-items">
67 % for error in c.errors:
65 % for error in c.errors:
68 <span class="error-message">
66 <span class="error-message">
69 % for k,v in error.asdict().items():
67 % for k,v in error.asdict().items():
70 ${k} - ${v}
68 ${k} - ${v}
71 % endfor
69 % endfor
72 </span>
70 </span>
73 % endfor
71 % endfor
74 <div class="field">
72 <div class="field">
75 <p class="filterexample" style="position: inherit" onclick="$('#search-help').toggle()">${_('Example Queries')}</p>
73 <p class="filterexample" style="position: inherit" onclick="$('#search-help').toggle()">${_('Query Langague examples')}</p>
76 <pre id="search-help" style="display: none">${h.tooltip(h.search_filter_help(c.searcher, request))}</pre>
74 <pre id="search-help" style="display: none">\
75
76 % if c.searcher.name == 'whoosh':
77 Example filter terms for `Whoosh` search:
78 query lang: <a href="${c.searcher.query_lang_doc}">Whoosh Query Language</a>
79 Whoosh has limited query capabilities. For advanced search use ElasticSearch 6 from RhodeCode EE edition.
80
81 Generate wildcards using '*' character:
82 "repo_name:vcs*" - search everything starting with 'vcs'
83 "repo_name:*vcs*" - search for repository containing 'vcs'
84
85 Optional AND / OR operators in queries
86 "repo_name:vcs OR repo_name:test"
87 "owner:test AND repo_name:test*" AND extension:py
88
89 Move advanced search is available via ElasticSearch6 backend in EE edition.
90 % elif c.searcher.name == 'elasticsearch' and c.searcher.es_version == '2':
91 Example filter terms for `ElasticSearch-${c.searcher.es_version}`search:
92 ElasticSearch-2 has limited query capabilities. For advanced search use ElasticSearch 6 from RhodeCode EE edition.
93
94 search type: content (File Content)
95 indexed fields: content
96
97 # search for `fix` string in all files
98 fix
99
100 search type: commit (Commit message)
101 indexed fields: message
102
103 search type: path (File name)
104 indexed fields: path
105
106 % else:
107 Example filter terms for `ElasticSearch-${c.searcher.es_version}`search:
108 query lang: <a href="${c.searcher.query_lang_doc}">ES 6 Query Language</a>
109 The reserved characters needed espace by `\`: + - = && || > < ! ( ) { } [ ] ^ " ~ * ? : \ /
110 % for handler in c.searcher.get_handlers().values():
111
112 search type: ${handler.search_type_label}
113 *indexed fields*: ${', '.join( [('\n ' if x[0]%4==0 else '')+x[1] for x in enumerate(handler.es_6_field_names)])}
114 % for entry in handler.es_6_example_queries:
115 ${entry.rstrip()}
116 % endfor
117 % endfor
118
119 % endif
120 </pre>
77 </div>
121 </div>
78
122
79 <div class="field">${c.runtime}</div>
123 <div class="field">${c.runtime}</div>
80 </div>
124 </div>
81 </div>
125 </div>
82 </div>
126 </div>
83
127
84 ${h.end_form()}
128 ${h.end_form()}
85 <div class="search">
129 <div class="search">
86 % if c.search_type == 'content':
130 % if c.search_type == 'content':
87 <%include file='search_content.mako'/>
131 <%include file='search_content.mako'/>
88 % elif c.search_type == 'path':
132 % elif c.search_type == 'path':
89 <%include file='search_path.mako'/>
133 <%include file='search_path.mako'/>
90 % elif c.search_type == 'commit':
134 % elif c.search_type == 'commit':
91 <%include file='search_commit.mako'/>
135 <%include file='search_commit.mako'/>
92 % elif c.search_type == 'repository':
136 % elif c.search_type == 'repository':
93 <%include file='search_repository.mako'/>
137 <%include file='search_repository.mako'/>
94 % endif
138 % endif
95 </div>
139 </div>
96 </div>
140 </div>
97 <script>
141 <script>
98 $(document).ready(function(){
142 $(document).ready(function(){
143 $('#q').autoGrowInput();
99 $("#id_search_type").select2({
144 $("#id_search_type").select2({
100 'containerCssClass': "drop-menu",
145 'containerCssClass': "drop-menu",
101 'dropdownCssClass': "drop-menu-dropdown",
146 'dropdownCssClass': "drop-menu-dropdown",
102 'dropdownAutoWidth': true,
147 'dropdownAutoWidth': true,
103 'minimumResultsForSearch': -1
148 'minimumResultsForSearch': -1
104 });
149 });
105 })
150 })
106 </script>
151 </script>
107 </%def>
152 </%def>
@@ -1,82 +1,102 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 % if c.formatted_results:
4
3 <table class="rctable search-results">
5 <table class="rctable search-results">
4 <tr>
6 <tr>
5 <th>${_('Repository')}</th>
7 <th>${_('Repository')}</th>
6 <th>${_('Commit')}</th>
8 <th>${_('Commit')}</th>
7 <th></th>
9 <th></th>
8 <th>${_('Commit message')}</th>
10 <th>${_('Commit message')}</th>
9 <th>
11 <th>
10 %if c.sort == 'newfirst':
12 %if c.sort == 'newfirst':
11 <a href="${c.url_generator(sort='oldfirst')}">${_('Age (new first)')}</a>
13 <a href="${c.url_generator(sort='oldfirst')}">${_('Age (new first)')}</a>
12 %else:
14 %else:
13 <a href="${c.url_generator(sort='newfirst')}">${_('Age (old first)')}</a>
15 <a href="${c.url_generator(sort='newfirst')}">${_('Age (old first)')}</a>
14 %endif
16 %endif
15 </th>
17 </th>
16 <th>${_('Author')}</th>
18 <th>${_('Author')}</th>
17 </tr>
19 </tr>
18 %for entry in c.formatted_results:
20 %for entry in c.formatted_results:
19 ## search results are additionally filtered, and this check is just a safe gate
21 ## search results are additionally filtered, and this check is just a safe gate
20 % if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(entry['repository'], 'search results commit check'):
22 % if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(entry['repository'], 'search results commit check'):
21 <tr class="body">
23 <tr class="body">
22 <td class="td-componentname">
24 <td class="td-componentname">
23 %if h.get_repo_type_by_name(entry.get('repository')) == 'hg':
25 %if h.get_repo_type_by_name(entry.get('repository')) == 'hg':
24 <i class="icon-hg"></i>
26 <i class="icon-hg"></i>
25 %elif h.get_repo_type_by_name(entry.get('repository')) == 'git':
27 %elif h.get_repo_type_by_name(entry.get('repository')) == 'git':
26 <i class="icon-git"></i>
28 <i class="icon-git"></i>
27 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
29 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
28 <i class="icon-svn"></i>
30 <i class="icon-svn"></i>
29 %endif
31 %endif
30 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
32 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
31 </td>
33 </td>
32 <td class="td-commit">
34 <td class="td-commit">
33 ${h.link_to(h._shorten_commit_id(entry['commit_id']),
35 ${h.link_to(h._shorten_commit_id(entry['commit_id']),
34 h.route_path('repo_commit',repo_name=entry['repository'],commit_id=entry['commit_id']))}
36 h.route_path('repo_commit',repo_name=entry['repository'],commit_id=entry['commit_id']))}
35 </td>
37 </td>
36 <td class="td-message expand_commit search open" data-commit-id="${h.md5_safe(entry['repository'])+entry['commit_id']}" id="t-${h.md5_safe(entry['repository'])+entry['commit_id']}" title="${_('Expand commit message')}">
38 <td class="td-message expand_commit search open" data-commit-id="${h.md5_safe(entry['repository'])+entry['commit_id']}" id="t-${h.md5_safe(entry['repository'])+entry['commit_id']}" title="${_('Expand commit message')}">
37 <div>
39 <div>
38 <i class="icon-expand-linked"></i>&nbsp;
40 <i class="icon-expand-linked"></i>&nbsp;
39 </div>
41 </div>
40 </td>
42 </td>
41 <td data-commit-id="${h.md5_safe(entry['repository'])+entry['commit_id']}" id="c-${h.md5_safe(entry['repository'])+entry['commit_id']}" class="message td-description open">
43 <td data-commit-id="${h.md5_safe(entry['repository'])+entry['commit_id']}" id="c-${h.md5_safe(entry['repository'])+entry['commit_id']}" class="message td-description open">
42 %if entry.get('message_hl'):
44 %if entry.get('message_hl'):
43 ${h.literal(entry['message_hl'])}
45 ${h.literal(entry['message_hl'])}
44 %else:
46 %else:
45 ${h.urlify_commit_message(entry['message'], entry['repository'])}
47 ${h.urlify_commit_message(entry['message'], entry['repository'])}
46 %endif
48 %endif
47 </td>
49 </td>
48 <td class="td-time">
50 <td class="td-time">
49 ${h.age_component(h.time_to_utcdatetime(entry['date']))}
51 ${h.age_component(h.time_to_utcdatetime(entry['date']))}
50 </td>
52 </td>
51
53
52 <td class="td-user author">
54 <td class="td-user author">
53 ${base.gravatar_with_user(entry['author'])}
55 <%
56 ## es6 stores this as object
57 author = entry['author']
58 if isinstance(author, dict):
59 author = author['email']
60 %>
61 ${base.gravatar_with_user(author)}
54 </td>
62 </td>
55 </tr>
63 </tr>
56 % endif
64 % endif
57 %endfor
65 %endfor
58 </table>
66 </table>
59
67
60 %if c.cur_query and c.formatted_results:
68 %if c.cur_query:
61 <div class="pagination-wh pagination-left">
69 <div class="pagination-wh pagination-left">
62 ${c.formatted_results.pager('$link_previous ~2~ $link_next')}
70 ${c.formatted_results.pager('$link_previous ~2~ $link_next')}
63 </div>
71 </div>
64 %endif
72 %endif
65
73
66 <script>
74 <script>
67 $('.expand_commit').on('click',function(e){
75 $('.expand_commit').on('click',function(e){
68 var target_expand = $(this);
76 var target_expand = $(this);
69 var cid = target_expand.data('commit-id');
77 var cid = target_expand.data('commit-id');
70
78
71 if (target_expand.hasClass('open')){
79 if (target_expand.hasClass('open')){
72 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'})
80 $('#c-'+cid).css({'height': '1.5em', 'white-space': 'nowrap', 'text-overflow': 'ellipsis', 'overflow':'hidden'})
73 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden'})
81 $('#t-'+cid).css({'height': 'auto', 'line-height': '.9em', 'text-overflow': 'ellipsis', 'overflow':'hidden'})
74 target_expand.removeClass('open');
82 target_expand.removeClass('open');
75 }
83 }
76 else {
84 else {
77 $('#c-'+cid).css({'height': 'auto', 'white-space': 'normal', 'text-overflow': 'initial', 'overflow':'visible'})
85 $('#c-'+cid).css({'height': 'auto', 'white-space': 'normal', 'text-overflow': 'initial', 'overflow':'visible'})
78 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible'})
86 $('#t-'+cid).css({'height': 'auto', 'max-height': 'none', 'text-overflow': 'initial', 'overflow':'visible'})
79 target_expand.addClass('open');
87 target_expand.addClass('open');
80 }
88 }
81 });
89 });
90
91 $(".message.td-description").mark(
92 "${c.searcher.query_to_mark(c.cur_query, 'message')}",
93 {
94 "className": 'match',
95 "accuracy": "complementary",
96 "ignorePunctuation": ":._(){}[]!'+=".split("")
97 }
98 );
99
82 </script>
100 </script>
101
102 % endif
@@ -1,100 +1,151 b''
1 <%def name="highlight_text_file(terms, text, url, line_context=3,
2 max_lines=10,
3 mimetype=None, filepath=None)">
4 <%
5 lines = text.split('\n')
6 lines_of_interest = set()
7 matching_lines = h.get_matching_line_offsets(lines, terms)
8 shown_matching_lines = 0
9
1
10 for line_number in matching_lines:
2 <%def name="highlight_text_file(has_matched_content, file_content, lexer, html_formatter, matching_lines, shown_matching_lines, url, use_hl_filter)">
11 if len(lines_of_interest) < max_lines:
3 % if has_matched_content:
12 lines_of_interest |= set(range(
4 ${h.code_highlight(file_content, lexer, html_formatter, use_hl_filter=use_hl_filter)|n}
13 max(line_number - line_context, 0),
5 % else:
14 min(line_number + line_context, len(lines) + 1)))
6 ${_('No content matched')} <br/>
15 shown_matching_lines += 1
7 % endif
16
17 %>
18 ${h.code_highlight(
19 text,
20 h.get_lexer_safe(
21 mimetype=mimetype,
22 filepath=filepath,
23 ),
24 h.SearchContentCodeHtmlFormatter(
25 linenos=True,
26 cssclass="code-highlight",
27 url=url,
28 query_terms=terms,
29 only_line_numbers=lines_of_interest
30 ))|n}
31
8
32 %if len(matching_lines) > shown_matching_lines:
9 %if len(matching_lines) > shown_matching_lines:
33 <a href="${url}">
10 <a href="${url}">
34 ${len(matching_lines) - shown_matching_lines} ${_('more matches in this file')}
11 ${len(matching_lines) - shown_matching_lines} ${_('more matches in this file')}
35 </a>
12 </a>
36 %endif
13 %endif
37 </%def>
14 </%def>
38
15
39 <div class="search-results">
16 <div class="search-results">
17 <% query_mark = c.searcher.query_to_mark(c.cur_query, 'content') %>
18
40 %for entry in c.formatted_results:
19 %for entry in c.formatted_results:
20
21 <%
22 file_content = entry['content_highlight'] or entry['content']
23 mimetype = entry.get('mimetype')
24 filepath = entry.get('path')
25 max_lines = h.safe_int(request.GET.get('max_lines', '10'))
26 line_context = h.safe_int(request.GET.get('line_contenxt', '3'))
27
28 match_file_url=h.route_path('repo_files',repo_name=entry['repository'], commit_id=entry.get('commit_id', 'tip'),f_path=entry['f_path'], _query={"mark": query_mark})
29 terms = c.cur_query
30
31 if c.searcher.is_es_6:
32 # use empty terms so we default to markers usage
33 total_lines, matching_lines = h.get_matching_line_offsets(file_content, terms=None)
34 else:
35 total_lines, matching_lines = h.get_matching_line_offsets(file_content, terms)
36
37 shown_matching_lines = 0
38 lines_of_interest = set()
39 for line_number in matching_lines:
40 if len(lines_of_interest) < max_lines:
41 lines_of_interest |= set(range(
42 max(line_number - line_context, 0),
43 min(line_number + line_context, total_lines + 1)))
44 shown_matching_lines += 1
45 lexer = h.get_lexer_safe(mimetype=mimetype, filepath=filepath)
46
47 html_formatter = h.SearchContentCodeHtmlFormatter(
48 linenos=True,
49 cssclass="code-highlight",
50 url=match_file_url,
51 query_terms=terms,
52 only_line_numbers=lines_of_interest
53 )
54
55 has_matched_content = len(lines_of_interest) >= 1
56
57 %>
41 ## search results are additionally filtered, and this check is just a safe gate
58 ## search results are additionally filtered, and this check is just a safe gate
42 % if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(entry['repository'], 'search results content check'):
59 % if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(entry['repository'], 'search results content check'):
43 <div id="codeblock" class="codeblock">
60 <div id="codeblock" class="codeblock">
44 <div class="codeblock-header">
61 <div class="codeblock-header">
45 <h2>
62 <h1>
46 %if h.get_repo_type_by_name(entry.get('repository')) == 'hg':
63 %if h.get_repo_type_by_name(entry.get('repository')) == 'hg':
47 <i class="icon-hg"></i>
64 <i class="icon-hg"></i>
48 %elif h.get_repo_type_by_name(entry.get('repository')) == 'git':
65 %elif h.get_repo_type_by_name(entry.get('repository')) == 'git':
49 <i class="icon-git"></i>
66 <i class="icon-git"></i>
50 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
67 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
51 <i class="icon-svn"></i>
68 <i class="icon-svn"></i>
52 %endif
69 %endif
53 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
70 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
54 </h2>
71 </h1>
72
55 <div class="stats">
73 <div class="stats">
56 ${h.link_to(h.literal(entry['f_path']), h.route_path('repo_files',repo_name=entry['repository'],commit_id=entry.get('commit_id', 'tip'),f_path=entry['f_path']))}
74 <span class="stats-filename">
57 %if entry.get('lines'):
75 <strong>
58 | ${entry.get('lines', 0.)} ${_ungettext('line', 'lines', entry.get('lines', 0.))}
76 <i class="icon-file-text"></i>
59 %endif
77 ${h.link_to(h.literal(entry['f_path']), h.route_path('repo_files',repo_name=entry['repository'],commit_id=entry.get('commit_id', 'tip'),f_path=entry['f_path']))}
60 %if entry.get('size'):
78 </strong>
61 | ${h.format_byte_size_binary(entry['size'])}
79 </span>
62 %endif
80 <span class="item last"><i class="tooltip icon-clipboard clipboard-action" data-clipboard-text="${entry['f_path']}" title="${_('Copy the full path')}"></i></span>
63 %if entry.get('mimetype'):
81 <br/>
64 | ${entry.get('mimetype', "unknown mimetype")}
82 <span class="stats-first-item">
65 %endif
83 ${len(matching_lines)} ${_ungettext('search match', 'search matches', len(matching_lines))}
84 </span>
85
86 <span >
87 %if entry.get('lines'):
88 | ${entry.get('lines', 0.)} ${_ungettext('line', 'lines', entry.get('lines', 0.))}
89 %endif
90 </span>
91
92 <span>
93 %if entry.get('size'):
94 | ${h.format_byte_size_binary(entry['size'])}
95 %endif
96 </span>
97
98 <span>
99 %if entry.get('mimetype'):
100 | ${entry.get('mimetype', "unknown mimetype")}
101 %endif
102 </span>
103
66 </div>
104 </div>
67 <div class="buttons">
105 <div class="buttons">
68 <a id="file_history_overview_full" href="${h.route_path('repo_changelog_file',repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path',''))}">
106 <a id="file_history_overview_full" href="${h.route_path('repo_changelog_file',repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path',''))}">
69 ${_('Show Full History')}
107 ${_('Show Full History')}
70 </a>
108 </a>
71 | ${h.link_to(_('Annotation'), h.route_path('repo_files:annotated', repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
109 | ${h.link_to(_('Annotation'), h.route_path('repo_files:annotated', repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
72 | ${h.link_to(_('Raw'), h.route_path('repo_file_raw', repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
110 | ${h.link_to(_('Raw'), h.route_path('repo_file_raw', repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
73 | ${h.link_to(_('Download'), h.route_path('repo_file_download',repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
111 | ${h.link_to(_('Download'), h.route_path('repo_file_download',repo_name=entry.get('repository',''),commit_id=entry.get('commit_id', 'tip'),f_path=entry.get('f_path','')))}
74 </div>
112 </div>
75 </div>
113 </div>
76 <div class="code-body search-code-body">
114 <div class="code-body search-code-body">
77 ${highlight_text_file(c.cur_query, entry['content'],
115
78 url=h.route_path('repo_files',repo_name=entry['repository'],commit_id=entry.get('commit_id', 'tip'),f_path=entry['f_path']),
116 ${highlight_text_file(
79 mimetype=entry.get('mimetype'), filepath=entry.get('path'))}
117 has_matched_content=has_matched_content,
118 file_content=file_content,
119 lexer=lexer,
120 html_formatter=html_formatter,
121 matching_lines=matching_lines,
122 shown_matching_lines=shown_matching_lines,
123 url=match_file_url,
124 use_hl_filter=c.searcher.is_es_6
125 )}
80 </div>
126 </div>
127
81 </div>
128 </div>
82 % endif
129 % endif
83 %endfor
130 %endfor
84 </div>
131 </div>
85 %if c.cur_query and c.formatted_results:
132 %if c.cur_query and c.formatted_results:
86 <div class="pagination-wh pagination-left" >
133 <div class="pagination-wh pagination-left" >
87 ${c.formatted_results.pager('$link_previous ~2~ $link_next')}
134 ${c.formatted_results.pager('$link_previous ~2~ $link_next')}
88 </div>
135 </div>
89 %endif
136 %endif
90
137
91 %if c.cur_query:
138 %if c.cur_query:
92 <script type="text/javascript">
139 <script type="text/javascript">
93 $(function(){
140 $(function(){
94 $(".code").mark(
141 $(".search-code-body").mark(
95 '${' '.join(h.normalize_text_for_matching(c.cur_query).split())}',
142 "${query_mark}",
96 {"className": 'match',
143 {
97 });
144 "className": 'match',
145 "accuracy": "complementary",
146 "ignorePunctuation": ":._(){}[]!'+=".split("")
147 }
148 );
98 })
149 })
99 </script>
150 </script>
100 %endif No newline at end of file
151 %endif
@@ -1,34 +1,38 b''
1 % if c.formatted_results:
2
1 <table class="rctable search-results">
3 <table class="rctable search-results">
2 <tr>
4 <tr>
3 <th>${_('Repository')}</th>
5 <th>${_('Repository')}</th>
4 <th>${_('File')}</th>
6 <th>${_('File')}</th>
5 ##TODO: add 'Last Change' and 'Author' here
7 ##TODO: add 'Last Change' and 'Author' here
6 </tr>
8 </tr>
7 %for entry in c.formatted_results:
9 %for entry in c.formatted_results:
8 ## search results are additionally filtered, and this check is just a safe gate
10 ## search results are additionally filtered, and this check is just a safe gate
9 % if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(entry['repository'], 'search results path check'):
11 % if h.HasRepoPermissionAny('repository.write','repository.read','repository.admin')(entry['repository'], 'search results path check'):
10 <tr class="body">
12 <tr class="body">
11 <td class="td-componentname">
13 <td class="td-componentname">
12 %if h.get_repo_type_by_name(entry.get('repository')) == 'hg':
14 %if h.get_repo_type_by_name(entry.get('repository')) == 'hg':
13 <i class="icon-hg"></i>
15 <i class="icon-hg"></i>
14 %elif h.get_repo_type_by_name(entry.get('repository')) == 'git':
16 %elif h.get_repo_type_by_name(entry.get('repository')) == 'git':
15 <i class="icon-git"></i>
17 <i class="icon-git"></i>
16 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
18 %elif h.get_repo_type_by_name(entry.get('repository')) == 'svn':
17 <i class="icon-svn"></i>
19 <i class="icon-svn"></i>
18 %endif
20 %endif
19 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
21 ${h.link_to(entry['repository'], h.route_path('repo_summary',repo_name=entry['repository']))}
20 </td>
22 </td>
21 <td class="td-componentname">
23 <td class="td-componentname">
22 ${h.link_to(h.literal(entry['f_path']),
24 ${h.link_to(h.literal(entry['f_path']),
23 h.route_path('repo_files',repo_name=entry['repository'],commit_id='tip',f_path=entry['f_path']))}
25 h.route_path('repo_files',repo_name=entry['repository'],commit_id='tip',f_path=entry['f_path']))}
24 </td>
26 </td>
25 </tr>
27 </tr>
26 % endif
28 % endif
27 %endfor
29 %endfor
28 </table>
30 </table>
29
31
30 %if c.cur_query and c.formatted_results:
32 %if c.cur_query:
31 <div class="pagination-wh pagination-left">
33 <div class="pagination-wh pagination-left">
32 ${c.formatted_results.pager('$link_previous ~2~ $link_next')}
34 ${c.formatted_results.pager('$link_previous ~2~ $link_next')}
33 </div>
35 </div>
34 %endif No newline at end of file
36 %endif
37
38 % endif
@@ -1,251 +1,210 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2018 RhodeCode GmbH
3 # Copyright (C) 2010-2018 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 import copy
21 import copy
22 import mock
22 import mock
23 import pytest
23 import pytest
24
24
25 from rhodecode.lib import helpers
25 from rhodecode.lib import helpers
26 from rhodecode.lib.utils2 import AttributeDict
26 from rhodecode.lib.utils2 import AttributeDict
27 from rhodecode.model.settings import IssueTrackerSettingsModel
27 from rhodecode.model.settings import IssueTrackerSettingsModel
28 from rhodecode.tests import no_newline_id_generator
28 from rhodecode.tests import no_newline_id_generator
29
29
30
30
31 @pytest.mark.parametrize('url, expected_url', [
31 @pytest.mark.parametrize('url, expected_url', [
32 ('http://rc.rc/test', '<a href="http://rc.rc/test">http://rc.rc/test</a>'),
32 ('http://rc.rc/test', '<a href="http://rc.rc/test">http://rc.rc/test</a>'),
33 ('http://rc.rc/@foo', '<a href="http://rc.rc/@foo">http://rc.rc/@foo</a>'),
33 ('http://rc.rc/@foo', '<a href="http://rc.rc/@foo">http://rc.rc/@foo</a>'),
34 ('http://rc.rc/!foo', '<a href="http://rc.rc/!foo">http://rc.rc/!foo</a>'),
34 ('http://rc.rc/!foo', '<a href="http://rc.rc/!foo">http://rc.rc/!foo</a>'),
35 ('http://rc.rc/&foo', '<a href="http://rc.rc/&foo">http://rc.rc/&foo</a>'),
35 ('http://rc.rc/&foo', '<a href="http://rc.rc/&foo">http://rc.rc/&foo</a>'),
36 ('http://rc.rc/#foo', '<a href="http://rc.rc/#foo">http://rc.rc/#foo</a>'),
36 ('http://rc.rc/#foo', '<a href="http://rc.rc/#foo">http://rc.rc/#foo</a>'),
37 ])
37 ])
38 def test_urlify_text(url, expected_url):
38 def test_urlify_text(url, expected_url):
39 assert helpers.urlify_text(url) == expected_url
39 assert helpers.urlify_text(url) == expected_url
40
40
41
41
42 @pytest.mark.parametrize('repo_name, commit_id, path, expected_result', [
42 @pytest.mark.parametrize('repo_name, commit_id, path, expected_result', [
43 ('rX<X', 'cX<X', 'pX<X/aX<X/bX<X',
43 ('rX<X', 'cX<X', 'pX<X/aX<X/bX<X',
44 '<a class="pjax-link" href="/rX%3CX/files/cX%3CX/">rX&lt;X</a>/'
44 '<a class="pjax-link" href="/rX%3CX/files/cX%3CX/">rX&lt;X</a>/'
45 '<a class="pjax-link" href="/rX%3CX/files/cX%3CX/pX%3CX">pX&lt;X</a>/'
45 '<a class="pjax-link" href="/rX%3CX/files/cX%3CX/pX%3CX">pX&lt;X</a>/'
46 '<a class="pjax-link" href="/rX%3CX/files/cX%3CX/pX%3CX/aX%3CX">aX&lt;X'
46 '<a class="pjax-link" href="/rX%3CX/files/cX%3CX/pX%3CX/aX%3CX">aX&lt;X'
47 '</a>/bX&lt;X'),
47 '</a>/bX&lt;X'),
48 # Path with only one segment
48 # Path with only one segment
49 ('rX<X', 'cX<X', 'pX<X',
49 ('rX<X', 'cX<X', 'pX<X',
50 '<a class="pjax-link" href="/rX%3CX/files/cX%3CX/">rX&lt;X</a>/pX&lt;X'),
50 '<a class="pjax-link" href="/rX%3CX/files/cX%3CX/">rX&lt;X</a>/pX&lt;X'),
51 # Empty path
51 # Empty path
52 ('rX<X', 'cX<X', '', 'rX&lt;X'),
52 ('rX<X', 'cX<X', '', 'rX&lt;X'),
53 ('rX"X', 'cX"X', 'pX"X/aX"X/bX"X',
53 ('rX"X', 'cX"X', 'pX"X/aX"X/bX"X',
54 '<a class="pjax-link" href="/rX%22X/files/cX%22X/">rX&#34;X</a>/'
54 '<a class="pjax-link" href="/rX%22X/files/cX%22X/">rX&#34;X</a>/'
55 '<a class="pjax-link" href="/rX%22X/files/cX%22X/pX%22X">pX&#34;X</a>/'
55 '<a class="pjax-link" href="/rX%22X/files/cX%22X/pX%22X">pX&#34;X</a>/'
56 '<a class="pjax-link" href="/rX%22X/files/cX%22X/pX%22X/aX%22X">aX&#34;X'
56 '<a class="pjax-link" href="/rX%22X/files/cX%22X/pX%22X/aX%22X">aX&#34;X'
57 '</a>/bX&#34;X'),
57 '</a>/bX&#34;X'),
58 ], ids=['simple', 'one_segment', 'empty_path', 'simple_quote'])
58 ], ids=['simple', 'one_segment', 'empty_path', 'simple_quote'])
59 def test_files_breadcrumbs_xss(
59 def test_files_breadcrumbs_xss(
60 repo_name, commit_id, path, app, expected_result):
60 repo_name, commit_id, path, app, expected_result):
61 result = helpers.files_breadcrumbs(repo_name, commit_id, path)
61 result = helpers.files_breadcrumbs(repo_name, commit_id, path)
62 # Expect it to encode all path fragments properly. This is important
62 # Expect it to encode all path fragments properly. This is important
63 # because it returns an instance of `literal`.
63 # because it returns an instance of `literal`.
64 assert result == expected_result
64 assert result == expected_result
65
65
66
66
67 def test_format_binary():
67 def test_format_binary():
68 assert helpers.format_byte_size_binary(298489462784) == '278.0 GiB'
68 assert helpers.format_byte_size_binary(298489462784) == '278.0 GiB'
69
69
70
70
71 @pytest.mark.parametrize('text_string, pattern, expected', [
71 @pytest.mark.parametrize('text_string, pattern, expected', [
72 ('No issue here', '(?:#)(?P<issue_id>\d+)', []),
72 ('No issue here', '(?:#)(?P<issue_id>\d+)', []),
73 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
73 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
74 [{'url': 'http://r.io/{repo}/i/42', 'id': '42'}]),
74 [{'url': 'http://r.io/{repo}/i/42', 'id': '42'}]),
75 ('Fix #42, #53', '(?:#)(?P<issue_id>\d+)', [
75 ('Fix #42, #53', '(?:#)(?P<issue_id>\d+)', [
76 {'url': 'http://r.io/{repo}/i/42', 'id': '42'},
76 {'url': 'http://r.io/{repo}/i/42', 'id': '42'},
77 {'url': 'http://r.io/{repo}/i/53', 'id': '53'}]),
77 {'url': 'http://r.io/{repo}/i/53', 'id': '53'}]),
78 ('Fix #42', '(?:#)?<issue_id>\d+)', []), # Broken regex
78 ('Fix #42', '(?:#)?<issue_id>\d+)', []), # Broken regex
79 ])
79 ])
80 def test_extract_issues(backend, text_string, pattern, expected):
80 def test_extract_issues(backend, text_string, pattern, expected):
81 repo = backend.create_repo()
81 repo = backend.create_repo()
82 config = {
82 config = {
83 '123': {
83 '123': {
84 'uid': '123',
84 'uid': '123',
85 'pat': pattern,
85 'pat': pattern,
86 'url': 'http://r.io/${repo}/i/${issue_id}',
86 'url': 'http://r.io/${repo}/i/${issue_id}',
87 'pref': '#',
87 'pref': '#',
88 }
88 }
89 }
89 }
90
90
91 def get_settings_mock(self, cache=True):
91 def get_settings_mock(self, cache=True):
92 return config
92 return config
93
93
94 with mock.patch.object(IssueTrackerSettingsModel,
94 with mock.patch.object(IssueTrackerSettingsModel,
95 'get_settings', get_settings_mock):
95 'get_settings', get_settings_mock):
96 text, issues = helpers.process_patterns(text_string, repo.repo_name)
96 text, issues = helpers.process_patterns(text_string, repo.repo_name)
97
97
98 expected = copy.deepcopy(expected)
98 expected = copy.deepcopy(expected)
99 for item in expected:
99 for item in expected:
100 item['url'] = item['url'].format(repo=repo.repo_name)
100 item['url'] = item['url'].format(repo=repo.repo_name)
101
101
102 assert issues == expected
102 assert issues == expected
103
103
104
104
105 @pytest.mark.parametrize('text_string, pattern, link_format, expected_text', [
105 @pytest.mark.parametrize('text_string, pattern, link_format, expected_text', [
106 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'html',
106 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'html',
107 'Fix <a class="issue-tracker-link" href="http://r.io/{repo}/i/42">#42</a>'),
107 'Fix <a class="issue-tracker-link" href="http://r.io/{repo}/i/42">#42</a>'),
108
108
109 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'markdown',
109 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'markdown',
110 'Fix [#42](http://r.io/{repo}/i/42)'),
110 'Fix [#42](http://r.io/{repo}/i/42)'),
111
111
112 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'rst',
112 ('Fix #42', '(?:#)(?P<issue_id>\d+)', 'rst',
113 'Fix `#42 <http://r.io/{repo}/i/42>`_'),
113 'Fix `#42 <http://r.io/{repo}/i/42>`_'),
114
114
115 ('Fix #42', '(?:#)?<issue_id>\d+)', 'html',
115 ('Fix #42', '(?:#)?<issue_id>\d+)', 'html',
116 'Fix #42'), # Broken regex
116 'Fix #42'), # Broken regex
117 ])
117 ])
118 def test_process_patterns_repo(backend, text_string, pattern, expected_text, link_format):
118 def test_process_patterns_repo(backend, text_string, pattern, expected_text, link_format):
119 repo = backend.create_repo()
119 repo = backend.create_repo()
120
120
121 def get_settings_mock(self, cache=True):
121 def get_settings_mock(self, cache=True):
122 return {
122 return {
123 '123': {
123 '123': {
124 'uid': '123',
124 'uid': '123',
125 'pat': pattern,
125 'pat': pattern,
126 'url': 'http://r.io/${repo}/i/${issue_id}',
126 'url': 'http://r.io/${repo}/i/${issue_id}',
127 'pref': '#',
127 'pref': '#',
128 }
128 }
129 }
129 }
130
130
131 with mock.patch.object(IssueTrackerSettingsModel,
131 with mock.patch.object(IssueTrackerSettingsModel,
132 'get_settings', get_settings_mock):
132 'get_settings', get_settings_mock):
133 processed_text, issues = helpers.process_patterns(
133 processed_text, issues = helpers.process_patterns(
134 text_string, repo.repo_name, link_format)
134 text_string, repo.repo_name, link_format)
135
135
136 assert processed_text == expected_text.format(repo=repo.repo_name)
136 assert processed_text == expected_text.format(repo=repo.repo_name)
137
137
138
138
139 @pytest.mark.parametrize('text_string, pattern, expected_text', [
139 @pytest.mark.parametrize('text_string, pattern, expected_text', [
140 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
140 ('Fix #42', '(?:#)(?P<issue_id>\d+)',
141 'Fix <a class="issue-tracker-link" href="http://r.io/i/42">#42</a>'),
141 'Fix <a class="issue-tracker-link" href="http://r.io/i/42">#42</a>'),
142 ('Fix #42', '(?:#)?<issue_id>\d+)',
142 ('Fix #42', '(?:#)?<issue_id>\d+)',
143 'Fix #42'), # Broken regex
143 'Fix #42'), # Broken regex
144 ])
144 ])
145 def test_process_patterns_no_repo(text_string, pattern, expected_text):
145 def test_process_patterns_no_repo(text_string, pattern, expected_text):
146
146
147 def get_settings_mock(self, cache=True):
147 def get_settings_mock(self, cache=True):
148 return {
148 return {
149 '123': {
149 '123': {
150 'uid': '123',
150 'uid': '123',
151 'pat': pattern,
151 'pat': pattern,
152 'url': 'http://r.io/i/${issue_id}',
152 'url': 'http://r.io/i/${issue_id}',
153 'pref': '#',
153 'pref': '#',
154 }
154 }
155 }
155 }
156
156
157 with mock.patch.object(IssueTrackerSettingsModel,
157 with mock.patch.object(IssueTrackerSettingsModel,
158 'get_global_settings', get_settings_mock):
158 'get_global_settings', get_settings_mock):
159 processed_text, issues = helpers.process_patterns(
159 processed_text, issues = helpers.process_patterns(
160 text_string, '')
160 text_string, '')
161
161
162 assert processed_text == expected_text
162 assert processed_text == expected_text
163
163
164
164
165 def test_process_patterns_non_existent_repo_name(backend):
165 def test_process_patterns_non_existent_repo_name(backend):
166 text_string = 'Fix #42'
166 text_string = 'Fix #42'
167 pattern = '(?:#)(?P<issue_id>\d+)'
167 pattern = '(?:#)(?P<issue_id>\d+)'
168 expected_text = ('Fix <a class="issue-tracker-link" '
168 expected_text = ('Fix <a class="issue-tracker-link" '
169 'href="http://r.io/do-not-exist/i/42">#42</a>')
169 'href="http://r.io/do-not-exist/i/42">#42</a>')
170
170
171 def get_settings_mock(self, cache=True):
171 def get_settings_mock(self, cache=True):
172 return {
172 return {
173 '123': {
173 '123': {
174 'uid': '123',
174 'uid': '123',
175 'pat': pattern,
175 'pat': pattern,
176 'url': 'http://r.io/${repo}/i/${issue_id}',
176 'url': 'http://r.io/${repo}/i/${issue_id}',
177 'pref': '#',
177 'pref': '#',
178 }
178 }
179 }
179 }
180
180
181 with mock.patch.object(IssueTrackerSettingsModel,
181 with mock.patch.object(IssueTrackerSettingsModel,
182 'get_global_settings', get_settings_mock):
182 'get_global_settings', get_settings_mock):
183 processed_text, issues = helpers.process_patterns(
183 processed_text, issues = helpers.process_patterns(
184 text_string, 'do-not-exist')
184 text_string, 'do-not-exist')
185
185
186 assert processed_text == expected_text
186 assert processed_text == expected_text
187
187
188
188
189 def test_get_visual_attr(baseapp):
189 def test_get_visual_attr(baseapp):
190 from rhodecode.apps._base import TemplateArgs
190 from rhodecode.apps._base import TemplateArgs
191 c = TemplateArgs()
191 c = TemplateArgs()
192 assert None is helpers.get_visual_attr(c, 'fakse')
192 assert None is helpers.get_visual_attr(c, 'fakse')
193
193
194 # emulate the c.visual behaviour
194 # emulate the c.visual behaviour
195 c.visual = AttributeDict({})
195 c.visual = AttributeDict({})
196 assert None is helpers.get_visual_attr(c, 'some_var')
196 assert None is helpers.get_visual_attr(c, 'some_var')
197
197
198 c.visual.some_var = 'foobar'
198 c.visual.some_var = 'foobar'
199 assert 'foobar' == helpers.get_visual_attr(c, 'some_var')
199 assert 'foobar' == helpers.get_visual_attr(c, 'some_var')
200
200
201
201
202 @pytest.mark.parametrize('test_text, inclusive, expected_text', [
202 @pytest.mark.parametrize('test_text, inclusive, expected_text', [
203 ('just a string', False, 'just a string'),
203 ('just a string', False, 'just a string'),
204 ('just a string\n', False, 'just a string'),
204 ('just a string\n', False, 'just a string'),
205 ('just a string\n next line', False, 'just a string...'),
205 ('just a string\n next line', False, 'just a string...'),
206 ('just a string\n next line', True, 'just a string\n...'),
206 ('just a string\n next line', True, 'just a string\n...'),
207 ], ids=no_newline_id_generator)
207 ], ids=no_newline_id_generator)
208 def test_chop_at(test_text, inclusive, expected_text):
208 def test_chop_at(test_text, inclusive, expected_text):
209 assert helpers.chop_at_smart(
209 assert helpers.chop_at_smart(
210 test_text, '\n', inclusive, '...') == expected_text
210 test_text, '\n', inclusive, '...') == expected_text
211
212
213 @pytest.mark.parametrize('test_text, expected_output', [
214 ('some text', ['some', 'text']),
215 ('some text', ['some', 'text']),
216 ('some text "with a phrase"', ['some', 'text', 'with a phrase']),
217 ('"a phrase" "another phrase"', ['a phrase', 'another phrase']),
218 ('"justphrase"', ['justphrase']),
219 ('""', []),
220 ('', []),
221 (' ', []),
222 ('" "', []),
223 ])
224 def test_extract_phrases(test_text, expected_output):
225 assert helpers.extract_phrases(test_text) == expected_output
226
227
228 @pytest.mark.parametrize('test_text, text_phrases, expected_output', [
229 ('some text here', ['some', 'here'], [(0, 4), (10, 14)]),
230 ('here here there', ['here'], [(0, 4), (5, 9), (11, 15)]),
231 ('irrelevant', ['not found'], []),
232 ('irrelevant', ['not found'], []),
233 ])
234 def test_get_matching_offsets(test_text, text_phrases, expected_output):
235 assert helpers.get_matching_offsets(
236 test_text, text_phrases) == expected_output
237
238
239 def test_normalize_text_for_matching():
240 assert helpers.normalize_text_for_matching(
241 'OJjfe)*#$*@)$JF*)3r2f80h') == 'ojjfe jf 3r2f80h'
242
243
244 def test_get_matching_line_offsets():
245 assert helpers.get_matching_line_offsets([
246 'words words words',
247 'words words words',
248 'some text some',
249 'words words words',
250 'words words words',
251 'text here what'], 'text') == {3: [(5, 9)], 6: [(0, 4)]}
General Comments 0
You need to be logged in to leave comments. Login now