##// END OF EJS Templates
forms: add deform for integration settings forms
dan -
r518:9f9ffd3a default
parent child Browse files
Show More
@@ -0,0 +1,91 b''
1 .deform {
2
3 * {
4 box-sizing: border-box;
5 }
6
7 .required:after {
8 color: #e32;
9 content: '*';
10 display:inline;
11 }
12
13 .control-label {
14 width: 200px;
15 float: left;
16 }
17 .control-inputs {
18 width: 400px;
19 float: left;
20 }
21 .form-group .radio, .form-group .checkbox {
22 position: relative;
23 display: block;
24 /* margin-bottom: 10px; */
25 }
26
27 .form-group {
28 clear: left;
29 }
30
31 .form-control {
32 width: 100%;
33 }
34
35 .error-block {
36 color: red;
37 }
38
39 .deform-seq-container .control-inputs {
40 width: 100%;
41 }
42
43 .deform-seq-container .deform-seq-item-handle {
44 width: 8.3%;
45 float: left;
46 }
47
48 .deform-seq-container .deform-seq-item-group {
49 width: 91.6%;
50 float: left;
51 }
52
53 .form-control {
54 input {
55 height: 40px;
56 }
57 input[type=checkbox], input[type=radio] {
58 height: auto;
59 }
60 select {
61 height: 40px;
62 }
63 }
64
65 .form-control.select2-container { height: 40px; }
66
67 .deform-two-field-sequence .deform-seq-container .deform-seq-item label {
68 display: none;
69 }
70 .deform-two-field-sequence .deform-seq-container .deform-seq-item:first-child label {
71 display: block;
72 }
73 .deform-two-field-sequence .deform-seq-container .deform-seq-item .panel-heading {
74 display: none;
75 }
76 .deform-two-field-sequence .deform-seq-container .deform-seq-item.form-group {
77 background: red;
78 }
79 .deform-two-field-sequence .deform-seq-container .deform-seq-item .deform-seq-item-group .form-group {
80 width: 45%; padding: 0 2px; float: left; clear: none;
81 }
82 .deform-two-field-sequence .deform-seq-container .deform-seq-item .deform-seq-item-group > .panel {
83 padding: 0;
84 margin: 5px 0;
85 border: none;
86 }
87 .deform-two-field-sequence .deform-seq-container .deform-seq-item .deform-seq-item-group > .panel > .panel-body {
88 padding: 0;
89 }
90
91 }
@@ -0,0 +1,20 b''
1 <div class="checkbox">
2 <input tal:define="name name|field.name;
3 true_val true_val|field.widget.true_val;
4 css_class css_class|field.widget.css_class;
5 style style|field.widget.style;
6 oid oid|field.oid"
7 type="checkbox"
8 name="${name}" value="${true_val}"
9 id="${oid}"
10 tal:attributes="checked cstruct == true_val;
11 class css_class;
12 style style;" />
13
14 <label for="${field.oid}">
15 <span tal:condition="hasattr(field, 'schema') and hasattr(field.schema, 'label')"
16 tal:replace="field.schema.label" class="checkbox-label" >
17 </span>
18
19 </label>
20 </div> No newline at end of file
@@ -0,0 +1,25 b''
1 <div tal:define="css_class css_class|field.widget.css_class;
2 style style|field.widget.style;
3 oid oid|field.oid;
4 inline getattr(field.widget, 'inline', False)"
5 tal:omit-tag="not inline">
6 ${field.start_sequence()}
7 <div tal:repeat="choice values | field.widget.values"
8 tal:omit-tag="inline"
9 class="checkbox">
10 <div tal:define="(value, title) choice">
11 <input tal:attributes="checked value in cstruct;
12 class css_class;
13 style style"
14 type="checkbox"
15 name="checkbox"
16 value="${value}"
17 id="${oid}-${repeat.choice.index}"/>
18 <label for="${oid}-${repeat.choice.index}"
19 tal:attributes="class inline and 'checkbox-inline'">
20 ${title}
21 </label>
22 </div>
23 </div>
24 ${field.end_sequence()}
25 </div> No newline at end of file
@@ -0,0 +1,100 b''
1 <form
2 tal:define="style style|field.widget.style;
3 css_class css_class|string:${field.widget.css_class or field.css_class or ''};
4 item_template item_template|field.widget.item_template;
5 autocomplete autocomplete|field.autocomplete;
6 title title|field.title;
7 errormsg errormsg|field.errormsg;
8 description description|field.description;
9 buttons buttons|field.buttons;
10 use_ajax use_ajax|field.use_ajax;
11 ajax_options ajax_options|field.ajax_options;
12 formid formid|field.formid;
13 action action|field.action or None;
14 method method|field.method;"
15 tal:attributes="autocomplete autocomplete;
16 style style;
17 class css_class;
18 action action;"
19 id="${formid}"
20 method="${method}"
21 enctype="multipart/form-data"
22 accept-charset="utf-8"
23 i18n:domain="deform"
24 >
25
26 <fieldset class="deform-form-fieldset">
27
28 <legend tal:condition="title">${title}</legend>
29
30 <input type="hidden" name="${h.csrf_token_key}" value="${h.get_csrf_token()}" />
31 <input type="hidden" name="_charset_" />
32 <input type="hidden" name="__formid__" value="${formid}"/>
33
34 <!--
35 <div class="alert alert-danger" tal:condition="field.error">
36 <div class="error-msg-lbl" i18n:translate=""
37 >There was a problem with your submission</div>
38 <div class="error-msg-detail" i18n:translate=""
39 >Errors have been highlighted below</div>
40 <p class="error-msg">${field.errormsg}</p>
41 </div>
42 -->
43
44 <p class="section first" tal:condition="description">
45 ${description}
46 </p>
47
48 <div tal:repeat="child field"
49 tal:replace="structure child.render_template(item_template)"/>
50
51 <div class="form-group">
52 <tal:loop tal:repeat="button buttons">
53 <button
54 tal:define="btn_disposition repeat.button.start and 'btn-primary' or (button.name == 'delete' and 'btn-danger' or 'btn-default');
55 btn_icon button.icon|None"
56 tal:attributes="disabled button.disabled if button.disabled else None"
57 id="${formid+button.name}"
58 name="${button.name}"
59 type="${button.type}"
60 class="btn ${button.css_class or btn_disposition}"
61 value="${button.value}">
62 <i tal:condition="btn_icon" class="${btn_icon}"> </i>
63 ${button.title}
64 </button>
65 </tal:loop>
66 </div>
67
68 </fieldset>
69
70 <script type="text/javascript" tal:condition="use_ajax">
71 deform.addCallback(
72 '${formid}',
73 function(oid) {
74 var target = '#' + oid;
75 var options = {
76 target: target,
77 replaceTarget: true,
78 success: function() {
79 deform.processCallbacks();
80 deform.focusFirstInput(target);
81 },
82 beforeSerialize: function() {
83 // See http://bit.ly/1agBs9Z (hack to fix tinymce-related ajax bug)
84 if ('tinymce' in window) {
85 $(tinymce.get()).each(
86 function(i, el) {
87 var content = el.getContent();
88 var editor_input = document.getElementById(el.id);
89 editor_input.value = content;
90 });
91 }
92 }
93 };
94 var extra_options = ${ajax_options} || {};
95 $('#' + oid).ajaxForm($.extend(options, extra_options));
96 }
97 );
98 </script>
99
100 </form> No newline at end of file
@@ -0,0 +1,33 b''
1 <tal:def tal:define="title title|field.title;
2 description description|field.description;
3 errormsg errormsg|field.errormsg;
4 item_template item_template|field.widget.item_template"
5 i18n:domain="deform">
6
7 <div class="panel panel-default">
8 <div class="panel-heading">${title}</div>
9 <div class="panel-body">
10
11 <div tal:condition="errormsg"
12 class="clearfix alert alert-danger">
13 <p i18n:translate="">
14 There was a problem with this section
15 </p>
16 <p>${errormsg}</p>
17 </div>
18
19 <div tal:condition="description">
20 ${description}
21 </div>
22
23 ${field.start_mapping()}
24 <div tal:repeat="child field.children"
25 tal:replace="structure child.render_template(item_template)" >
26 </div>
27 ${field.end_mapping()}
28
29 <div style="clear: both"></div>
30 </div>
31 </div>
32
33 </tal:def> No newline at end of file
@@ -0,0 +1,47 b''
1 <div tal:define="error_class error_class|field.widget.error_class;
2 description description|field.description;
3 title title|field.title;
4 oid oid|field.oid;
5 hidden hidden|field.widget.hidden;
6 category category|field.widget.category;
7 structural hidden or category == 'structural';
8 required required|field.required;"
9 class="form-group ${field.error and 'has-error' or ''} ${field.widget.item_css_class or ''}"
10 id="item-${oid}"
11 tal:omit-tag="structural"
12 i18n:domain="deform">
13
14 <label for="${oid}"
15 class="control-label ${required and 'required' or ''}"
16 tal:condition="not structural"
17 id="req-${oid}"
18 >
19 ${title}
20 </label>
21 <div class="control-inputs">
22 <div tal:define="input_prepend field.widget.input_prepend | None;
23 input_append field.widget.input_append | None"
24 tal:omit-tag="not (input_prepend or input_append)"
25 class="input-group">
26 <span class="input-group-addon"
27 tal:condition="input_prepend">${input_prepend}</span
28 ><span tal:replace="structure field.serialize(cstruct).strip()"
29 /><span class="input-group-addon"
30 tal:condition="input_append">${input_append}</span>
31 </div>
32 <p class="help-block error-block"
33 tal:define="errstr 'error-%s' % field.oid"
34 tal:repeat="msg field.error.messages()"
35 i18n:translate=""
36 tal:attributes="id repeat.msg.index==0 and errstr or
37 ('%s-%s' % (errstr, repeat.msg.index))"
38 tal:condition="field.error and not field.widget.hidden and not field.typ.__class__.__name__=='Mapping'">
39 ${msg}
40 </p>
41
42 <p tal:condition="field.description and not field.widget.hidden"
43 class="help-block" >
44 ${field.description}
45 </p>
46 </div>
47 </div> No newline at end of file
@@ -0,0 +1,24 b''
1 <div tal:define="
2 item_tmpl item_template|field.widget.readonly_item_template;
3 oid oid|field.oid;
4 name name|field.name;
5 title title|field.title;"
6 class="deform-seq"
7 id="${oid}">
8
9 <div class="panel panel-default">
10 <div class="panel-heading">${title}</div>
11 <div class="panel-body">
12
13 <div class="deform-seq-container">
14 <div tal:define="subfields [ x[1] for x in subfields ]"
15 tal:repeat="subfield subfields"
16 tal:replace="structure subfield.render_template(item_tmpl,
17 parent=field)" />
18 </div>
19
20 <div style="clear: both"></div>
21 </div>
22
23 </div>
24 </div> No newline at end of file
@@ -0,0 +1,11 b''
1 <div tal:omit-tag="field.widget.hidden"
2 tal:define="
3 hidden hidden|field.widget.hidden;
4 description description|field.description;"
5 title="${description}"
6 class="form-group row deform-seq-item ${field.error and error_class or ''} ${field.widget.item_css_class or ''}"
7 i18n:domain="deform">
8 <div class="deform-seq-item-group">
9 <span tal:replace="structure field.serialize(cstruct, readonly=True)"/>
10 </div>
11 </div>
@@ -0,0 +1,55 b''
1 <div tal:define="
2 name name|field.name;
3 style field.widget.style;
4 oid oid|field.oid;
5 css_class css_class|field.widget.css_class;
6 optgroup_class optgroup_class|field.widget.optgroup_class;
7 multiple multiple|field.widget.multiple;"
8 tal:omit-tag="">
9 <input type="hidden" name="__start__" value="${name}:sequence"
10 tal:condition="multiple" />
11
12 <select tal:attributes="
13 name name;
14 id oid;
15 class string: form-control ${css_class or ''};
16 data-placeholder field.widget.placeholder|None;
17 multiple multiple;
18 style style;">
19 <tal:loop tal:repeat="item values">
20 <optgroup tal:condition="isinstance(item, optgroup_class)"
21 tal:attributes="label item.label">
22 <option tal:repeat="(value, description) item.options"
23 tal:attributes="
24 selected (multiple and value in list(map(unicode, cstruct)) or value == list(map(unicode, cstruct))) and 'selected';
25 class css_class;
26 label field.widget.long_label_generator and description;
27 value value"
28 tal:content="field.widget.long_label_generator and field.widget.long_label_generator(item.label, description) or description"/>
29 </optgroup>
30 <option tal:condition="not isinstance(item, optgroup_class)"
31 tal:attributes="
32 selected (multiple and item[0] in list(map(unicode, cstruct)) or item[0] == unicode(cstruct)) and 'selected';
33 class css_class;
34 value item[0]">${item[1]}</option>
35 </tal:loop>
36 </select>
37
38 <script type="text/javascript">
39 deform.addCallback(
40 '${field.oid}',
41 function(oid) {
42 $('#' + oid).select2({
43 containerCssClass: 'form-control drop-menu',
44 dropdownCssClass: 'drop-menu-dropdown',
45 dropdownAutoWidth: true,
46 placeholder: "${str(field.widget.placeholder).replace('"','\\"')|""}",
47 allowClear: true
48 });
49 }
50 );
51 </script>
52
53 <input type="hidden" name="__end__" value="${name}:sequence"
54 tal:condition="multiple" />
55 </div> No newline at end of file
@@ -0,0 +1,105 b''
1 <div tal:define="item_tmpl item_template|field.widget.item_template;
2 oid oid|field.oid;
3 name name|field.name;
4 min_len min_len|field.widget.min_len;
5 min_len min_len or 0;
6 max_len max_len|field.widget.max_len;
7 max_len max_len or 100000;
8 now_len len(subfields);
9 orderable orderable|field.widget.orderable;
10 orderable orderable and 1 or 0;
11 prototype field.widget.prototype(field);
12 title title|field.title;"
13 class="deform-seq"
14 id="${oid}">
15
16 <style>
17 body.dragging, body.dragging * {
18 cursor: move !important;
19 }
20
21 .dragged {
22 position: absolute;
23 opacity: 0.5;
24 z-index: 2000;
25 }
26 </style>
27
28 <!-- sequence -->
29 <input type="hidden" name="__start__"
30 value="${field.name}:sequence"
31 class="deform-proto"
32 tal:attributes="prototype prototype"/>
33
34 <div class="panel panel-default">
35 <div class="panel-heading">${title}</div>
36 <div class="panel-body">
37
38 <div class="deform-seq-container"
39 id="${oid}-orderable">
40 <div tal:define="subfields [ x[1] for x in subfields ]"
41 tal:repeat="subfield subfields"
42 tal:replace="structure subfield.render_template(item_tmpl,
43 parent=field)" />
44 <span class="deform-insert-before"
45 tal:attributes="
46 min_len min_len;
47 max_len max_len;
48 now_len now_len;
49 orderable orderable;"></span>
50 </div>
51
52 <div style="clear: both"></div>
53 </div>
54
55 <div class="panel-footer">
56 <a href="#"
57 class="btn deform-seq-add"
58 id="${field.oid}-seqAdd"
59 onclick="javascript: return deform.appendSequenceItem(this);">
60 <small id="${field.oid}-addtext">${add_subitem_text}</small>
61 </a>
62
63 <script type="text/javascript">
64 deform.addCallback(
65 '${field.oid}',
66 function(oid) {
67 oid_node = $('#'+ oid);
68 deform.processSequenceButtons(oid_node, ${min_len},
69 ${max_len}, ${now_len},
70 ${orderable});
71 }
72 )
73 <tal:block condition="orderable">
74 $( "#${oid}-orderable" ).sortable({
75 handle: ".deform-order-button, .panel-heading",
76 containerSelector: "#${oid}-orderable",
77 itemSelector: ".deform-seq-item",
78 placeholder: '<span class="glyphicon glyphicon-arrow-right placeholder"></span>',
79 onDragStart: function ($item, container, _super) {
80 var offset = $item.offset(),
81 pointer = container.rootGroup.pointer
82
83 adjustment = {
84 left: pointer.left - offset.left,
85 top: pointer.top - offset.top
86 }
87
88 _super($item, container)
89 },
90 onDrag: function ($item, position) {
91 $item.css({
92 left: position.left - adjustment.left,
93 top: position.top - adjustment.top
94 })
95 }
96 });
97 </tal:block>
98 </script>
99
100 <input type="hidden" name="__end__" value="${field.name}:sequence"/>
101 <!-- /sequence -->
102 </div>
103
104 </div>
105 </div> No newline at end of file
@@ -0,0 +1,35 b''
1 <div tal:omit-tag="field.widget.hidden"
2 tal:define="hidden hidden|field.widget.hidden;
3 error_class error_class|field.widget.error_class;
4 description description|field.description;
5 title title|field.title;
6 oid oid|field.oid"
7 class="form-group row deform-seq-item ${field.error and error_class or ''} ${field.widget.item_css_class or ''}"
8 i18n:domain="deform">
9 <div class="deform-seq-item-group">
10 <span tal:replace="structure field.serialize(cstruct)"/>
11 <tal:errors condition="field.error and not hidden"
12 define="errstr 'error-%s' % oid"
13 repeat="msg field.error.messages()">
14 <p tal:condition="msg"
15 id="${errstr if repeat.msg.index==0 else '%s-%s' % (errstr, repeat.msg.index)}"
16 class="${error_class} help-block"
17 i18n:translate="">${msg}</p>
18 </tal:errors>
19 </div>
20 <div class="deform-seq-item-handle" style="padding:0">
21 <!-- sequence_item -->
22 <span class="deform-order-button close glyphicon glyphicon-resize-vertical"
23 id="${oid}-order"
24 tal:condition="not hidden"
25 title="Reorder (via drag and drop)"
26 i18n:attributes="title"></span>
27 <a class="deform-close-button close"
28 id="${oid}-close"
29 tal:condition="not field.widget.hidden"
30 title="Remove"
31 i18n:attributes="title"
32 onclick="javascript:deform.removeSequenceItem(this);">&times;</a>
33 </div>
34 <!-- /sequence_item -->
35 </div>
@@ -0,0 +1,23 b''
1 <span tal:define="name name|field.name;
2 css_class css_class|field.widget.css_class;
3 oid oid|field.oid;
4 mask mask|field.widget.mask;
5 placeholder placeholder|field.widget.placeholder|field.placeholder|'';
6 mask_placeholder mask_placeholder|field.widget.mask_placeholder;
7 style style|field.widget.style;
8 "
9 tal:omit-tag="">
10 <input type="text" name="${name}" value="${cstruct}"
11 tal:attributes="class string: form-control ${css_class or ''};
12 style style"
13 placeholder="${placeholder}"
14 id="${oid}"/>
15 <script tal:condition="mask" type="text/javascript">
16 deform.addCallback(
17 '${oid}',
18 function (oid) {
19 $("#" + oid).mask("${mask}",
20 {placeholder:"${mask_placeholder}"});
21 });
22 </script>
23 </span> No newline at end of file
@@ -1,1641 +1,1668 b''
1 1 {
2 2 Babel = super.buildPythonPackage {
3 3 name = "Babel-1.3";
4 4 buildInputs = with self; [];
5 5 doCheck = false;
6 6 propagatedBuildInputs = with self; [pytz];
7 7 src = fetchurl {
8 8 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
9 9 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
10 10 };
11 11 meta = {
12 12 license = [ pkgs.lib.licenses.bsdOriginal ];
13 13 };
14 14 };
15 15 Beaker = super.buildPythonPackage {
16 16 name = "Beaker-1.7.0";
17 17 buildInputs = with self; [];
18 18 doCheck = false;
19 19 propagatedBuildInputs = with self; [];
20 20 src = fetchurl {
21 21 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
22 22 md5 = "386be3f7fe427358881eee4622b428b3";
23 23 };
24 24 meta = {
25 25 license = [ pkgs.lib.licenses.bsdOriginal ];
26 26 };
27 27 };
28 28 CProfileV = super.buildPythonPackage {
29 29 name = "CProfileV-1.0.6";
30 30 buildInputs = with self; [];
31 31 doCheck = false;
32 32 propagatedBuildInputs = with self; [bottle];
33 33 src = fetchurl {
34 34 url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz";
35 35 md5 = "08c7c242b6e64237bc53c5d13537e03d";
36 36 };
37 37 meta = {
38 38 license = [ pkgs.lib.licenses.mit ];
39 39 };
40 40 };
41 Chameleon = super.buildPythonPackage {
42 name = "Chameleon-2.24";
43 buildInputs = with self; [];
44 doCheck = false;
45 propagatedBuildInputs = with self; [];
46 src = fetchurl {
47 url = "https://pypi.python.org/packages/5a/9e/637379ffa13c5172b5c0e704833ffea6bf51cec7567f93fd6e903d53ed74/Chameleon-2.24.tar.gz";
48 md5 = "1b01f1f6533a8a11d0d2f2366dec5342";
49 };
50 meta = {
51 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
52 };
53 };
41 54 Fabric = super.buildPythonPackage {
42 55 name = "Fabric-1.10.0";
43 56 buildInputs = with self; [];
44 57 doCheck = false;
45 58 propagatedBuildInputs = with self; [paramiko];
46 59 src = fetchurl {
47 60 url = "https://pypi.python.org/packages/e3/5f/b6ebdb5241d5ec9eab582a5c8a01255c1107da396f849e538801d2fe64a5/Fabric-1.10.0.tar.gz";
48 61 md5 = "2cb96473387f0e7aa035210892352f4a";
49 62 };
50 63 meta = {
51 64 license = [ pkgs.lib.licenses.bsdOriginal ];
52 65 };
53 66 };
54 67 FormEncode = super.buildPythonPackage {
55 68 name = "FormEncode-1.2.4";
56 69 buildInputs = with self; [];
57 70 doCheck = false;
58 71 propagatedBuildInputs = with self; [];
59 72 src = fetchurl {
60 73 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
61 74 md5 = "6bc17fb9aed8aea198975e888e2077f4";
62 75 };
63 76 meta = {
64 77 license = [ pkgs.lib.licenses.psfl ];
65 78 };
66 79 };
67 80 Jinja2 = super.buildPythonPackage {
68 81 name = "Jinja2-2.7.3";
69 82 buildInputs = with self; [];
70 83 doCheck = false;
71 84 propagatedBuildInputs = with self; [MarkupSafe];
72 85 src = fetchurl {
73 86 url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz";
74 87 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
75 88 };
76 89 meta = {
77 90 license = [ pkgs.lib.licenses.bsdOriginal ];
78 91 };
79 92 };
80 93 Mako = super.buildPythonPackage {
81 94 name = "Mako-1.0.1";
82 95 buildInputs = with self; [];
83 96 doCheck = false;
84 97 propagatedBuildInputs = with self; [MarkupSafe];
85 98 src = fetchurl {
86 99 url = "https://pypi.python.org/packages/8e/a4/aa56533ecaa5f22ca92428f74e074d0c9337282933c722391902c8f9e0f8/Mako-1.0.1.tar.gz";
87 100 md5 = "9f0aafd177b039ef67b90ea350497a54";
88 101 };
89 102 meta = {
90 103 license = [ pkgs.lib.licenses.mit ];
91 104 };
92 105 };
93 106 Markdown = super.buildPythonPackage {
94 107 name = "Markdown-2.6.2";
95 108 buildInputs = with self; [];
96 109 doCheck = false;
97 110 propagatedBuildInputs = with self; [];
98 111 src = fetchurl {
99 112 url = "https://pypi.python.org/packages/62/8b/83658b5f6c220d5fcde9f9852d46ea54765d734cfbc5a9f4c05bfc36db4d/Markdown-2.6.2.tar.gz";
100 113 md5 = "256d19afcc564dc4ce4c229bb762f7ae";
101 114 };
102 115 meta = {
103 116 license = [ pkgs.lib.licenses.bsdOriginal ];
104 117 };
105 118 };
106 119 MarkupSafe = super.buildPythonPackage {
107 120 name = "MarkupSafe-0.23";
108 121 buildInputs = with self; [];
109 122 doCheck = false;
110 123 propagatedBuildInputs = with self; [];
111 124 src = fetchurl {
112 125 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
113 126 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
114 127 };
115 128 meta = {
116 129 license = [ pkgs.lib.licenses.bsdOriginal ];
117 130 };
118 131 };
119 132 MySQL-python = super.buildPythonPackage {
120 133 name = "MySQL-python-1.2.5";
121 134 buildInputs = with self; [];
122 135 doCheck = false;
123 136 propagatedBuildInputs = with self; [];
124 137 src = fetchurl {
125 138 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
126 139 md5 = "654f75b302db6ed8dc5a898c625e030c";
127 140 };
128 141 meta = {
129 142 license = [ pkgs.lib.licenses.gpl1 ];
130 143 };
131 144 };
132 145 Paste = super.buildPythonPackage {
133 146 name = "Paste-2.0.2";
134 147 buildInputs = with self; [];
135 148 doCheck = false;
136 149 propagatedBuildInputs = with self; [six];
137 150 src = fetchurl {
138 151 url = "https://pypi.python.org/packages/d5/8d/0f8ac40687b97ff3e07ebd1369be20bdb3f93864d2dc3c2ff542edb4ce50/Paste-2.0.2.tar.gz";
139 152 md5 = "4bfc8a7eaf858f6309d2ac0f40fc951c";
140 153 };
141 154 meta = {
142 155 license = [ pkgs.lib.licenses.mit ];
143 156 };
144 157 };
145 158 PasteDeploy = super.buildPythonPackage {
146 159 name = "PasteDeploy-1.5.2";
147 160 buildInputs = with self; [];
148 161 doCheck = false;
149 162 propagatedBuildInputs = with self; [];
150 163 src = fetchurl {
151 164 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
152 165 md5 = "352b7205c78c8de4987578d19431af3b";
153 166 };
154 167 meta = {
155 168 license = [ pkgs.lib.licenses.mit ];
156 169 };
157 170 };
158 171 PasteScript = super.buildPythonPackage {
159 172 name = "PasteScript-1.7.5";
160 173 buildInputs = with self; [];
161 174 doCheck = false;
162 175 propagatedBuildInputs = with self; [Paste PasteDeploy];
163 176 src = fetchurl {
164 177 url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz";
165 178 md5 = "4c72d78dcb6bb993f30536842c16af4d";
166 179 };
167 180 meta = {
168 181 license = [ pkgs.lib.licenses.mit ];
169 182 };
170 183 };
171 184 Pygments = super.buildPythonPackage {
172 185 name = "Pygments-2.1.3";
173 186 buildInputs = with self; [];
174 187 doCheck = false;
175 188 propagatedBuildInputs = with self; [];
176 189 src = fetchurl {
177 190 url = "https://pypi.python.org/packages/b8/67/ab177979be1c81bc99c8d0592ef22d547e70bb4c6815c383286ed5dec504/Pygments-2.1.3.tar.gz";
178 191 md5 = "ed3fba2467c8afcda4d317e4ef2c6150";
179 192 };
180 193 meta = {
181 194 license = [ pkgs.lib.licenses.bsdOriginal ];
182 195 };
183 196 };
184 197 Pylons = super.buildPythonPackage {
185 198 name = "Pylons-1.0.1";
186 199 buildInputs = with self; [];
187 200 doCheck = false;
188 201 propagatedBuildInputs = with self; [Routes WebHelpers Beaker Paste PasteDeploy PasteScript FormEncode simplejson decorator nose Mako WebError WebTest Tempita MarkupSafe WebOb];
189 202 src = fetchurl {
190 203 url = "https://pypi.python.org/packages/a2/69/b835a6bad00acbfeed3f33c6e44fa3f936efc998c795bfb15c61a79ecf62/Pylons-1.0.1.tar.gz";
191 204 md5 = "6cb880d75fa81213192142b07a6e4915";
192 205 };
193 206 meta = {
194 207 license = [ pkgs.lib.licenses.bsdOriginal ];
195 208 };
196 209 };
197 210 Pyro4 = super.buildPythonPackage {
198 211 name = "Pyro4-4.41";
199 212 buildInputs = with self; [];
200 213 doCheck = false;
201 214 propagatedBuildInputs = with self; [serpent];
202 215 src = fetchurl {
203 216 url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz";
204 217 md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c";
205 218 };
206 219 meta = {
207 220 license = [ pkgs.lib.licenses.mit ];
208 221 };
209 222 };
210 223 Routes = super.buildPythonPackage {
211 224 name = "Routes-1.13";
212 225 buildInputs = with self; [];
213 226 doCheck = false;
214 227 propagatedBuildInputs = with self; [repoze.lru];
215 228 src = fetchurl {
216 229 url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz";
217 230 md5 = "d527b0ab7dd9172b1275a41f97448783";
218 231 };
219 232 meta = {
220 233 license = [ pkgs.lib.licenses.bsdOriginal ];
221 234 };
222 235 };
223 236 SQLAlchemy = super.buildPythonPackage {
224 237 name = "SQLAlchemy-0.9.9";
225 238 buildInputs = with self; [];
226 239 doCheck = false;
227 240 propagatedBuildInputs = with self; [];
228 241 src = fetchurl {
229 242 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
230 243 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
231 244 };
232 245 meta = {
233 246 license = [ pkgs.lib.licenses.mit ];
234 247 };
235 248 };
236 249 Sphinx = super.buildPythonPackage {
237 250 name = "Sphinx-1.2.2";
238 251 buildInputs = with self; [];
239 252 doCheck = false;
240 253 propagatedBuildInputs = with self; [Pygments docutils Jinja2];
241 254 src = fetchurl {
242 255 url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz";
243 256 md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";
244 257 };
245 258 meta = {
246 259 license = [ pkgs.lib.licenses.bsdOriginal ];
247 260 };
248 261 };
249 262 Tempita = super.buildPythonPackage {
250 263 name = "Tempita-0.5.2";
251 264 buildInputs = with self; [];
252 265 doCheck = false;
253 266 propagatedBuildInputs = with self; [];
254 267 src = fetchurl {
255 268 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
256 269 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
257 270 };
258 271 meta = {
259 272 license = [ pkgs.lib.licenses.mit ];
260 273 };
261 274 };
262 275 URLObject = super.buildPythonPackage {
263 276 name = "URLObject-2.4.0";
264 277 buildInputs = with self; [];
265 278 doCheck = false;
266 279 propagatedBuildInputs = with self; [];
267 280 src = fetchurl {
268 281 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
269 282 md5 = "2ed819738a9f0a3051f31dc9924e3065";
270 283 };
271 284 meta = {
272 285 license = [ ];
273 286 };
274 287 };
275 288 WebError = super.buildPythonPackage {
276 289 name = "WebError-0.10.3";
277 290 buildInputs = with self; [];
278 291 doCheck = false;
279 292 propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste];
280 293 src = fetchurl {
281 294 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
282 295 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
283 296 };
284 297 meta = {
285 298 license = [ pkgs.lib.licenses.mit ];
286 299 };
287 300 };
288 301 WebHelpers = super.buildPythonPackage {
289 302 name = "WebHelpers-1.3";
290 303 buildInputs = with self; [];
291 304 doCheck = false;
292 305 propagatedBuildInputs = with self; [MarkupSafe];
293 306 src = fetchurl {
294 307 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
295 308 md5 = "32749ffadfc40fea51075a7def32588b";
296 309 };
297 310 meta = {
298 311 license = [ pkgs.lib.licenses.bsdOriginal ];
299 312 };
300 313 };
301 314 WebHelpers2 = super.buildPythonPackage {
302 315 name = "WebHelpers2-2.0";
303 316 buildInputs = with self; [];
304 317 doCheck = false;
305 318 propagatedBuildInputs = with self; [MarkupSafe six];
306 319 src = fetchurl {
307 320 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
308 321 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
309 322 };
310 323 meta = {
311 324 license = [ pkgs.lib.licenses.mit ];
312 325 };
313 326 };
314 327 WebOb = super.buildPythonPackage {
315 328 name = "WebOb-1.3.1";
316 329 buildInputs = with self; [];
317 330 doCheck = false;
318 331 propagatedBuildInputs = with self; [];
319 332 src = fetchurl {
320 333 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
321 334 md5 = "20918251c5726956ba8fef22d1556177";
322 335 };
323 336 meta = {
324 337 license = [ pkgs.lib.licenses.mit ];
325 338 };
326 339 };
327 340 WebTest = super.buildPythonPackage {
328 341 name = "WebTest-1.4.3";
329 342 buildInputs = with self; [];
330 343 doCheck = false;
331 344 propagatedBuildInputs = with self; [WebOb];
332 345 src = fetchurl {
333 346 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
334 347 md5 = "631ce728bed92c681a4020a36adbc353";
335 348 };
336 349 meta = {
337 350 license = [ pkgs.lib.licenses.mit ];
338 351 };
339 352 };
340 353 Whoosh = super.buildPythonPackage {
341 354 name = "Whoosh-2.7.0";
342 355 buildInputs = with self; [];
343 356 doCheck = false;
344 357 propagatedBuildInputs = with self; [];
345 358 src = fetchurl {
346 359 url = "https://pypi.python.org/packages/1c/dc/2f0231ff3875ded36df8c1ab851451e51a237dc0e5a86d3d96036158da94/Whoosh-2.7.0.zip";
347 360 md5 = "7abfd970f16fadc7311960f3fa0bc7a9";
348 361 };
349 362 meta = {
350 363 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
351 364 };
352 365 };
353 366 alembic = super.buildPythonPackage {
354 367 name = "alembic-0.8.4";
355 368 buildInputs = with self; [];
356 369 doCheck = false;
357 370 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor];
358 371 src = fetchurl {
359 372 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
360 373 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
361 374 };
362 375 meta = {
363 376 license = [ pkgs.lib.licenses.mit ];
364 377 };
365 378 };
366 379 amqplib = super.buildPythonPackage {
367 380 name = "amqplib-1.0.2";
368 381 buildInputs = with self; [];
369 382 doCheck = false;
370 383 propagatedBuildInputs = with self; [];
371 384 src = fetchurl {
372 385 url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz";
373 386 md5 = "5c92f17fbedd99b2b4a836d4352d1e2f";
374 387 };
375 388 meta = {
376 389 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
377 390 };
378 391 };
379 392 anyjson = super.buildPythonPackage {
380 393 name = "anyjson-0.3.3";
381 394 buildInputs = with self; [];
382 395 doCheck = false;
383 396 propagatedBuildInputs = with self; [];
384 397 src = fetchurl {
385 398 url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz";
386 399 md5 = "2ea28d6ec311aeeebaf993cb3008b27c";
387 400 };
388 401 meta = {
389 402 license = [ pkgs.lib.licenses.bsdOriginal ];
390 403 };
391 404 };
392 405 appenlight-client = super.buildPythonPackage {
393 406 name = "appenlight-client-0.6.14";
394 407 buildInputs = with self; [];
395 408 doCheck = false;
396 409 propagatedBuildInputs = with self; [WebOb requests];
397 410 src = fetchurl {
398 411 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
399 412 md5 = "578c69b09f4356d898fff1199b98a95c";
400 413 };
401 414 meta = {
402 415 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "DFSG approved"; } ];
403 416 };
404 417 };
405 418 authomatic = super.buildPythonPackage {
406 419 name = "authomatic-0.1.0.post1";
407 420 buildInputs = with self; [];
408 421 doCheck = false;
409 422 propagatedBuildInputs = with self; [];
410 423 src = fetchurl {
411 424 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
412 425 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
413 426 };
414 427 meta = {
415 428 license = [ pkgs.lib.licenses.mit ];
416 429 };
417 430 };
418 431 backport-ipaddress = super.buildPythonPackage {
419 432 name = "backport-ipaddress-0.1";
420 433 buildInputs = with self; [];
421 434 doCheck = false;
422 435 propagatedBuildInputs = with self; [];
423 436 src = fetchurl {
424 437 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
425 438 md5 = "9c1f45f4361f71b124d7293a60006c05";
426 439 };
427 440 meta = {
428 441 license = [ pkgs.lib.licenses.psfl ];
429 442 };
430 443 };
431 444 bottle = super.buildPythonPackage {
432 445 name = "bottle-0.12.8";
433 446 buildInputs = with self; [];
434 447 doCheck = false;
435 448 propagatedBuildInputs = with self; [];
436 449 src = fetchurl {
437 450 url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz";
438 451 md5 = "13132c0a8f607bf860810a6ee9064c5b";
439 452 };
440 453 meta = {
441 454 license = [ pkgs.lib.licenses.mit ];
442 455 };
443 456 };
444 457 bumpversion = super.buildPythonPackage {
445 458 name = "bumpversion-0.5.3";
446 459 buildInputs = with self; [];
447 460 doCheck = false;
448 461 propagatedBuildInputs = with self; [];
449 462 src = fetchurl {
450 463 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
451 464 md5 = "c66a3492eafcf5ad4b024be9fca29820";
452 465 };
453 466 meta = {
454 467 license = [ pkgs.lib.licenses.mit ];
455 468 };
456 469 };
457 470 celery = super.buildPythonPackage {
458 471 name = "celery-2.2.10";
459 472 buildInputs = with self; [];
460 473 doCheck = false;
461 474 propagatedBuildInputs = with self; [python-dateutil anyjson kombu pyparsing];
462 475 src = fetchurl {
463 476 url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz";
464 477 md5 = "898bc87e54f278055b561316ba73e222";
465 478 };
466 479 meta = {
467 480 license = [ pkgs.lib.licenses.bsdOriginal ];
468 481 };
469 482 };
470 483 click = super.buildPythonPackage {
471 484 name = "click-5.1";
472 485 buildInputs = with self; [];
473 486 doCheck = false;
474 487 propagatedBuildInputs = with self; [];
475 488 src = fetchurl {
476 489 url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz";
477 490 md5 = "9c5323008cccfe232a8b161fc8196d41";
478 491 };
479 492 meta = {
480 493 license = [ pkgs.lib.licenses.bsdOriginal ];
481 494 };
482 495 };
483 496 colander = super.buildPythonPackage {
484 497 name = "colander-1.2";
485 498 buildInputs = with self; [];
486 499 doCheck = false;
487 500 propagatedBuildInputs = with self; [translationstring iso8601];
488 501 src = fetchurl {
489 502 url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz";
490 503 md5 = "83db21b07936a0726e588dae1914b9ed";
491 504 };
492 505 meta = {
493 506 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
494 507 };
495 508 };
496 509 configobj = super.buildPythonPackage {
497 510 name = "configobj-5.0.6";
498 511 buildInputs = with self; [];
499 512 doCheck = false;
500 513 propagatedBuildInputs = with self; [six];
501 514 src = fetchurl {
502 515 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
503 516 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
504 517 };
505 518 meta = {
506 519 license = [ pkgs.lib.licenses.bsdOriginal ];
507 520 };
508 521 };
509 522 cov-core = super.buildPythonPackage {
510 523 name = "cov-core-1.15.0";
511 524 buildInputs = with self; [];
512 525 doCheck = false;
513 526 propagatedBuildInputs = with self; [coverage];
514 527 src = fetchurl {
515 528 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
516 529 md5 = "f519d4cb4c4e52856afb14af52919fe6";
517 530 };
518 531 meta = {
519 532 license = [ pkgs.lib.licenses.mit ];
520 533 };
521 534 };
522 535 coverage = super.buildPythonPackage {
523 536 name = "coverage-3.7.1";
524 537 buildInputs = with self; [];
525 538 doCheck = false;
526 539 propagatedBuildInputs = with self; [];
527 540 src = fetchurl {
528 541 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
529 542 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
530 543 };
531 544 meta = {
532 545 license = [ pkgs.lib.licenses.bsdOriginal ];
533 546 };
534 547 };
535 548 cssselect = super.buildPythonPackage {
536 549 name = "cssselect-0.9.1";
537 550 buildInputs = with self; [];
538 551 doCheck = false;
539 552 propagatedBuildInputs = with self; [];
540 553 src = fetchurl {
541 554 url = "https://pypi.python.org/packages/aa/e5/9ee1460d485b94a6d55732eb7ad5b6c084caf73dd6f9cb0bb7d2a78fafe8/cssselect-0.9.1.tar.gz";
542 555 md5 = "c74f45966277dc7a0f768b9b0f3522ac";
543 556 };
544 557 meta = {
545 558 license = [ pkgs.lib.licenses.bsdOriginal ];
546 559 };
547 560 };
548 561 decorator = super.buildPythonPackage {
549 562 name = "decorator-3.4.2";
550 563 buildInputs = with self; [];
551 564 doCheck = false;
552 565 propagatedBuildInputs = with self; [];
553 566 src = fetchurl {
554 567 url = "https://pypi.python.org/packages/35/3a/42566eb7a2cbac774399871af04e11d7ae3fc2579e7dae85213b8d1d1c57/decorator-3.4.2.tar.gz";
555 568 md5 = "9e0536870d2b83ae27d58dbf22582f4d";
556 569 };
557 570 meta = {
558 571 license = [ pkgs.lib.licenses.bsdOriginal ];
559 572 };
560 573 };
574 deform = super.buildPythonPackage {
575 name = "deform-2.0a3.dev0";
576 buildInputs = with self; [];
577 doCheck = false;
578 propagatedBuildInputs = with self; [Chameleon colander iso8601 peppercorn translationstring zope.deprecation];
579 src = fetchgit {
580 url = "https://github.com/Pylons/deform";
581 rev = "08fb9de077c76951f6e70e28d4bf060a209d3d39";
582 sha256 = "0nmhajc4pfgp4lbwhs5szqfzswpij1qyr69m7qkyhncl2g2d759r";
583 };
584 meta = {
585 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
586 };
587 };
561 588 docutils = super.buildPythonPackage {
562 589 name = "docutils-0.12";
563 590 buildInputs = with self; [];
564 591 doCheck = false;
565 592 propagatedBuildInputs = with self; [];
566 593 src = fetchurl {
567 594 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
568 595 md5 = "4622263b62c5c771c03502afa3157768";
569 596 };
570 597 meta = {
571 598 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 ];
572 599 };
573 600 };
574 601 dogpile.cache = super.buildPythonPackage {
575 602 name = "dogpile.cache-0.6.1";
576 603 buildInputs = with self; [];
577 604 doCheck = false;
578 605 propagatedBuildInputs = with self; [dogpile.core];
579 606 src = fetchurl {
580 607 url = "https://pypi.python.org/packages/f6/a0/6f2142c58c6588d17c734265b103ae1cd0741e1681dd9483a63f22033375/dogpile.cache-0.6.1.tar.gz";
581 608 md5 = "35d7fb30f22bbd0685763d894dd079a9";
582 609 };
583 610 meta = {
584 611 license = [ pkgs.lib.licenses.bsdOriginal ];
585 612 };
586 613 };
587 614 dogpile.core = super.buildPythonPackage {
588 615 name = "dogpile.core-0.4.1";
589 616 buildInputs = with self; [];
590 617 doCheck = false;
591 618 propagatedBuildInputs = with self; [];
592 619 src = fetchurl {
593 620 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
594 621 md5 = "01cb19f52bba3e95c9b560f39341f045";
595 622 };
596 623 meta = {
597 624 license = [ pkgs.lib.licenses.bsdOriginal ];
598 625 };
599 626 };
600 627 dulwich = super.buildPythonPackage {
601 628 name = "dulwich-0.12.0";
602 629 buildInputs = with self; [];
603 630 doCheck = false;
604 631 propagatedBuildInputs = with self; [];
605 632 src = fetchurl {
606 633 url = "https://pypi.python.org/packages/6f/04/fbe561b6d45c0ec758330d5b7f5ba4b6cb4f1ca1ab49859d2fc16320da75/dulwich-0.12.0.tar.gz";
607 634 md5 = "f3a8a12bd9f9dd8c233e18f3d49436fa";
608 635 };
609 636 meta = {
610 637 license = [ pkgs.lib.licenses.gpl2Plus ];
611 638 };
612 639 };
613 640 ecdsa = super.buildPythonPackage {
614 641 name = "ecdsa-0.11";
615 642 buildInputs = with self; [];
616 643 doCheck = false;
617 644 propagatedBuildInputs = with self; [];
618 645 src = fetchurl {
619 646 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
620 647 md5 = "8ef586fe4dbb156697d756900cb41d7c";
621 648 };
622 649 meta = {
623 650 license = [ pkgs.lib.licenses.mit ];
624 651 };
625 652 };
626 653 elasticsearch = super.buildPythonPackage {
627 654 name = "elasticsearch-2.3.0";
628 655 buildInputs = with self; [];
629 656 doCheck = false;
630 657 propagatedBuildInputs = with self; [urllib3];
631 658 src = fetchurl {
632 659 url = "https://pypi.python.org/packages/10/35/5fd52c5f0b0ee405ed4b5195e8bce44c5e041787680dc7b94b8071cac600/elasticsearch-2.3.0.tar.gz";
633 660 md5 = "2550f3b51629cf1ef9636608af92c340";
634 661 };
635 662 meta = {
636 663 license = [ pkgs.lib.licenses.asl20 ];
637 664 };
638 665 };
639 666 elasticsearch-dsl = super.buildPythonPackage {
640 667 name = "elasticsearch-dsl-2.0.0";
641 668 buildInputs = with self; [];
642 669 doCheck = false;
643 670 propagatedBuildInputs = with self; [six python-dateutil elasticsearch];
644 671 src = fetchurl {
645 672 url = "https://pypi.python.org/packages/4e/5d/e788ae8dbe2ff4d13426db0a027533386a5c276c77a2654dc0e2007ce04a/elasticsearch-dsl-2.0.0.tar.gz";
646 673 md5 = "4cdfec81bb35383dd3b7d02d7dc5ee68";
647 674 };
648 675 meta = {
649 676 license = [ pkgs.lib.licenses.asl20 ];
650 677 };
651 678 };
652 679 flake8 = super.buildPythonPackage {
653 680 name = "flake8-2.4.1";
654 681 buildInputs = with self; [];
655 682 doCheck = false;
656 683 propagatedBuildInputs = with self; [pyflakes pep8 mccabe];
657 684 src = fetchurl {
658 685 url = "https://pypi.python.org/packages/8f/b5/9a73c66c7dba273bac8758398f060c008a25f3e84531063b42503b5d0a95/flake8-2.4.1.tar.gz";
659 686 md5 = "ed45d3db81a3b7c88bd63c6e37ca1d65";
660 687 };
661 688 meta = {
662 689 license = [ pkgs.lib.licenses.mit ];
663 690 };
664 691 };
665 692 future = super.buildPythonPackage {
666 693 name = "future-0.14.3";
667 694 buildInputs = with self; [];
668 695 doCheck = false;
669 696 propagatedBuildInputs = with self; [];
670 697 src = fetchurl {
671 698 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
672 699 md5 = "e94079b0bd1fc054929e8769fc0f6083";
673 700 };
674 701 meta = {
675 702 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
676 703 };
677 704 };
678 705 futures = super.buildPythonPackage {
679 706 name = "futures-3.0.2";
680 707 buildInputs = with self; [];
681 708 doCheck = false;
682 709 propagatedBuildInputs = with self; [];
683 710 src = fetchurl {
684 711 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
685 712 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
686 713 };
687 714 meta = {
688 715 license = [ pkgs.lib.licenses.bsdOriginal ];
689 716 };
690 717 };
691 718 gnureadline = super.buildPythonPackage {
692 719 name = "gnureadline-6.3.3";
693 720 buildInputs = with self; [];
694 721 doCheck = false;
695 722 propagatedBuildInputs = with self; [];
696 723 src = fetchurl {
697 724 url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz";
698 725 md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c";
699 726 };
700 727 meta = {
701 728 license = [ pkgs.lib.licenses.gpl1 ];
702 729 };
703 730 };
704 731 gprof2dot = super.buildPythonPackage {
705 732 name = "gprof2dot-2015.12.1";
706 733 buildInputs = with self; [];
707 734 doCheck = false;
708 735 propagatedBuildInputs = with self; [];
709 736 src = fetchurl {
710 737 url = "https://pypi.python.org/packages/b9/34/7bf93c1952d40fa5c95ad963f4d8344b61ef58558632402eca18e6c14127/gprof2dot-2015.12.1.tar.gz";
711 738 md5 = "e23bf4e2f94db032750c193384b4165b";
712 739 };
713 740 meta = {
714 741 license = [ { fullName = "LGPL"; } ];
715 742 };
716 743 };
717 744 gunicorn = super.buildPythonPackage {
718 745 name = "gunicorn-19.6.0";
719 746 buildInputs = with self; [];
720 747 doCheck = false;
721 748 propagatedBuildInputs = with self; [];
722 749 src = fetchurl {
723 750 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
724 751 md5 = "338e5e8a83ea0f0625f768dba4597530";
725 752 };
726 753 meta = {
727 754 license = [ pkgs.lib.licenses.mit ];
728 755 };
729 756 };
730 757 infrae.cache = super.buildPythonPackage {
731 758 name = "infrae.cache-1.0.1";
732 759 buildInputs = with self; [];
733 760 doCheck = false;
734 761 propagatedBuildInputs = with self; [Beaker repoze.lru];
735 762 src = fetchurl {
736 763 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
737 764 md5 = "b09076a766747e6ed2a755cc62088e32";
738 765 };
739 766 meta = {
740 767 license = [ pkgs.lib.licenses.zpt21 ];
741 768 };
742 769 };
743 770 invoke = super.buildPythonPackage {
744 771 name = "invoke-0.13.0";
745 772 buildInputs = with self; [];
746 773 doCheck = false;
747 774 propagatedBuildInputs = with self; [];
748 775 src = fetchurl {
749 776 url = "https://pypi.python.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
750 777 md5 = "c0d1ed4bfb34eaab551662d8cfee6540";
751 778 };
752 779 meta = {
753 780 license = [ pkgs.lib.licenses.bsdOriginal ];
754 781 };
755 782 };
756 783 ipdb = super.buildPythonPackage {
757 784 name = "ipdb-0.8";
758 785 buildInputs = with self; [];
759 786 doCheck = false;
760 787 propagatedBuildInputs = with self; [ipython];
761 788 src = fetchurl {
762 789 url = "https://pypi.python.org/packages/f0/25/d7dd430ced6cd8dc242a933c8682b5dbf32eb4011d82f87e34209e5ec845/ipdb-0.8.zip";
763 790 md5 = "96dca0712efa01aa5eaf6b22071dd3ed";
764 791 };
765 792 meta = {
766 793 license = [ pkgs.lib.licenses.gpl1 ];
767 794 };
768 795 };
769 796 ipython = super.buildPythonPackage {
770 797 name = "ipython-3.1.0";
771 798 buildInputs = with self; [];
772 799 doCheck = false;
773 800 propagatedBuildInputs = with self; [];
774 801 src = fetchurl {
775 802 url = "https://pypi.python.org/packages/06/91/120c0835254c120af89f066afaabf81289bc2726c1fc3ca0555df6882f58/ipython-3.1.0.tar.gz";
776 803 md5 = "a749d90c16068687b0ec45a27e72ef8f";
777 804 };
778 805 meta = {
779 806 license = [ pkgs.lib.licenses.bsdOriginal ];
780 807 };
781 808 };
782 809 iso8601 = super.buildPythonPackage {
783 810 name = "iso8601-0.1.11";
784 811 buildInputs = with self; [];
785 812 doCheck = false;
786 813 propagatedBuildInputs = with self; [];
787 814 src = fetchurl {
788 815 url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
789 816 md5 = "b06d11cd14a64096f907086044f0fe38";
790 817 };
791 818 meta = {
792 819 license = [ pkgs.lib.licenses.mit ];
793 820 };
794 821 };
795 822 itsdangerous = super.buildPythonPackage {
796 823 name = "itsdangerous-0.24";
797 824 buildInputs = with self; [];
798 825 doCheck = false;
799 826 propagatedBuildInputs = with self; [];
800 827 src = fetchurl {
801 828 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
802 829 md5 = "a3d55aa79369aef5345c036a8a26307f";
803 830 };
804 831 meta = {
805 832 license = [ pkgs.lib.licenses.bsdOriginal ];
806 833 };
807 834 };
808 835 kombu = super.buildPythonPackage {
809 836 name = "kombu-1.5.1";
810 837 buildInputs = with self; [];
811 838 doCheck = false;
812 839 propagatedBuildInputs = with self; [anyjson amqplib];
813 840 src = fetchurl {
814 841 url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz";
815 842 md5 = "50662f3c7e9395b3d0721fb75d100b63";
816 843 };
817 844 meta = {
818 845 license = [ pkgs.lib.licenses.bsdOriginal ];
819 846 };
820 847 };
821 848 lxml = super.buildPythonPackage {
822 849 name = "lxml-3.4.4";
823 850 buildInputs = with self; [];
824 851 doCheck = false;
825 852 propagatedBuildInputs = with self; [];
826 853 src = fetchurl {
827 854 url = "https://pypi.python.org/packages/63/c7/4f2a2a4ad6c6fa99b14be6b3c1cece9142e2d915aa7c43c908677afc8fa4/lxml-3.4.4.tar.gz";
828 855 md5 = "a9a65972afc173ec7a39c585f4eea69c";
829 856 };
830 857 meta = {
831 858 license = [ pkgs.lib.licenses.bsdOriginal ];
832 859 };
833 860 };
834 861 mccabe = super.buildPythonPackage {
835 862 name = "mccabe-0.3";
836 863 buildInputs = with self; [];
837 864 doCheck = false;
838 865 propagatedBuildInputs = with self; [];
839 866 src = fetchurl {
840 867 url = "https://pypi.python.org/packages/c9/2e/75231479e11a906b64ac43bad9d0bb534d00080b18bdca8db9da46e1faf7/mccabe-0.3.tar.gz";
841 868 md5 = "81640948ff226f8c12b3277059489157";
842 869 };
843 870 meta = {
844 871 license = [ { fullName = "Expat license"; } pkgs.lib.licenses.mit ];
845 872 };
846 873 };
847 874 meld3 = super.buildPythonPackage {
848 875 name = "meld3-1.0.2";
849 876 buildInputs = with self; [];
850 877 doCheck = false;
851 878 propagatedBuildInputs = with self; [];
852 879 src = fetchurl {
853 880 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
854 881 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
855 882 };
856 883 meta = {
857 884 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
858 885 };
859 886 };
860 887 mock = super.buildPythonPackage {
861 888 name = "mock-1.0.1";
862 889 buildInputs = with self; [];
863 890 doCheck = false;
864 891 propagatedBuildInputs = with self; [];
865 892 src = fetchurl {
866 893 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
867 894 md5 = "869f08d003c289a97c1a6610faf5e913";
868 895 };
869 896 meta = {
870 897 license = [ pkgs.lib.licenses.bsdOriginal ];
871 898 };
872 899 };
873 900 msgpack-python = super.buildPythonPackage {
874 901 name = "msgpack-python-0.4.6";
875 902 buildInputs = with self; [];
876 903 doCheck = false;
877 904 propagatedBuildInputs = with self; [];
878 905 src = fetchurl {
879 906 url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz";
880 907 md5 = "8b317669314cf1bc881716cccdaccb30";
881 908 };
882 909 meta = {
883 910 license = [ pkgs.lib.licenses.asl20 ];
884 911 };
885 912 };
886 913 nose = super.buildPythonPackage {
887 914 name = "nose-1.3.6";
888 915 buildInputs = with self; [];
889 916 doCheck = false;
890 917 propagatedBuildInputs = with self; [];
891 918 src = fetchurl {
892 919 url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz";
893 920 md5 = "0ca546d81ca8309080fc80cb389e7a16";
894 921 };
895 922 meta = {
896 923 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "GNU LGPL"; } ];
897 924 };
898 925 };
899 926 objgraph = super.buildPythonPackage {
900 927 name = "objgraph-2.0.0";
901 928 buildInputs = with self; [];
902 929 doCheck = false;
903 930 propagatedBuildInputs = with self; [];
904 931 src = fetchurl {
905 932 url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz";
906 933 md5 = "25b0d5e5adc74aa63ead15699614159c";
907 934 };
908 935 meta = {
909 936 license = [ pkgs.lib.licenses.mit ];
910 937 };
911 938 };
912 939 packaging = super.buildPythonPackage {
913 940 name = "packaging-15.2";
914 941 buildInputs = with self; [];
915 942 doCheck = false;
916 943 propagatedBuildInputs = with self; [];
917 944 src = fetchurl {
918 945 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
919 946 md5 = "c16093476f6ced42128bf610e5db3784";
920 947 };
921 948 meta = {
922 949 license = [ pkgs.lib.licenses.asl20 ];
923 950 };
924 951 };
925 952 paramiko = super.buildPythonPackage {
926 953 name = "paramiko-1.15.1";
927 954 buildInputs = with self; [];
928 955 doCheck = false;
929 956 propagatedBuildInputs = with self; [pycrypto ecdsa];
930 957 src = fetchurl {
931 958 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
932 959 md5 = "48c274c3f9b1282932567b21f6acf3b5";
933 960 };
934 961 meta = {
935 962 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
936 963 };
937 964 };
938 965 pep8 = super.buildPythonPackage {
939 966 name = "pep8-1.5.7";
940 967 buildInputs = with self; [];
941 968 doCheck = false;
942 969 propagatedBuildInputs = with self; [];
943 970 src = fetchurl {
944 971 url = "https://pypi.python.org/packages/8b/de/259f5e735897ada1683489dd514b2a1c91aaa74e5e6b68f80acf128a6368/pep8-1.5.7.tar.gz";
945 972 md5 = "f6adbdd69365ecca20513c709f9b7c93";
946 973 };
947 974 meta = {
948 975 license = [ { fullName = "Expat license"; } pkgs.lib.licenses.mit ];
949 976 };
950 977 };
951 978 psutil = super.buildPythonPackage {
952 979 name = "psutil-2.2.1";
953 980 buildInputs = with self; [];
954 981 doCheck = false;
955 982 propagatedBuildInputs = with self; [];
956 983 src = fetchurl {
957 984 url = "https://pypi.python.org/packages/df/47/ee54ef14dd40f8ce831a7581001a5096494dc99fe71586260ca6b531fe86/psutil-2.2.1.tar.gz";
958 985 md5 = "1a2b58cd9e3a53528bb6148f0c4d5244";
959 986 };
960 987 meta = {
961 988 license = [ pkgs.lib.licenses.bsdOriginal ];
962 989 };
963 990 };
964 991 psycopg2 = super.buildPythonPackage {
965 992 name = "psycopg2-2.6.1";
966 993 buildInputs = with self; [];
967 994 doCheck = false;
968 995 propagatedBuildInputs = with self; [];
969 996 src = fetchurl {
970 997 url = "https://pypi.python.org/packages/86/fd/cc8315be63a41fe000cce20482a917e874cdc1151e62cb0141f5e55f711e/psycopg2-2.6.1.tar.gz";
971 998 md5 = "842b44f8c95517ed5b792081a2370da1";
972 999 };
973 1000 meta = {
974 1001 license = [ pkgs.lib.licenses.zpt21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
975 1002 };
976 1003 };
977 1004 py = super.buildPythonPackage {
978 1005 name = "py-1.4.29";
979 1006 buildInputs = with self; [];
980 1007 doCheck = false;
981 1008 propagatedBuildInputs = with self; [];
982 1009 src = fetchurl {
983 1010 url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz";
984 1011 md5 = "c28e0accba523a29b35a48bb703fb96c";
985 1012 };
986 1013 meta = {
987 1014 license = [ pkgs.lib.licenses.mit ];
988 1015 };
989 1016 };
990 1017 py-bcrypt = super.buildPythonPackage {
991 1018 name = "py-bcrypt-0.4";
992 1019 buildInputs = with self; [];
993 1020 doCheck = false;
994 1021 propagatedBuildInputs = with self; [];
995 1022 src = fetchurl {
996 1023 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
997 1024 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
998 1025 };
999 1026 meta = {
1000 1027 license = [ pkgs.lib.licenses.bsdOriginal ];
1001 1028 };
1002 1029 };
1003 1030 py-gfm = super.buildPythonPackage {
1004 1031 name = "py-gfm-0.1.3";
1005 1032 buildInputs = with self; [];
1006 1033 doCheck = false;
1007 1034 propagatedBuildInputs = with self; [setuptools Markdown];
1008 1035 src = fetchurl {
1009 1036 url = "https://pypi.python.org/packages/12/e4/6b3d8678da04f97d7490d8264d8de51c2dc9fb91209ccee9c515c95e14c5/py-gfm-0.1.3.tar.gz";
1010 1037 md5 = "e588d9e69640a241b97e2c59c22527a6";
1011 1038 };
1012 1039 meta = {
1013 1040 license = [ pkgs.lib.licenses.bsdOriginal ];
1014 1041 };
1015 1042 };
1016 1043 pycrypto = super.buildPythonPackage {
1017 1044 name = "pycrypto-2.6.1";
1018 1045 buildInputs = with self; [];
1019 1046 doCheck = false;
1020 1047 propagatedBuildInputs = with self; [];
1021 1048 src = fetchurl {
1022 1049 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1023 1050 md5 = "55a61a054aa66812daf5161a0d5d7eda";
1024 1051 };
1025 1052 meta = {
1026 1053 license = [ pkgs.lib.licenses.publicDomain ];
1027 1054 };
1028 1055 };
1029 1056 pycurl = super.buildPythonPackage {
1030 1057 name = "pycurl-7.19.5";
1031 1058 buildInputs = with self; [];
1032 1059 doCheck = false;
1033 1060 propagatedBuildInputs = with self; [];
1034 1061 src = fetchurl {
1035 1062 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
1036 1063 md5 = "47b4eac84118e2606658122104e62072";
1037 1064 };
1038 1065 meta = {
1039 1066 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1040 1067 };
1041 1068 };
1042 1069 pyflakes = super.buildPythonPackage {
1043 1070 name = "pyflakes-0.8.1";
1044 1071 buildInputs = with self; [];
1045 1072 doCheck = false;
1046 1073 propagatedBuildInputs = with self; [];
1047 1074 src = fetchurl {
1048 1075 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
1049 1076 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
1050 1077 };
1051 1078 meta = {
1052 1079 license = [ pkgs.lib.licenses.mit ];
1053 1080 };
1054 1081 };
1055 1082 pyparsing = super.buildPythonPackage {
1056 1083 name = "pyparsing-1.5.7";
1057 1084 buildInputs = with self; [];
1058 1085 doCheck = false;
1059 1086 propagatedBuildInputs = with self; [];
1060 1087 src = fetchurl {
1061 1088 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
1062 1089 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
1063 1090 };
1064 1091 meta = {
1065 1092 license = [ pkgs.lib.licenses.mit ];
1066 1093 };
1067 1094 };
1068 1095 pyramid = super.buildPythonPackage {
1069 1096 name = "pyramid-1.6.1";
1070 1097 buildInputs = with self; [];
1071 1098 doCheck = false;
1072 1099 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
1073 1100 src = fetchurl {
1074 1101 url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz";
1075 1102 md5 = "b18688ff3cc33efdbb098a35b45dd122";
1076 1103 };
1077 1104 meta = {
1078 1105 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1079 1106 };
1080 1107 };
1081 1108 pyramid-beaker = super.buildPythonPackage {
1082 1109 name = "pyramid-beaker-0.8";
1083 1110 buildInputs = with self; [];
1084 1111 doCheck = false;
1085 1112 propagatedBuildInputs = with self; [pyramid Beaker];
1086 1113 src = fetchurl {
1087 1114 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
1088 1115 md5 = "22f14be31b06549f80890e2c63a93834";
1089 1116 };
1090 1117 meta = {
1091 1118 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1092 1119 };
1093 1120 };
1094 1121 pyramid-debugtoolbar = super.buildPythonPackage {
1095 1122 name = "pyramid-debugtoolbar-2.4.2";
1096 1123 buildInputs = with self; [];
1097 1124 doCheck = false;
1098 1125 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments];
1099 1126 src = fetchurl {
1100 1127 url = "https://pypi.python.org/packages/89/00/ed5426ee41ed747ba3ffd30e8230841a6878286ea67d480b1444d24f06a2/pyramid_debugtoolbar-2.4.2.tar.gz";
1101 1128 md5 = "073ea67086cc4bd5decc3a000853642d";
1102 1129 };
1103 1130 meta = {
1104 1131 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1105 1132 };
1106 1133 };
1107 1134 pyramid-jinja2 = super.buildPythonPackage {
1108 1135 name = "pyramid-jinja2-2.5";
1109 1136 buildInputs = with self; [];
1110 1137 doCheck = false;
1111 1138 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
1112 1139 src = fetchurl {
1113 1140 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
1114 1141 md5 = "07cb6547204ac5e6f0b22a954ccee928";
1115 1142 };
1116 1143 meta = {
1117 1144 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1118 1145 };
1119 1146 };
1120 1147 pyramid-mako = super.buildPythonPackage {
1121 1148 name = "pyramid-mako-1.0.2";
1122 1149 buildInputs = with self; [];
1123 1150 doCheck = false;
1124 1151 propagatedBuildInputs = with self; [pyramid Mako];
1125 1152 src = fetchurl {
1126 1153 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
1127 1154 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
1128 1155 };
1129 1156 meta = {
1130 1157 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1131 1158 };
1132 1159 };
1133 1160 pysqlite = super.buildPythonPackage {
1134 1161 name = "pysqlite-2.6.3";
1135 1162 buildInputs = with self; [];
1136 1163 doCheck = false;
1137 1164 propagatedBuildInputs = with self; [];
1138 1165 src = fetchurl {
1139 1166 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
1140 1167 md5 = "7ff1cedee74646b50117acff87aa1cfa";
1141 1168 };
1142 1169 meta = {
1143 1170 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1144 1171 };
1145 1172 };
1146 1173 pytest = super.buildPythonPackage {
1147 1174 name = "pytest-2.8.5";
1148 1175 buildInputs = with self; [];
1149 1176 doCheck = false;
1150 1177 propagatedBuildInputs = with self; [py];
1151 1178 src = fetchurl {
1152 1179 url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip";
1153 1180 md5 = "8493b06f700862f1294298d6c1b715a9";
1154 1181 };
1155 1182 meta = {
1156 1183 license = [ pkgs.lib.licenses.mit ];
1157 1184 };
1158 1185 };
1159 1186 pytest-catchlog = super.buildPythonPackage {
1160 1187 name = "pytest-catchlog-1.2.2";
1161 1188 buildInputs = with self; [];
1162 1189 doCheck = false;
1163 1190 propagatedBuildInputs = with self; [py pytest];
1164 1191 src = fetchurl {
1165 1192 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
1166 1193 md5 = "09d890c54c7456c818102b7ff8c182c8";
1167 1194 };
1168 1195 meta = {
1169 1196 license = [ pkgs.lib.licenses.mit ];
1170 1197 };
1171 1198 };
1172 1199 pytest-cov = super.buildPythonPackage {
1173 1200 name = "pytest-cov-1.8.1";
1174 1201 buildInputs = with self; [];
1175 1202 doCheck = false;
1176 1203 propagatedBuildInputs = with self; [py pytest coverage cov-core];
1177 1204 src = fetchurl {
1178 1205 url = "https://pypi.python.org/packages/11/4b/b04646e97f1721878eb21e9f779102d84dd044d324382263b1770a3e4838/pytest-cov-1.8.1.tar.gz";
1179 1206 md5 = "76c778afa2494088270348be42d759fc";
1180 1207 };
1181 1208 meta = {
1182 1209 license = [ pkgs.lib.licenses.mit ];
1183 1210 };
1184 1211 };
1185 1212 pytest-profiling = super.buildPythonPackage {
1186 1213 name = "pytest-profiling-1.0.1";
1187 1214 buildInputs = with self; [];
1188 1215 doCheck = false;
1189 1216 propagatedBuildInputs = with self; [six pytest gprof2dot];
1190 1217 src = fetchurl {
1191 1218 url = "https://pypi.python.org/packages/d8/67/8ffab73406e22870e07fa4dc8dce1d7689b26dba8efd00161c9b6fc01ec0/pytest-profiling-1.0.1.tar.gz";
1192 1219 md5 = "354404eb5b3fd4dc5eb7fffbb3d9b68b";
1193 1220 };
1194 1221 meta = {
1195 1222 license = [ pkgs.lib.licenses.mit ];
1196 1223 };
1197 1224 };
1198 1225 pytest-runner = super.buildPythonPackage {
1199 1226 name = "pytest-runner-2.7.1";
1200 1227 buildInputs = with self; [];
1201 1228 doCheck = false;
1202 1229 propagatedBuildInputs = with self; [];
1203 1230 src = fetchurl {
1204 1231 url = "https://pypi.python.org/packages/99/6b/c4ff4418d3424d4475b7af60724fd4a5cdd91ed8e489dc9443281f0052bc/pytest-runner-2.7.1.tar.gz";
1205 1232 md5 = "e56f0bc8d79a6bd91772b44ef4215c7e";
1206 1233 };
1207 1234 meta = {
1208 1235 license = [ pkgs.lib.licenses.mit ];
1209 1236 };
1210 1237 };
1211 1238 pytest-timeout = super.buildPythonPackage {
1212 1239 name = "pytest-timeout-0.4";
1213 1240 buildInputs = with self; [];
1214 1241 doCheck = false;
1215 1242 propagatedBuildInputs = with self; [pytest];
1216 1243 src = fetchurl {
1217 1244 url = "https://pypi.python.org/packages/24/48/5f6bd4b8026a26e1dd427243d560a29a0f1b24a5c7cffca4bf049a7bb65b/pytest-timeout-0.4.tar.gz";
1218 1245 md5 = "03b28aff69cbbfb959ed35ade5fde262";
1219 1246 };
1220 1247 meta = {
1221 1248 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1222 1249 };
1223 1250 };
1224 1251 python-dateutil = super.buildPythonPackage {
1225 1252 name = "python-dateutil-1.5";
1226 1253 buildInputs = with self; [];
1227 1254 doCheck = false;
1228 1255 propagatedBuildInputs = with self; [];
1229 1256 src = fetchurl {
1230 1257 url = "https://pypi.python.org/packages/b4/7c/df59c89a753eb33c7c44e1dd42de0e9bc2ccdd5a4d576e0bfad97cc280cb/python-dateutil-1.5.tar.gz";
1231 1258 md5 = "0dcb1de5e5cad69490a3b6ab63f0cfa5";
1232 1259 };
1233 1260 meta = {
1234 1261 license = [ pkgs.lib.licenses.psfl ];
1235 1262 };
1236 1263 };
1237 1264 python-editor = super.buildPythonPackage {
1238 1265 name = "python-editor-1.0.1";
1239 1266 buildInputs = with self; [];
1240 1267 doCheck = false;
1241 1268 propagatedBuildInputs = with self; [];
1242 1269 src = fetchurl {
1243 1270 url = "https://pypi.python.org/packages/2b/c0/df7b87d5cf016f82eab3b05cd35f53287c1178ad8c42bfb6fa61b89b22f6/python-editor-1.0.1.tar.gz";
1244 1271 md5 = "e1fa63535b40e022fa4fd646fd8b511a";
1245 1272 };
1246 1273 meta = {
1247 1274 license = [ pkgs.lib.licenses.asl20 ];
1248 1275 };
1249 1276 };
1250 1277 python-ldap = super.buildPythonPackage {
1251 1278 name = "python-ldap-2.4.19";
1252 1279 buildInputs = with self; [];
1253 1280 doCheck = false;
1254 1281 propagatedBuildInputs = with self; [setuptools];
1255 1282 src = fetchurl {
1256 1283 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
1257 1284 md5 = "b941bf31d09739492aa19ef679e94ae3";
1258 1285 };
1259 1286 meta = {
1260 1287 license = [ pkgs.lib.licenses.psfl ];
1261 1288 };
1262 1289 };
1263 1290 python-memcached = super.buildPythonPackage {
1264 1291 name = "python-memcached-1.57";
1265 1292 buildInputs = with self; [];
1266 1293 doCheck = false;
1267 1294 propagatedBuildInputs = with self; [six];
1268 1295 src = fetchurl {
1269 1296 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
1270 1297 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
1271 1298 };
1272 1299 meta = {
1273 1300 license = [ pkgs.lib.licenses.psfl ];
1274 1301 };
1275 1302 };
1276 1303 python-pam = super.buildPythonPackage {
1277 1304 name = "python-pam-1.8.2";
1278 1305 buildInputs = with self; [];
1279 1306 doCheck = false;
1280 1307 propagatedBuildInputs = with self; [];
1281 1308 src = fetchurl {
1282 1309 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
1283 1310 md5 = "db71b6b999246fb05d78ecfbe166629d";
1284 1311 };
1285 1312 meta = {
1286 1313 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1287 1314 };
1288 1315 };
1289 1316 pytz = super.buildPythonPackage {
1290 1317 name = "pytz-2015.4";
1291 1318 buildInputs = with self; [];
1292 1319 doCheck = false;
1293 1320 propagatedBuildInputs = with self; [];
1294 1321 src = fetchurl {
1295 1322 url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip";
1296 1323 md5 = "233f2a2b370d03f9b5911700cc9ebf3c";
1297 1324 };
1298 1325 meta = {
1299 1326 license = [ pkgs.lib.licenses.mit ];
1300 1327 };
1301 1328 };
1302 1329 pyzmq = super.buildPythonPackage {
1303 1330 name = "pyzmq-14.6.0";
1304 1331 buildInputs = with self; [];
1305 1332 doCheck = false;
1306 1333 propagatedBuildInputs = with self; [];
1307 1334 src = fetchurl {
1308 1335 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1309 1336 md5 = "395b5de95a931afa5b14c9349a5b8024";
1310 1337 };
1311 1338 meta = {
1312 1339 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1313 1340 };
1314 1341 };
1315 1342 recaptcha-client = super.buildPythonPackage {
1316 1343 name = "recaptcha-client-1.0.6";
1317 1344 buildInputs = with self; [];
1318 1345 doCheck = false;
1319 1346 propagatedBuildInputs = with self; [];
1320 1347 src = fetchurl {
1321 1348 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1322 1349 md5 = "74228180f7e1fb76c4d7089160b0d919";
1323 1350 };
1324 1351 meta = {
1325 1352 license = [ { fullName = "MIT/X11"; } ];
1326 1353 };
1327 1354 };
1328 1355 repoze.lru = super.buildPythonPackage {
1329 1356 name = "repoze.lru-0.6";
1330 1357 buildInputs = with self; [];
1331 1358 doCheck = false;
1332 1359 propagatedBuildInputs = with self; [];
1333 1360 src = fetchurl {
1334 1361 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
1335 1362 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
1336 1363 };
1337 1364 meta = {
1338 1365 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1339 1366 };
1340 1367 };
1341 1368 requests = super.buildPythonPackage {
1342 1369 name = "requests-2.9.1";
1343 1370 buildInputs = with self; [];
1344 1371 doCheck = false;
1345 1372 propagatedBuildInputs = with self; [];
1346 1373 src = fetchurl {
1347 1374 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1348 1375 md5 = "0b7f480d19012ec52bab78292efd976d";
1349 1376 };
1350 1377 meta = {
1351 1378 license = [ pkgs.lib.licenses.asl20 ];
1352 1379 };
1353 1380 };
1354 1381 rhodecode-enterprise-ce = super.buildPythonPackage {
1355 1382 name = "rhodecode-enterprise-ce-4.3.0";
1356 1383 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
1357 1384 doCheck = true;
1358 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery colander decorator docutils gunicorn infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 py-gfm pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
1385 propagatedBuildInputs = with self; [Babel Beaker FormEncode Mako Markdown MarkupSafe MySQL-python Paste PasteDeploy PasteScript Pygments Pylons Pyro4 Routes SQLAlchemy Tempita URLObject WebError WebHelpers WebHelpers2 WebOb WebTest Whoosh alembic amqplib anyjson appenlight-client authomatic backport-ipaddress celery colander decorator deform docutils gunicorn infrae.cache ipython iso8601 kombu msgpack-python packaging psycopg2 py-gfm pycrypto pycurl pyparsing pyramid pyramid-debugtoolbar pyramid-mako pyramid-beaker pysqlite python-dateutil python-ldap python-memcached python-pam recaptcha-client repoze.lru requests simplejson waitress zope.cachedescriptors dogpile.cache dogpile.core psutil py-bcrypt];
1359 1386 src = ./.;
1360 1387 meta = {
1361 1388 license = [ { fullName = "AGPLv3, and Commercial License"; } ];
1362 1389 };
1363 1390 };
1364 1391 rhodecode-tools = super.buildPythonPackage {
1365 1392 name = "rhodecode-tools-0.8.3";
1366 1393 buildInputs = with self; [];
1367 1394 doCheck = false;
1368 1395 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests Whoosh elasticsearch elasticsearch-dsl];
1369 1396 src = fetchurl {
1370 1397 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.8.3.zip";
1371 1398 md5 = "9acdfd71b8ddf4056057065f37ab9ccb";
1372 1399 };
1373 1400 meta = {
1374 1401 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1375 1402 };
1376 1403 };
1377 1404 serpent = super.buildPythonPackage {
1378 1405 name = "serpent-1.12";
1379 1406 buildInputs = with self; [];
1380 1407 doCheck = false;
1381 1408 propagatedBuildInputs = with self; [];
1382 1409 src = fetchurl {
1383 1410 url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz";
1384 1411 md5 = "05869ac7b062828b34f8f927f0457b65";
1385 1412 };
1386 1413 meta = {
1387 1414 license = [ pkgs.lib.licenses.mit ];
1388 1415 };
1389 1416 };
1390 1417 setproctitle = super.buildPythonPackage {
1391 1418 name = "setproctitle-1.1.8";
1392 1419 buildInputs = with self; [];
1393 1420 doCheck = false;
1394 1421 propagatedBuildInputs = with self; [];
1395 1422 src = fetchurl {
1396 1423 url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz";
1397 1424 md5 = "728f4c8c6031bbe56083a48594027edd";
1398 1425 };
1399 1426 meta = {
1400 1427 license = [ pkgs.lib.licenses.bsdOriginal ];
1401 1428 };
1402 1429 };
1403 1430 setuptools = super.buildPythonPackage {
1404 1431 name = "setuptools-20.8.1";
1405 1432 buildInputs = with self; [];
1406 1433 doCheck = false;
1407 1434 propagatedBuildInputs = with self; [];
1408 1435 src = fetchurl {
1409 1436 url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip";
1410 1437 md5 = "fe58a5cac0df20bb83942b252a4b0543";
1411 1438 };
1412 1439 meta = {
1413 1440 license = [ pkgs.lib.licenses.mit ];
1414 1441 };
1415 1442 };
1416 1443 setuptools-scm = super.buildPythonPackage {
1417 1444 name = "setuptools-scm-1.11.0";
1418 1445 buildInputs = with self; [];
1419 1446 doCheck = false;
1420 1447 propagatedBuildInputs = with self; [];
1421 1448 src = fetchurl {
1422 1449 url = "https://pypi.python.org/packages/cd/5f/e3a038292358058d83d764a47d09114aa5a8003ed4529518f9e580f1a94f/setuptools_scm-1.11.0.tar.gz";
1423 1450 md5 = "4c5c896ba52e134bbc3507bac6400087";
1424 1451 };
1425 1452 meta = {
1426 1453 license = [ pkgs.lib.licenses.mit ];
1427 1454 };
1428 1455 };
1429 1456 simplejson = super.buildPythonPackage {
1430 1457 name = "simplejson-3.7.2";
1431 1458 buildInputs = with self; [];
1432 1459 doCheck = false;
1433 1460 propagatedBuildInputs = with self; [];
1434 1461 src = fetchurl {
1435 1462 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1436 1463 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1437 1464 };
1438 1465 meta = {
1439 1466 license = [ pkgs.lib.licenses.mit pkgs.lib.licenses.afl21 ];
1440 1467 };
1441 1468 };
1442 1469 six = super.buildPythonPackage {
1443 1470 name = "six-1.9.0";
1444 1471 buildInputs = with self; [];
1445 1472 doCheck = false;
1446 1473 propagatedBuildInputs = with self; [];
1447 1474 src = fetchurl {
1448 1475 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
1449 1476 md5 = "476881ef4012262dfc8adc645ee786c4";
1450 1477 };
1451 1478 meta = {
1452 1479 license = [ pkgs.lib.licenses.mit ];
1453 1480 };
1454 1481 };
1455 1482 subprocess32 = super.buildPythonPackage {
1456 1483 name = "subprocess32-3.2.6";
1457 1484 buildInputs = with self; [];
1458 1485 doCheck = false;
1459 1486 propagatedBuildInputs = with self; [];
1460 1487 src = fetchurl {
1461 1488 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1462 1489 md5 = "754c5ab9f533e764f931136974b618f1";
1463 1490 };
1464 1491 meta = {
1465 1492 license = [ pkgs.lib.licenses.psfl ];
1466 1493 };
1467 1494 };
1468 1495 supervisor = super.buildPythonPackage {
1469 1496 name = "supervisor-3.3.0";
1470 1497 buildInputs = with self; [];
1471 1498 doCheck = false;
1472 1499 propagatedBuildInputs = with self; [meld3];
1473 1500 src = fetchurl {
1474 1501 url = "https://pypi.python.org/packages/44/80/d28047d120bfcc8158b4e41127706731ee6a3419c661e0a858fb0e7c4b2d/supervisor-3.3.0.tar.gz";
1475 1502 md5 = "46bac00378d1eddb616752b990c67416";
1476 1503 };
1477 1504 meta = {
1478 1505 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1479 1506 };
1480 1507 };
1481 1508 transifex-client = super.buildPythonPackage {
1482 1509 name = "transifex-client-0.10";
1483 1510 buildInputs = with self; [];
1484 1511 doCheck = false;
1485 1512 propagatedBuildInputs = with self; [];
1486 1513 src = fetchurl {
1487 1514 url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz";
1488 1515 md5 = "5549538d84b8eede6b254cd81ae024fa";
1489 1516 };
1490 1517 meta = {
1491 1518 license = [ pkgs.lib.licenses.gpl2 ];
1492 1519 };
1493 1520 };
1494 1521 translationstring = super.buildPythonPackage {
1495 1522 name = "translationstring-1.3";
1496 1523 buildInputs = with self; [];
1497 1524 doCheck = false;
1498 1525 propagatedBuildInputs = with self; [];
1499 1526 src = fetchurl {
1500 1527 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1501 1528 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1502 1529 };
1503 1530 meta = {
1504 1531 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
1505 1532 };
1506 1533 };
1507 1534 trollius = super.buildPythonPackage {
1508 1535 name = "trollius-1.0.4";
1509 1536 buildInputs = with self; [];
1510 1537 doCheck = false;
1511 1538 propagatedBuildInputs = with self; [futures];
1512 1539 src = fetchurl {
1513 1540 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1514 1541 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1515 1542 };
1516 1543 meta = {
1517 1544 license = [ pkgs.lib.licenses.asl20 ];
1518 1545 };
1519 1546 };
1520 1547 uWSGI = super.buildPythonPackage {
1521 1548 name = "uWSGI-2.0.11.2";
1522 1549 buildInputs = with self; [];
1523 1550 doCheck = false;
1524 1551 propagatedBuildInputs = with self; [];
1525 1552 src = fetchurl {
1526 1553 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1527 1554 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1528 1555 };
1529 1556 meta = {
1530 1557 license = [ pkgs.lib.licenses.gpl2 ];
1531 1558 };
1532 1559 };
1533 1560 urllib3 = super.buildPythonPackage {
1534 1561 name = "urllib3-1.16";
1535 1562 buildInputs = with self; [];
1536 1563 doCheck = false;
1537 1564 propagatedBuildInputs = with self; [];
1538 1565 src = fetchurl {
1539 1566 url = "https://pypi.python.org/packages/3b/f0/e763169124e3f5db0926bc3dbfcd580a105f9ca44cf5d8e6c7a803c9f6b5/urllib3-1.16.tar.gz";
1540 1567 md5 = "fcaab1c5385c57deeb7053d3d7d81d59";
1541 1568 };
1542 1569 meta = {
1543 1570 license = [ pkgs.lib.licenses.mit ];
1544 1571 };
1545 1572 };
1546 1573 venusian = super.buildPythonPackage {
1547 1574 name = "venusian-1.0";
1548 1575 buildInputs = with self; [];
1549 1576 doCheck = false;
1550 1577 propagatedBuildInputs = with self; [];
1551 1578 src = fetchurl {
1552 1579 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1553 1580 md5 = "dccf2eafb7113759d60c86faf5538756";
1554 1581 };
1555 1582 meta = {
1556 1583 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1557 1584 };
1558 1585 };
1559 1586 waitress = super.buildPythonPackage {
1560 1587 name = "waitress-0.8.9";
1561 1588 buildInputs = with self; [];
1562 1589 doCheck = false;
1563 1590 propagatedBuildInputs = with self; [setuptools];
1564 1591 src = fetchurl {
1565 1592 url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz";
1566 1593 md5 = "da3f2e62b3676be5dd630703a68e2a04";
1567 1594 };
1568 1595 meta = {
1569 1596 license = [ pkgs.lib.licenses.zpt21 ];
1570 1597 };
1571 1598 };
1572 1599 wsgiref = super.buildPythonPackage {
1573 1600 name = "wsgiref-0.1.2";
1574 1601 buildInputs = with self; [];
1575 1602 doCheck = false;
1576 1603 propagatedBuildInputs = with self; [];
1577 1604 src = fetchurl {
1578 1605 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
1579 1606 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
1580 1607 };
1581 1608 meta = {
1582 1609 license = [ { fullName = "PSF or ZPL"; } ];
1583 1610 };
1584 1611 };
1585 1612 zope.cachedescriptors = super.buildPythonPackage {
1586 1613 name = "zope.cachedescriptors-4.0.0";
1587 1614 buildInputs = with self; [];
1588 1615 doCheck = false;
1589 1616 propagatedBuildInputs = with self; [setuptools];
1590 1617 src = fetchurl {
1591 1618 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
1592 1619 md5 = "8d308de8c936792c8e758058fcb7d0f0";
1593 1620 };
1594 1621 meta = {
1595 1622 license = [ pkgs.lib.licenses.zpt21 ];
1596 1623 };
1597 1624 };
1598 1625 zope.deprecation = super.buildPythonPackage {
1599 1626 name = "zope.deprecation-4.1.2";
1600 1627 buildInputs = with self; [];
1601 1628 doCheck = false;
1602 1629 propagatedBuildInputs = with self; [setuptools];
1603 1630 src = fetchurl {
1604 1631 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
1605 1632 md5 = "e9a663ded58f4f9f7881beb56cae2782";
1606 1633 };
1607 1634 meta = {
1608 1635 license = [ pkgs.lib.licenses.zpt21 ];
1609 1636 };
1610 1637 };
1611 1638 zope.event = super.buildPythonPackage {
1612 1639 name = "zope.event-4.0.3";
1613 1640 buildInputs = with self; [];
1614 1641 doCheck = false;
1615 1642 propagatedBuildInputs = with self; [setuptools];
1616 1643 src = fetchurl {
1617 1644 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
1618 1645 md5 = "9a3780916332b18b8b85f522bcc3e249";
1619 1646 };
1620 1647 meta = {
1621 1648 license = [ pkgs.lib.licenses.zpt21 ];
1622 1649 };
1623 1650 };
1624 1651 zope.interface = super.buildPythonPackage {
1625 1652 name = "zope.interface-4.1.3";
1626 1653 buildInputs = with self; [];
1627 1654 doCheck = false;
1628 1655 propagatedBuildInputs = with self; [setuptools];
1629 1656 src = fetchurl {
1630 1657 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
1631 1658 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
1632 1659 };
1633 1660 meta = {
1634 1661 license = [ pkgs.lib.licenses.zpt21 ];
1635 1662 };
1636 1663 };
1637 1664
1638 1665 ### Test requirements
1639 1666
1640 1667
1641 1668 }
@@ -1,151 +1,152 b''
1 1 Babel==1.3
2 2 Beaker==1.7.0
3 3 CProfileV==1.0.6
4 4 Fabric==1.10.0
5 5 FormEncode==1.2.4
6 6 Jinja2==2.7.3
7 7 Mako==1.0.1
8 8 Markdown==2.6.2
9 9 MarkupSafe==0.23
10 10 MySQL-python==1.2.5
11 11 Paste==2.0.2
12 12 PasteDeploy==1.5.2
13 13 PasteScript==1.7.5
14 14 Pygments==2.1.3
15 15
16 16 # TODO: This version is not available on PyPI
17 17 # Pylons==1.0.2.dev20160108
18 18 Pylons==1.0.1
19 19
20 20 # TODO: This version is not available, but newer ones are
21 21 # Pyro4==4.35
22 22 Pyro4==4.41
23 23
24 24 # TODO: This should probably not be in here
25 25 # -e hg+https://johbo@code.rhodecode.com/johbo/rhodecode-fork@3a454bd1f17c0b2b2a951cf2b111e0320d7942a9#egg=RhodeCodeEnterprise-dev
26 26
27 27 # TODO: This is not really a dependency, we should add it only
28 28 # into the development environment, since there it is useful.
29 29 # RhodeCodeVCSServer==3.9.0
30 30
31 31 Routes==1.13
32 32 SQLAlchemy==0.9.9
33 33 Sphinx==1.2.2
34 34 Tempita==0.5.2
35 35 URLObject==2.4.0
36 36 WebError==0.10.3
37 37
38 38 # TODO: This is modified by us, needs a better integration. For now
39 39 # using the latest version before.
40 40 # WebHelpers==1.3.dev20150807
41 41 WebHelpers==1.3
42 42
43 43 WebHelpers2==2.0
44 44 WebOb==1.3.1
45 45 WebTest==1.4.3
46 46 Whoosh==2.7.0
47 47 alembic==0.8.4
48 48 amqplib==1.0.2
49 49 anyjson==0.3.3
50 50 appenlight-client==0.6.14
51 51 authomatic==0.1.0.post1;
52 52 backport-ipaddress==0.1
53 53 bottle==0.12.8
54 54 bumpversion==0.5.3
55 55 celery==2.2.10
56 56 click==5.1
57 57 colander==1.2
58 58 configobj==5.0.6
59 59 cov-core==1.15.0
60 60 coverage==3.7.1
61 61 cssselect==0.9.1
62 62 decorator==3.4.2
63 git+https://github.com/Pylons/deform@08fb9de077c76951f6e70e28d4bf060a209d3d39#egg=deform
63 64 docutils==0.12
64 65 dogpile.cache==0.6.1
65 66 dogpile.core==0.4.1
66 67 dulwich==0.12.0
67 68 ecdsa==0.11
68 69 flake8==2.4.1
69 70 future==0.14.3
70 71 futures==3.0.2
71 72 gprof2dot==2015.12.1
72 73 gunicorn==19.6.0
73 74
74 75 # TODO: Needs subvertpy and blows up without Subversion headers,
75 76 # actually we should not need this for Enterprise at all.
76 77 # hgsubversion==1.8.2
77 78
78 79 gnureadline==6.3.3
79 80 infrae.cache==1.0.1
80 81 invoke==0.13.0
81 82 ipdb==0.8
82 83 ipython==3.1.0
83 84 iso8601==0.1.11
84 85 itsdangerous==0.24
85 86 kombu==1.5.1
86 87 lxml==3.4.4
87 88 mccabe==0.3
88 89 meld3==1.0.2
89 90 mock==1.0.1
90 91 msgpack-python==0.4.6
91 92 nose==1.3.6
92 93 objgraph==2.0.0
93 94 packaging==15.2
94 95 paramiko==1.15.1
95 96 pep8==1.5.7
96 97 psutil==2.2.1
97 98 psycopg2==2.6.1
98 99 py==1.4.29
99 100 py-bcrypt==0.4
100 101 py-gfm==0.1.3
101 102 pycrypto==2.6.1
102 103 pycurl==7.19.5
103 104 pyflakes==0.8.1
104 105 pyparsing==1.5.7
105 106 pyramid==1.6.1
106 107 pyramid-beaker==0.8
107 108 pyramid-debugtoolbar==2.4.2
108 109 pyramid-jinja2==2.5
109 110 pyramid-mako==1.0.2
110 111 pysqlite==2.6.3
111 112 pytest==2.8.5
112 113 pytest-runner==2.7.1
113 114 pytest-catchlog==1.2.2
114 115 pytest-cov==1.8.1
115 116 pytest-profiling==1.0.1
116 117 pytest-timeout==0.4
117 118 python-dateutil==1.5
118 119 python-ldap==2.4.19
119 120 python-memcached==1.57
120 121 python-pam==1.8.2
121 122 pytz==2015.4
122 123 pyzmq==14.6.0
123 124
124 125 # TODO: This is not available in public
125 126 # rc-testdata==0.2.0
126 127
127 128 https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.8.3.zip#md5=9acdfd71b8ddf4056057065f37ab9ccb
128 129
129 130
130 131 recaptcha-client==1.0.6
131 132 repoze.lru==0.6
132 133 requests==2.9.1
133 134 serpent==1.12
134 135 setproctitle==1.1.8
135 136 setuptools==20.8.1
136 137 setuptools-scm==1.11.0
137 138 simplejson==3.7.2
138 139 six==1.9.0
139 140 subprocess32==3.2.6
140 141 supervisor==3.3.0
141 142 transifex-client==0.10
142 143 translationstring==1.3
143 144 trollius==1.0.4
144 145 uWSGI==2.0.11.2
145 146 venusian==1.0
146 147 waitress==0.8.9
147 148 wsgiref==0.1.2
148 149 zope.cachedescriptors==4.0.0
149 150 zope.deprecation==4.1.2
150 151 zope.event==4.0.3
151 152 zope.interface==4.1.3
@@ -1,394 +1,395 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Pylons middleware initialization
23 23 """
24 24 import logging
25 25
26 26 from paste.registry import RegistryManager
27 27 from paste.gzipper import make_gzip_middleware
28 28 from pylons.wsgiapp import PylonsApp
29 29 from pyramid.authorization import ACLAuthorizationPolicy
30 30 from pyramid.config import Configurator
31 31 from pyramid.static import static_view
32 32 from pyramid.settings import asbool, aslist
33 33 from pyramid.wsgi import wsgiapp
34 34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
35 35 from pylons.controllers.util import abort, redirect
36 36 import pyramid.httpexceptions as httpexceptions
37 37 from pyramid.renderers import render_to_response, render
38 38 from routes.middleware import RoutesMiddleware
39 39 import routes.util
40 40
41 41 import rhodecode
42 42 import rhodecode.integrations # do not remove this as it registers celery tasks
43 43 from rhodecode.config import patches
44 44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 45 from rhodecode.config.environment import (
46 46 load_environment, load_pyramid_environment)
47 47 from rhodecode.lib.middleware import csrf
48 48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 49 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
50 50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 51 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 52 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
53 53
54 54
55 55 log = logging.getLogger(__name__)
56 56
57 57
58 58 # this is used to avoid avoid the route lookup overhead in routesmiddleware
59 59 # for certain routes which won't go to pylons to - eg. static files, debugger
60 60 # it is only needed for the pylons migration and can be removed once complete
61 61 class SkippableRoutesMiddleware(RoutesMiddleware):
62 62 """ Routes middleware that allows you to skip prefixes """
63 63
64 64 def __init__(self, *args, **kw):
65 65 self.skip_prefixes = kw.pop('skip_prefixes', [])
66 66 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
67 67
68 68 def __call__(self, environ, start_response):
69 69 for prefix in self.skip_prefixes:
70 70 if environ['PATH_INFO'].startswith(prefix):
71 71 return self.app(environ, start_response)
72 72
73 73 return super(SkippableRoutesMiddleware, self).__call__(
74 74 environ, start_response)
75 75
76 76
77 77 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
78 78 """Create a Pylons WSGI application and return it
79 79
80 80 ``global_conf``
81 81 The inherited configuration for this application. Normally from
82 82 the [DEFAULT] section of the Paste ini file.
83 83
84 84 ``full_stack``
85 85 Whether or not this application provides a full WSGI stack (by
86 86 default, meaning it handles its own exceptions and errors).
87 87 Disable full_stack when this application is "managed" by
88 88 another WSGI middleware.
89 89
90 90 ``app_conf``
91 91 The application's local configuration. Normally specified in
92 92 the [app:<name>] section of the Paste ini file (where <name>
93 93 defaults to main).
94 94
95 95 """
96 96 # Apply compatibility patches
97 97 patches.kombu_1_5_1_python_2_7_11()
98 98 patches.inspect_getargspec()
99 99
100 100 # Configure the Pylons environment
101 101 config = load_environment(global_conf, app_conf)
102 102
103 103 # The Pylons WSGI app
104 104 app = PylonsApp(config=config)
105 105 if rhodecode.is_test:
106 106 app = csrf.CSRFDetector(app)
107 107
108 108 expected_origin = config.get('expected_origin')
109 109 if expected_origin:
110 110 # The API can be accessed from other Origins.
111 111 app = csrf.OriginChecker(app, expected_origin,
112 112 skip_urls=[routes.util.url_for('api')])
113 113
114 114
115 115 if asbool(full_stack):
116 116
117 117 # Appenlight monitoring and error handler
118 118 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
119 119
120 120 # we want our low level middleware to get to the request ASAP. We don't
121 121 # need any pylons stack middleware in them
122 122 app = VCSMiddleware(app, config, appenlight_client)
123 123
124 124 # Establish the Registry for this application
125 125 app = RegistryManager(app)
126 126
127 127 app.config = config
128 128
129 129 return app
130 130
131 131
132 132 def make_pyramid_app(global_config, **settings):
133 133 """
134 134 Constructs the WSGI application based on Pyramid and wraps the Pylons based
135 135 application.
136 136
137 137 Specials:
138 138
139 139 * We migrate from Pylons to Pyramid. While doing this, we keep both
140 140 frameworks functional. This involves moving some WSGI middlewares around
141 141 and providing access to some data internals, so that the old code is
142 142 still functional.
143 143
144 144 * The application can also be integrated like a plugin via the call to
145 145 `includeme`. This is accompanied with the other utility functions which
146 146 are called. Changing this should be done with great care to not break
147 147 cases when these fragments are assembled from another place.
148 148
149 149 """
150 150 # The edition string should be available in pylons too, so we add it here
151 151 # before copying the settings.
152 152 settings.setdefault('rhodecode.edition', 'Community Edition')
153 153
154 154 # As long as our Pylons application does expect "unprepared" settings, make
155 155 # sure that we keep an unmodified copy. This avoids unintentional change of
156 156 # behavior in the old application.
157 157 settings_pylons = settings.copy()
158 158
159 159 sanitize_settings_and_apply_defaults(settings)
160 160 config = Configurator(settings=settings)
161 161 add_pylons_compat_data(config.registry, global_config, settings_pylons)
162 162
163 163 load_pyramid_environment(global_config, settings)
164 164
165 165 includeme_first(config)
166 166 includeme(config)
167 167 pyramid_app = config.make_wsgi_app()
168 168 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
169 169 return pyramid_app
170 170
171 171
172 172 def add_pylons_compat_data(registry, global_config, settings):
173 173 """
174 174 Attach data to the registry to support the Pylons integration.
175 175 """
176 176 registry._pylons_compat_global_config = global_config
177 177 registry._pylons_compat_settings = settings
178 178
179 179
180 180 def webob_to_pyramid_http_response(webob_response):
181 181 ResponseClass = httpexceptions.status_map[webob_response.status_int]
182 182 pyramid_response = ResponseClass(webob_response.status)
183 183 pyramid_response.status = webob_response.status
184 184 pyramid_response.headers.update(webob_response.headers)
185 185 if pyramid_response.headers['content-type'] == 'text/html':
186 186 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
187 187 return pyramid_response
188 188
189 189
190 190 def error_handler(exception, request):
191 191 # TODO: dan: replace the old pylons error controller with this
192 192 from rhodecode.model.settings import SettingsModel
193 193 from rhodecode.lib.utils2 import AttributeDict
194 194
195 195 try:
196 196 rc_config = SettingsModel().get_all_settings()
197 197 except Exception:
198 198 log.exception('failed to fetch settings')
199 199 rc_config = {}
200 200
201 201 base_response = HTTPInternalServerError()
202 202 # prefer original exception for the response since it may have headers set
203 203 if isinstance(exception, HTTPError):
204 204 base_response = exception
205 205
206 206 c = AttributeDict()
207 207 c.error_message = base_response.status
208 208 c.error_explanation = base_response.explanation or str(base_response)
209 209 c.visual = AttributeDict()
210 210
211 211 c.visual.rhodecode_support_url = (
212 212 request.registry.settings.get('rhodecode_support_url') or
213 213 request.route_url('rhodecode_support')
214 214 )
215 215 c.redirect_time = 0
216 216 c.rhodecode_name = rc_config.get('rhodecode_title', '')
217 217 if not c.rhodecode_name:
218 218 c.rhodecode_name = 'Rhodecode'
219 219
220 220 response = render_to_response(
221 221 '/errors/error_document.html', {'c': c}, request=request,
222 222 response=base_response)
223 223
224 224 return response
225 225
226 226
227 227 def includeme(config):
228 228 settings = config.registry.settings
229 229
230 230 # plugin information
231 231 config.registry.rhodecode_plugins = {}
232 232
233 233 config.add_directive(
234 234 'register_rhodecode_plugin', register_rhodecode_plugin)
235 235
236 236 if asbool(settings.get('appenlight', 'false')):
237 237 config.include('appenlight_client.ext.pyramid_tween')
238 238
239 239 # Includes which are required. The application would fail without them.
240 240 config.include('pyramid_mako')
241 241 config.include('pyramid_beaker')
242 242 config.include('rhodecode.admin')
243 243 config.include('rhodecode.authentication')
244 244 config.include('rhodecode.integrations')
245 245 config.include('rhodecode.login')
246 246 config.include('rhodecode.tweens')
247 247 config.include('rhodecode.api')
248 248 config.add_route(
249 249 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
250 250
251 251 # Set the authorization policy.
252 252 authz_policy = ACLAuthorizationPolicy()
253 253 config.set_authorization_policy(authz_policy)
254 254
255 255 # Set the default renderer for HTML templates to mako.
256 256 config.add_mako_renderer('.html')
257 257
258 258 # include RhodeCode plugins
259 259 includes = aslist(settings.get('rhodecode.includes', []))
260 260 for inc in includes:
261 261 config.include(inc)
262 262
263 263 pylons_app = make_app(
264 264 config.registry._pylons_compat_global_config,
265 265 **config.registry._pylons_compat_settings)
266 266 config.registry._pylons_compat_config = pylons_app.config
267 267
268 268 pylons_app_as_view = wsgiapp(pylons_app)
269 269
270 270 # Protect from VCS Server error related pages when server is not available
271 271 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
272 272 if not vcs_server_enabled:
273 273 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
274 274
275 275
276 276 def pylons_app_with_error_handler(context, request):
277 277 """
278 278 Handle exceptions from rc pylons app:
279 279
280 280 - old webob type exceptions get converted to pyramid exceptions
281 281 - pyramid exceptions are passed to the error handler view
282 282 """
283 283 try:
284 284 response = pylons_app_as_view(context, request)
285 285 if 400 <= response.status_int <= 599: # webob type error responses
286 286 return error_handler(
287 287 webob_to_pyramid_http_response(response), request)
288 288 except HTTPError as e: # pyramid type exceptions
289 289 return error_handler(e, request)
290 290 except Exception:
291 291 if settings.get('debugtoolbar.enabled', False):
292 292 raise
293 293 return error_handler(HTTPInternalServerError(), request)
294 294 return response
295 295
296 296 # This is the glue which allows us to migrate in chunks. By registering the
297 297 # pylons based application as the "Not Found" view in Pyramid, we will
298 298 # fallback to the old application each time the new one does not yet know
299 299 # how to handle a request.
300 300 config.add_notfound_view(pylons_app_with_error_handler)
301 301
302 302 if not settings.get('debugtoolbar.enabled', False):
303 303 # if no toolbar, then any exception gets caught and rendered
304 304 config.add_view(error_handler, context=Exception)
305 305
306 306 config.add_view(error_handler, context=HTTPError)
307 307
308 308
309 309 def includeme_first(config):
310 310 # redirect automatic browser favicon.ico requests to correct place
311 311 def favicon_redirect(context, request):
312 312 return redirect(
313 313 request.static_url('rhodecode:public/images/favicon.ico'))
314 314
315 315 config.add_view(favicon_redirect, route_name='favicon')
316 316 config.add_route('favicon', '/favicon.ico')
317 317
318 318 config.add_static_view(
319 '_static/deform', 'deform:static')
320 config.add_static_view(
319 321 '_static', path='rhodecode:public', cache_max_age=3600 * 24)
320 322
321
322 323 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
323 324 """
324 325 Apply outer WSGI middlewares around the application.
325 326
326 327 Part of this has been moved up from the Pylons layer, so that the
327 328 data is also available if old Pylons code is hit through an already ported
328 329 view.
329 330 """
330 331 settings = config.registry.settings
331 332
332 333 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
333 334 pyramid_app = HttpsFixup(pyramid_app, settings)
334 335
335 336 # Add RoutesMiddleware to support the pylons compatibility tween during
336 337 # migration to pyramid.
337 338 pyramid_app = SkippableRoutesMiddleware(
338 339 pyramid_app, config.registry._pylons_compat_config['routes.map'],
339 340 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
340 341
341 342 if asbool(settings.get('appenlight', 'false')):
342 343 pyramid_app, _ = wrap_in_appenlight_if_enabled(
343 344 pyramid_app, config.registry._pylons_compat_config)
344 345
345 346 if asbool(settings.get('gzip_responses', 'true')):
346 347 pyramid_app = make_gzip_middleware(
347 348 pyramid_app, settings, compress_level=1)
348 349
349 350 return pyramid_app
350 351
351 352
352 353 def sanitize_settings_and_apply_defaults(settings):
353 354 """
354 355 Applies settings defaults and does all type conversion.
355 356
356 357 We would move all settings parsing and preparation into this place, so that
357 358 we have only one place left which deals with this part. The remaining parts
358 359 of the application would start to rely fully on well prepared settings.
359 360
360 361 This piece would later be split up per topic to avoid a big fat monster
361 362 function.
362 363 """
363 364
364 365 # Pyramid's mako renderer has to search in the templates folder so that the
365 366 # old templates still work. Ported and new templates are expected to use
366 367 # real asset specifications for the includes.
367 368 mako_directories = settings.setdefault('mako.directories', [
368 369 # Base templates of the original Pylons application
369 370 'rhodecode:templates',
370 371 ])
371 372 log.debug(
372 373 "Using the following Mako template directories: %s",
373 374 mako_directories)
374 375
375 376 # Default includes, possible to change as a user
376 377 pyramid_includes = settings.setdefault('pyramid.includes', [
377 378 'rhodecode.lib.middleware.request_wrapper',
378 379 ])
379 380 log.debug(
380 381 "Using the following pyramid.includes: %s",
381 382 pyramid_includes)
382 383
383 384 # TODO: johbo: Re-think this, usually the call to config.include
384 385 # should allow to pass in a prefix.
385 386 settings.setdefault('rhodecode.api.url', '/_admin/api')
386 387
387 388 _bool_setting(settings, 'vcs.server.enable', 'true')
388 389 _bool_setting(settings, 'is_test', 'false')
389 390
390 391 return settings
391 392
392 393
393 394 def _bool_setting(settings, name, default):
394 395 settings[name] = asbool(settings.get(name, default))
@@ -1,57 +1,58 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22
22 23 from rhodecode.integrations.registry import IntegrationTypeRegistry
23 24 from rhodecode.integrations.types import webhook, slack
24 25
25 26 log = logging.getLogger(__name__)
26 27
27 28
28 29 # TODO: dan: This is currently global until we figure out what to do about
29 30 # VCS's not having a pyramid context - move it to pyramid app configuration
30 31 # includeme level later to allow per instance integration setup
31 32 integration_type_registry = IntegrationTypeRegistry()
32 33
33 34 integration_type_registry.register_integration_type(
34 35 webhook.WebhookIntegrationType)
35 36 integration_type_registry.register_integration_type(
36 37 slack.SlackIntegrationType)
37 38
38 39
39 40 def integrations_event_handler(event):
40 41 """
41 42 Takes an event and passes it to all enabled integrations
42 43 """
43 44 from rhodecode.model.integration import IntegrationModel
44 45
45 46 integration_model = IntegrationModel()
46 47 integrations = integration_model.get_for_event(event)
47 48 for integration in integrations:
48 49 try:
49 50 integration_model.send_event(integration, event)
50 51 except Exception:
51 52 log.exception(
52 53 'failure occured when sending event %s to integration %s' % (
53 54 event, integration))
54 55
55 56
56 57 def includeme(config):
57 58 config.include('rhodecode.integrations.routes')
@@ -1,48 +1,45 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import colander
22 22
23 23 from rhodecode.translation import lazy_ugettext
24 24
25 25
26 26 class IntegrationSettingsSchemaBase(colander.MappingSchema):
27 27 """
28 28 This base schema is intended for use in integrations.
29 29 It adds a few default settings (e.g., "enabled"), so that integration
30 30 authors don't have to maintain a bunch of boilerplate.
31 31 """
32 32 enabled = colander.SchemaNode(
33 33 colander.Bool(),
34 34 default=True,
35 35 description=lazy_ugettext('Enable or disable this integration.'),
36 36 missing=False,
37 37 title=lazy_ugettext('Enabled'),
38 widget='bool',
39 38 )
40 39
41 40 name = colander.SchemaNode(
42 41 colander.String(),
43 42 description=lazy_ugettext('Short name for this integration.'),
44 43 missing=colander.required,
45 44 title=lazy_ugettext('Integration name'),
46 widget='string',
47 45 )
48
@@ -1,43 +1,42 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
22 22
23 23
24 24 class IntegrationTypeBase(object):
25 25 """ Base class for IntegrationType plugins """
26 26
27 27 def __init__(self, settings):
28 28 """
29 29 :param settings: dict of settings to be used for the integration
30 30 """
31 31 self.settings = settings
32 32
33 33
34 @classmethod
35 def settings_schema(cls):
34 def settings_schema(self):
36 35 """
37 36 A colander schema of settings for the integration type
38 37
39 38 Subclasses can return their own schema but should always
40 39 inherit from IntegrationSettingsSchemaBase
41 40 """
42 41 return IntegrationSettingsSchemaBase()
43 42
@@ -1,246 +1,253 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 from __future__ import unicode_literals
22
22 import deform
23 23 import re
24 24 import logging
25 25 import requests
26 26 import colander
27 27 import textwrap
28 28 from celery.task import task
29 29 from mako.template import Template
30 30
31 31 from rhodecode import events
32 32 from rhodecode.translation import lazy_ugettext
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.lib.celerylib import run_task
35 35 from rhodecode.lib.colander_utils import strip_whitespace
36 36 from rhodecode.integrations.types.base import IntegrationTypeBase
37 37 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
38 38
39 39 log = logging.getLogger(__name__)
40 40
41 41
42 42 class SlackSettingsSchema(IntegrationSettingsSchemaBase):
43 43 service = colander.SchemaNode(
44 44 colander.String(),
45 45 title=lazy_ugettext('Slack service URL'),
46 46 description=h.literal(lazy_ugettext(
47 47 'This can be setup at the '
48 48 '<a href="https://my.slack.com/services/new/incoming-webhook/">'
49 49 'slack app manager</a>')),
50 50 default='',
51 placeholder='https://hooks.slack.com/services/...',
52 51 preparer=strip_whitespace,
53 52 validator=colander.url,
54 widget='string'
53 widget=deform.widget.TextInputWidget(
54 placeholder='https://hooks.slack.com/services/...',
55 ),
55 56 )
56 57 username = colander.SchemaNode(
57 58 colander.String(),
58 59 title=lazy_ugettext('Username'),
59 60 description=lazy_ugettext('Username to show notifications coming from.'),
60 61 missing='Rhodecode',
61 62 preparer=strip_whitespace,
62 widget='string',
63 placeholder='Rhodecode'
63 widget=deform.widget.TextInputWidget(
64 placeholder='Rhodecode'
65 ),
64 66 )
65 67 channel = colander.SchemaNode(
66 68 colander.String(),
67 69 title=lazy_ugettext('Channel'),
68 70 description=lazy_ugettext('Channel to send notifications to.'),
69 71 missing='',
70 72 preparer=strip_whitespace,
71 widget='string',
72 placeholder='#general'
73 widget=deform.widget.TextInputWidget(
74 placeholder='#general'
75 ),
73 76 )
74 77 icon_emoji = colander.SchemaNode(
75 78 colander.String(),
76 79 title=lazy_ugettext('Emoji'),
77 80 description=lazy_ugettext('Emoji to use eg. :studio_microphone:'),
78 81 missing='',
79 82 preparer=strip_whitespace,
80 widget='string',
81 placeholder=':studio_microphone:'
83 widget=deform.widget.TextInputWidget(
84 placeholder=':studio_microphone:'
85 ),
82 86 )
83 87
84 88
85 89 repo_push_template = Template(r'''
86 90 *${data['actor']['username']}* pushed to \
87 91 %if data['push']['branches']:
88 92 ${len(data['push']['branches']) > 1 and 'branches' or 'branch'} \
89 93 ${', '.join('<%s|%s>' % (branch['url'], branch['name']) for branch in data['push']['branches'])} \
90 94 %else:
91 95 unknown branch \
92 96 %endif
93 97 in <${data['repo']['url']}|${data['repo']['repo_name']}>
94 98 >>>
95 99 %for commit in data['push']['commits']:
96 100 <${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
97 101 %endfor
98 102 ''')
99 103
100 104
101 105 class SlackIntegrationType(IntegrationTypeBase):
102 106 key = 'slack'
103 107 display_name = lazy_ugettext('Slack')
104 108 SettingsSchema = SlackSettingsSchema
105 109 valid_events = [
106 110 events.PullRequestCloseEvent,
107 111 events.PullRequestMergeEvent,
108 112 events.PullRequestUpdateEvent,
109 113 events.PullRequestCommentEvent,
110 114 events.PullRequestReviewEvent,
111 115 events.PullRequestCreateEvent,
112 116 events.RepoPushEvent,
113 117 events.RepoCreateEvent,
114 118 ]
115 119
116 120 def send_event(self, event):
117 121 if event.__class__ not in self.valid_events:
118 122 log.debug('event not valid: %r' % event)
119 123 return
120 124
121 125 if event.name not in self.settings['events']:
122 126 log.debug('event ignored: %r' % event)
123 127 return
124 128
125 129 data = event.as_dict()
126 130
127 131 text = '*%s* caused a *%s* event' % (
128 132 data['actor']['username'], event.name)
129 133
130 134 log.debug('handling slack event for %s' % event.name)
131 135
132 136 if isinstance(event, events.PullRequestCommentEvent):
133 137 text = self.format_pull_request_comment_event(event, data)
134 138 elif isinstance(event, events.PullRequestReviewEvent):
135 139 text = self.format_pull_request_review_event(event, data)
136 140 elif isinstance(event, events.PullRequestEvent):
137 141 text = self.format_pull_request_event(event, data)
138 142 elif isinstance(event, events.RepoPushEvent):
139 143 text = self.format_repo_push_event(data)
140 144 elif isinstance(event, events.RepoCreateEvent):
141 145 text = self.format_repo_create_event(data)
142 146 else:
143 147 log.error('unhandled event type: %r' % event)
144 148
145 149 run_task(post_text_to_slack, self.settings, text)
146 150
147 @classmethod
148 def settings_schema(cls):
151 def settings_schema(self):
149 152 schema = SlackSettingsSchema()
150 153 schema.add(colander.SchemaNode(
151 154 colander.Set(),
152 widget='checkbox_list',
153 choices=sorted([e.name for e in cls.valid_events]),
155 widget=deform.widget.CheckboxChoiceWidget(
156 values=sorted(
157 [(e.name, e.display_name) for e in self.valid_events]
158 )
159 ),
154 160 description="Events activated for this integration",
155 161 name='events'
156 162 ))
163
157 164 return schema
158 165
159 166 def format_pull_request_comment_event(self, event, data):
160 167 comment_text = data['comment']['text']
161 168 if len(comment_text) > 200:
162 169 comment_text = '<{comment_url}|{comment_text}...>'.format(
163 170 comment_text=comment_text[:200],
164 171 comment_url=data['comment']['url'],
165 172 )
166 173
167 174 comment_status = ''
168 175 if data['comment']['status']:
169 176 comment_status = '[{}]: '.format(data['comment']['status'])
170 177
171 178 return (textwrap.dedent(
172 179 '''
173 180 {user} commented on pull request <{pr_url}|#{number}> - {pr_title}:
174 181 >>> {comment_status}{comment_text}
175 182 ''').format(
176 183 comment_status=comment_status,
177 184 user=data['actor']['username'],
178 185 number=data['pullrequest']['pull_request_id'],
179 186 pr_url=data['pullrequest']['url'],
180 187 pr_status=data['pullrequest']['status'],
181 188 pr_title=data['pullrequest']['title'],
182 189 comment_text=comment_text
183 190 )
184 191 )
185 192
186 193 def format_pull_request_review_event(self, event, data):
187 194 return (textwrap.dedent(
188 195 '''
189 196 Status changed to {pr_status} for pull request <{pr_url}|#{number}> - {pr_title}
190 197 ''').format(
191 198 user=data['actor']['username'],
192 199 number=data['pullrequest']['pull_request_id'],
193 200 pr_url=data['pullrequest']['url'],
194 201 pr_status=data['pullrequest']['status'],
195 202 pr_title=data['pullrequest']['title'],
196 203 )
197 204 )
198 205
199 206 def format_pull_request_event(self, event, data):
200 207 action = {
201 208 events.PullRequestCloseEvent: 'closed',
202 209 events.PullRequestMergeEvent: 'merged',
203 210 events.PullRequestUpdateEvent: 'updated',
204 211 events.PullRequestCreateEvent: 'created',
205 212 }.get(event.__class__, str(event.__class__))
206 213
207 214 return ('Pull request <{url}|#{number}> - {title} '
208 215 '{action} by {user}').format(
209 216 user=data['actor']['username'],
210 217 number=data['pullrequest']['pull_request_id'],
211 218 url=data['pullrequest']['url'],
212 219 title=data['pullrequest']['title'],
213 220 action=action
214 221 )
215 222
216 223 def format_repo_push_event(self, data):
217 224 result = repo_push_template.render(
218 225 data=data,
219 226 html_to_slack_links=html_to_slack_links,
220 227 )
221 228 return result
222 229
223 230 def format_repo_create_event(self, data):
224 231 return '<{}|{}> ({}) repository created by *{}*'.format(
225 232 data['repo']['url'],
226 233 data['repo']['repo_name'],
227 234 data['repo']['repo_type'],
228 235 data['actor']['username'],
229 236 )
230 237
231 238
232 239 def html_to_slack_links(message):
233 240 return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(
234 241 r'<\1|\2>', message)
235 242
236 243
237 244 @task(ignore_result=True)
238 245 def post_text_to_slack(settings, text):
239 246 log.debug('sending %s to slack %s' % (text, settings['service']))
240 247 resp = requests.post(settings['service'], json={
241 248 "channel": settings.get('channel', ''),
242 249 "username": settings.get('username', 'Rhodecode'),
243 250 "text": text,
244 251 "icon_emoji": settings.get('icon_emoji', ':studio_microphone:')
245 252 })
246 253 resp.raise_for_status() # raise exception on a failed request
@@ -1,106 +1,111 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 from __future__ import unicode_literals
22 22
23 import deform
23 24 import logging
24 25 import requests
25 26 import colander
26 27 from celery.task import task
27 28 from mako.template import Template
28 29
29 30 from rhodecode import events
30 31 from rhodecode.translation import lazy_ugettext
31 32 from rhodecode.integrations.types.base import IntegrationTypeBase
32 33 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
33 34
34 35 log = logging.getLogger(__name__)
35 36
36 37
37 38 class WebhookSettingsSchema(IntegrationSettingsSchemaBase):
38 39 url = colander.SchemaNode(
39 40 colander.String(),
40 41 title=lazy_ugettext('Webhook URL'),
41 42 description=lazy_ugettext('URL of the webhook to receive POST event.'),
42 43 default='',
43 44 validator=colander.url,
44 placeholder='https://www.example.com/webhook',
45 widget='string'
45 widget=deform.widget.TextInputWidget(
46 placeholder='https://www.example.com/webhook'
47 ),
46 48 )
47 49 secret_token = colander.SchemaNode(
48 50 colander.String(),
49 51 title=lazy_ugettext('Secret Token'),
50 52 description=lazy_ugettext('String used to validate received payloads.'),
51 53 default='',
52 placeholder='secret_token',
53 widget='string'
54 widget=deform.widget.TextInputWidget(
55 placeholder='secret_token'
56 ),
54 57 )
55 58
56 59
57 60 class WebhookIntegrationType(IntegrationTypeBase):
58 61 key = 'webhook'
59 62 display_name = lazy_ugettext('Webhook')
60 63 valid_events = [
61 64 events.PullRequestCloseEvent,
62 65 events.PullRequestMergeEvent,
63 66 events.PullRequestUpdateEvent,
64 67 events.PullRequestCommentEvent,
65 68 events.PullRequestReviewEvent,
66 69 events.PullRequestCreateEvent,
67 70 events.RepoPushEvent,
68 71 events.RepoCreateEvent,
69 72 ]
70 73
71 @classmethod
72 def settings_schema(cls):
74 def settings_schema(self):
73 75 schema = WebhookSettingsSchema()
74 76 schema.add(colander.SchemaNode(
75 77 colander.Set(),
76 widget='checkbox_list',
77 choices=sorted([e.name for e in cls.valid_events]),
78 widget=deform.widget.CheckboxChoiceWidget(
79 values=sorted(
80 [(e.name, e.display_name) for e in self.valid_events]
81 )
82 ),
78 83 description="Events activated for this integration",
79 84 name='events'
80 85 ))
81 86 return schema
82 87
83 88 def send_event(self, event):
84 89 log.debug('handling event %s with webhook integration %s',
85 90 event.name, self)
86 91
87 92 if event.__class__ not in self.valid_events:
88 93 log.debug('event not valid: %r' % event)
89 94 return
90 95
91 96 if event.name not in self.settings['events']:
92 97 log.debug('event ignored: %r' % event)
93 98 return
94 99
95 100 data = event.as_dict()
96 101 post_to_webhook(data, self.settings)
97 102
98 103
99 104 @task(ignore_result=True)
100 105 def post_to_webhook(data, settings):
101 106 log.debug('sending event:%s to webhook %s', data['name'], settings['url'])
102 107 resp = requests.post(settings['url'], json={
103 108 'token': settings['secret_token'],
104 109 'event': data
105 110 })
106 111 resp.raise_for_status() # raise exception on a failed request
@@ -1,258 +1,272 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import colander
22 22 import logging
23 23 import pylons
24 import deform
24 25
25 26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 27 from pyramid.renderers import render
27 28 from pyramid.response import Response
28 29
29 30 from rhodecode.lib import auth
30 31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 32 from rhodecode.model.db import Repository, Session, Integration
32 33 from rhodecode.model.scm import ScmModel
33 34 from rhodecode.model.integration import IntegrationModel
34 35 from rhodecode.admin.navigation import navigation_list
35 36 from rhodecode.translation import _
36 37 from rhodecode.integrations import integration_type_registry
37 38
38 39 log = logging.getLogger(__name__)
39 40
40 41
41 42 class IntegrationSettingsViewBase(object):
42 43 """ Base Integration settings view used by both repo / global settings """
43 44
44 45 def __init__(self, context, request):
45 46 self.context = context
46 47 self.request = request
47 48 self._load_general_context()
48 49
49 50 if not self.perm_check(request.user):
50 51 raise HTTPForbidden()
51 52
52 53 def _load_general_context(self):
53 54 """
54 55 This avoids boilerplate for repo/global+list/edit+views/templates
55 56 by doing all possible contexts at the same time however it should
56 57 be split up into separate functions once more "contexts" exist
57 58 """
58 59
59 60 self.IntegrationType = None
60 61 self.repo = None
61 62 self.integration = None
62 63 self.integrations = {}
63 64
64 65 request = self.request
65 66
66 67 if 'repo_name' in request.matchdict: # we're in a repo context
67 68 repo_name = request.matchdict['repo_name']
68 69 self.repo = Repository.get_by_repo_name(repo_name)
69 70
70 71 if 'integration' in request.matchdict: # we're in integration context
71 72 integration_type = request.matchdict['integration']
72 73 self.IntegrationType = integration_type_registry[integration_type]
73 74
74 75 if 'integration_id' in request.matchdict: # single integration context
75 76 integration_id = request.matchdict['integration_id']
76 77 self.integration = Integration.get(integration_id)
77 78 else: # list integrations context
78 79 for integration in IntegrationModel().get_integrations(self.repo):
79 80 self.integrations.setdefault(integration.integration_type, []
80 81 ).append(integration)
81 82
82 83 self.settings = self.integration and self.integration.settings or {}
83 84
84 85 def _template_c_context(self):
85 86 # TODO: dan: this is a stopgap in order to inherit from current pylons
86 87 # based admin/repo settings templates - this should be removed entirely
87 88 # after port to pyramid
88 89
89 90 c = pylons.tmpl_context
90 91 c.active = 'integrations'
91 92 c.rhodecode_user = self.request.user
92 93 c.repo = self.repo
93 94 c.repo_name = self.repo and self.repo.repo_name or None
94 95 if self.repo:
95 96 c.repo_info = self.repo
96 97 c.rhodecode_db_repo = self.repo
97 98 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
98 99 else:
99 100 c.navlist = navigation_list(self.request)
100 101
101 102 return c
102 103
103 104 def _form_schema(self):
104 return self.IntegrationType.settings_schema()
105 if self.integration:
106 settings = self.integration.settings
107 else:
108 settings = {}
109 return self.IntegrationType(settings=settings).settings_schema()
105 110
106 def settings_get(self, defaults=None, errors=None):
111 def settings_get(self, defaults=None, errors=None, form=None):
107 112 """
108 113 View that displays the plugin settings as a form.
109 114 """
110 115 defaults = defaults or {}
111 116 errors = errors or {}
112 117
113 schema = self._form_schema()
114
115 if not defaults:
116 if self.integration:
117 defaults['enabled'] = self.integration.enabled
118 defaults['name'] = self.integration.name
118 if self.integration:
119 defaults = self.integration.settings or {}
120 defaults['name'] = self.integration.name
121 defaults['enabled'] = self.integration.enabled
122 else:
123 if self.repo:
124 scope = self.repo.repo_name
119 125 else:
120 if self.repo:
121 scope = self.repo.repo_name
122 else:
123 scope = _('Global')
126 scope = _('Global')
127
128 defaults['name'] = '{} {} integration'.format(scope,
129 self.IntegrationType.display_name)
130 defaults['enabled'] = True
124 131
125 defaults['name'] = '{} {} integration'.format(scope,
126 self.IntegrationType.display_name)
127 defaults['enabled'] = True
132 schema = self._form_schema().bind(request=self.request)
133
134 if self.integration:
135 buttons = ('submit', 'delete')
136 else:
137 buttons = ('submit',)
138
139 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
128 140
129 141 for node in schema:
130 142 setting = self.settings.get(node.name)
131 143 if setting is not None:
132 144 defaults.setdefault(node.name, setting)
133 145 else:
134 146 if node.default:
135 147 defaults.setdefault(node.name, node.default)
136 148
137 149 template_context = {
150 'form': form,
138 151 'defaults': defaults,
139 152 'errors': errors,
140 153 'schema': schema,
141 154 'current_IntegrationType': self.IntegrationType,
142 155 'integration': self.integration,
143 156 'settings': self.settings,
144 157 'resource': self.context,
145 158 'c': self._template_c_context(),
146 159 }
147 160
148 161 return template_context
149 162
150 163 @auth.CSRFRequired()
151 164 def settings_post(self):
152 165 """
153 166 View that validates and stores the plugin settings.
154 167 """
155 168 if self.request.params.get('delete'):
156 169 Session().delete(self.integration)
157 170 Session().commit()
158 171 self.request.session.flash(
159 172 _('Integration {integration_name} deleted successfully.').format(
160 173 integration_name=self.integration.name),
161 174 queue='success')
162 175 if self.repo:
163 176 redirect_to = self.request.route_url(
164 177 'repo_integrations_home', repo_name=self.repo.repo_name)
165 178 else:
166 179 redirect_to = self.request.route_url('global_integrations_home')
167 180 raise HTTPFound(redirect_to)
168 181
169 schema = self._form_schema()
182 schema = self._form_schema().bind(request=self.request)
183
184 form = deform.Form(schema, buttons=('submit', 'delete'))
170 185
171 186 params = {}
172 187 for node in schema.children:
173 188 if type(node.typ) in (colander.Set, colander.List):
174 189 val = self.request.params.getall(node.name)
175 190 else:
176 191 val = self.request.params.get(node.name)
177 192 if val:
178 193 params[node.name] = val
179 194
195 controls = self.request.POST.items()
180 196 try:
181 valid_data = schema.deserialize(params)
182 except colander.Invalid as e:
183 # Display error message and display form again.
197 valid_data = form.validate(controls)
198 except deform.ValidationFailure as e:
184 199 self.request.session.flash(
185 _('Errors exist when saving plugin settings. '
200 _('Errors exist when saving integration settings. '
186 201 'Please check the form inputs.'),
187 202 queue='error')
188 return self.settings_get(errors=e.asdict(), defaults=params)
203 return self.settings_get(errors={}, defaults=params, form=e)
189 204
190 205 if not self.integration:
191 206 self.integration = Integration()
192 207 self.integration.integration_type = self.IntegrationType.key
193 208 if self.repo:
194 209 self.integration.repo = self.repo
195 210 Session().add(self.integration)
196 211
197 212 self.integration.enabled = valid_data.pop('enabled', False)
198 213 self.integration.name = valid_data.pop('name')
199 214 self.integration.settings = valid_data
200 215
201 216 Session().commit()
202 217
203 218 # Display success message and redirect.
204 219 self.request.session.flash(
205 220 _('Integration {integration_name} updated successfully.').format(
206 221 integration_name=self.IntegrationType.display_name),
207 222 queue='success')
208 223
209 224 if self.repo:
210 225 redirect_to = self.request.route_url(
211 226 'repo_integrations_edit', repo_name=self.repo.repo_name,
212 227 integration=self.integration.integration_type,
213 228 integration_id=self.integration.integration_id)
214 229 else:
215 230 redirect_to = self.request.route_url(
216 231 'global_integrations_edit',
217 232 integration=self.integration.integration_type,
218 233 integration_id=self.integration.integration_id)
219 234
220 235 return HTTPFound(redirect_to)
221 236
222 237 def index(self):
223 238 current_integrations = self.integrations
224 239 if self.IntegrationType:
225 240 current_integrations = {
226 241 self.IntegrationType.key: self.integrations.get(
227 242 self.IntegrationType.key, [])
228 243 }
229 244
230 245 template_context = {
231 246 'current_IntegrationType': self.IntegrationType,
232 247 'current_integrations': current_integrations,
233 'current_integration': 'none',
234 248 'available_integrations': integration_type_registry,
235 249 'c': self._template_c_context()
236 250 }
237 251
238 252 if self.repo:
239 253 html = render('rhodecode:templates/admin/integrations/list.html',
240 254 template_context,
241 255 request=self.request)
242 256 else:
243 257 html = render('rhodecode:templates/admin/integrations/list.html',
244 258 template_context,
245 259 request=self.request)
246 260
247 261 return Response(html)
248 262
249 263
250 264 class GlobalIntegrationsView(IntegrationSettingsViewBase):
251 265 def perm_check(self, user):
252 266 return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
253 267
254 268
255 269 class RepoIntegrationsView(IntegrationSettingsViewBase):
256 270 def perm_check(self, user):
257 271 return auth.HasRepoPermissionAll('repository.admin'
258 272 )(repo_name=self.repo.repo_name, user=user)
@@ -1,1947 +1,1962 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Helper functions
23 23
24 24 Consists of functions to typically be used within templates, but also
25 25 available to Controllers. This module is available to both as 'h'.
26 26 """
27 27
28 28 import random
29 29 import hashlib
30 30 import StringIO
31 31 import urllib
32 32 import math
33 33 import logging
34 34 import re
35 35 import urlparse
36 36 import time
37 37 import string
38 38 import hashlib
39 39 import pygments
40 40
41 41 from datetime import datetime
42 42 from functools import partial
43 43 from pygments.formatters.html import HtmlFormatter
44 44 from pygments import highlight as code_highlight
45 45 from pygments.lexers import (
46 46 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
47 47 from pylons import url as pylons_url
48 48 from pylons.i18n.translation import _, ungettext
49 49 from pyramid.threadlocal import get_current_request
50 50
51 51 from webhelpers.html import literal, HTML, escape
52 52 from webhelpers.html.tools import *
53 53 from webhelpers.html.builder import make_tag
54 54 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
55 55 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
56 56 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
57 57 submit, text, password, textarea, title, ul, xml_declaration, radio
58 58 from webhelpers.html.tools import auto_link, button_to, highlight, \
59 59 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
60 60 from webhelpers.pylonslib import Flash as _Flash
61 61 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
62 62 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
63 63 replace_whitespace, urlify, truncate, wrap_paragraphs
64 64 from webhelpers.date import time_ago_in_words
65 65 from webhelpers.paginate import Page as _Page
66 66 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
67 67 convert_boolean_attrs, NotGiven, _make_safe_id_component
68 68 from webhelpers2.number import format_byte_size
69 69
70 70 from rhodecode.lib.annotate import annotate_highlight
71 71 from rhodecode.lib.action_parser import action_parser
72 72 from rhodecode.lib.ext_json import json
73 73 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
74 74 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
75 75 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
76 76 AttributeDict, safe_int, md5, md5_safe
77 77 from rhodecode.lib.markup_renderer import MarkupRenderer
78 78 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
79 79 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
80 80 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
81 81 from rhodecode.model.changeset_status import ChangesetStatusModel
82 82 from rhodecode.model.db import Permission, User, Repository
83 83 from rhodecode.model.repo_group import RepoGroupModel
84 84 from rhodecode.model.settings import IssueTrackerSettingsModel
85 85
86 86 log = logging.getLogger(__name__)
87 87
88 88
89 89 DEFAULT_USER = User.DEFAULT_USER
90 90 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
91 91
92 92
93 93 def url(*args, **kw):
94 94 return pylons_url(*args, **kw)
95 95
96 96
97 97 def pylons_url_current(*args, **kw):
98 98 """
99 99 This function overrides pylons.url.current() which returns the current
100 100 path so that it will also work from a pyramid only context. This
101 101 should be removed once port to pyramid is complete.
102 102 """
103 103 if not args and not kw:
104 104 request = get_current_request()
105 105 return request.path
106 106 return pylons_url.current(*args, **kw)
107 107
108 108 url.current = pylons_url_current
109 109
110 110
111 111 def asset(path, ver=None):
112 112 """
113 113 Helper to generate a static asset file path for rhodecode assets
114 114
115 115 eg. h.asset('images/image.png', ver='3923')
116 116
117 117 :param path: path of asset
118 118 :param ver: optional version query param to append as ?ver=
119 119 """
120 120 request = get_current_request()
121 121 return request.static_url('rhodecode:public/{}'.format(path), ver=ver)
122 122
123 123
124 124 def html_escape(text, html_escape_table=None):
125 125 """Produce entities within text."""
126 126 if not html_escape_table:
127 127 html_escape_table = {
128 128 "&": "&amp;",
129 129 '"': "&quot;",
130 130 "'": "&apos;",
131 131 ">": "&gt;",
132 132 "<": "&lt;",
133 133 }
134 134 return "".join(html_escape_table.get(c, c) for c in text)
135 135
136 136
137 137 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
138 138 """
139 139 Truncate string ``s`` at the first occurrence of ``sub``.
140 140
141 141 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
142 142 """
143 143 suffix_if_chopped = suffix_if_chopped or ''
144 144 pos = s.find(sub)
145 145 if pos == -1:
146 146 return s
147 147
148 148 if inclusive:
149 149 pos += len(sub)
150 150
151 151 chopped = s[:pos]
152 152 left = s[pos:].strip()
153 153
154 154 if left and suffix_if_chopped:
155 155 chopped += suffix_if_chopped
156 156
157 157 return chopped
158 158
159 159
160 160 def shorter(text, size=20):
161 161 postfix = '...'
162 162 if len(text) > size:
163 163 return text[:size - len(postfix)] + postfix
164 164 return text
165 165
166 166
167 167 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
168 168 """
169 169 Reset button
170 170 """
171 171 _set_input_attrs(attrs, type, name, value)
172 172 _set_id_attr(attrs, id, name)
173 173 convert_boolean_attrs(attrs, ["disabled"])
174 174 return HTML.input(**attrs)
175 175
176 176 reset = _reset
177 177 safeid = _make_safe_id_component
178 178
179 179
180 180 def branding(name, length=40):
181 181 return truncate(name, length, indicator="")
182 182
183 183
184 184 def FID(raw_id, path):
185 185 """
186 186 Creates a unique ID for filenode based on it's hash of path and commit
187 187 it's safe to use in urls
188 188
189 189 :param raw_id:
190 190 :param path:
191 191 """
192 192
193 193 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
194 194
195 195
196 196 class _GetError(object):
197 197 """Get error from form_errors, and represent it as span wrapped error
198 198 message
199 199
200 200 :param field_name: field to fetch errors for
201 201 :param form_errors: form errors dict
202 202 """
203 203
204 204 def __call__(self, field_name, form_errors):
205 205 tmpl = """<span class="error_msg">%s</span>"""
206 206 if form_errors and field_name in form_errors:
207 207 return literal(tmpl % form_errors.get(field_name))
208 208
209 209 get_error = _GetError()
210 210
211 211
212 212 class _ToolTip(object):
213 213
214 214 def __call__(self, tooltip_title, trim_at=50):
215 215 """
216 216 Special function just to wrap our text into nice formatted
217 217 autowrapped text
218 218
219 219 :param tooltip_title:
220 220 """
221 221 tooltip_title = escape(tooltip_title)
222 222 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
223 223 return tooltip_title
224 224 tooltip = _ToolTip()
225 225
226 226
227 227 def files_breadcrumbs(repo_name, commit_id, file_path):
228 228 if isinstance(file_path, str):
229 229 file_path = safe_unicode(file_path)
230 230
231 231 # TODO: johbo: Is this always a url like path, or is this operating
232 232 # system dependent?
233 233 path_segments = file_path.split('/')
234 234
235 235 repo_name_html = escape(repo_name)
236 236 if len(path_segments) == 1 and path_segments[0] == '':
237 237 url_segments = [repo_name_html]
238 238 else:
239 239 url_segments = [
240 240 link_to(
241 241 repo_name_html,
242 242 url('files_home',
243 243 repo_name=repo_name,
244 244 revision=commit_id,
245 245 f_path=''),
246 246 class_='pjax-link')]
247 247
248 248 last_cnt = len(path_segments) - 1
249 249 for cnt, segment in enumerate(path_segments):
250 250 if not segment:
251 251 continue
252 252 segment_html = escape(segment)
253 253
254 254 if cnt != last_cnt:
255 255 url_segments.append(
256 256 link_to(
257 257 segment_html,
258 258 url('files_home',
259 259 repo_name=repo_name,
260 260 revision=commit_id,
261 261 f_path='/'.join(path_segments[:cnt + 1])),
262 262 class_='pjax-link'))
263 263 else:
264 264 url_segments.append(segment_html)
265 265
266 266 return literal('/'.join(url_segments))
267 267
268 268
269 269 class CodeHtmlFormatter(HtmlFormatter):
270 270 """
271 271 My code Html Formatter for source codes
272 272 """
273 273
274 274 def wrap(self, source, outfile):
275 275 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
276 276
277 277 def _wrap_code(self, source):
278 278 for cnt, it in enumerate(source):
279 279 i, t = it
280 280 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
281 281 yield i, t
282 282
283 283 def _wrap_tablelinenos(self, inner):
284 284 dummyoutfile = StringIO.StringIO()
285 285 lncount = 0
286 286 for t, line in inner:
287 287 if t:
288 288 lncount += 1
289 289 dummyoutfile.write(line)
290 290
291 291 fl = self.linenostart
292 292 mw = len(str(lncount + fl - 1))
293 293 sp = self.linenospecial
294 294 st = self.linenostep
295 295 la = self.lineanchors
296 296 aln = self.anchorlinenos
297 297 nocls = self.noclasses
298 298 if sp:
299 299 lines = []
300 300
301 301 for i in range(fl, fl + lncount):
302 302 if i % st == 0:
303 303 if i % sp == 0:
304 304 if aln:
305 305 lines.append('<a href="#%s%d" class="special">%*d</a>' %
306 306 (la, i, mw, i))
307 307 else:
308 308 lines.append('<span class="special">%*d</span>' % (mw, i))
309 309 else:
310 310 if aln:
311 311 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
312 312 else:
313 313 lines.append('%*d' % (mw, i))
314 314 else:
315 315 lines.append('')
316 316 ls = '\n'.join(lines)
317 317 else:
318 318 lines = []
319 319 for i in range(fl, fl + lncount):
320 320 if i % st == 0:
321 321 if aln:
322 322 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
323 323 else:
324 324 lines.append('%*d' % (mw, i))
325 325 else:
326 326 lines.append('')
327 327 ls = '\n'.join(lines)
328 328
329 329 # in case you wonder about the seemingly redundant <div> here: since the
330 330 # content in the other cell also is wrapped in a div, some browsers in
331 331 # some configurations seem to mess up the formatting...
332 332 if nocls:
333 333 yield 0, ('<table class="%stable">' % self.cssclass +
334 334 '<tr><td><div class="linenodiv" '
335 335 'style="background-color: #f0f0f0; padding-right: 10px">'
336 336 '<pre style="line-height: 125%">' +
337 337 ls + '</pre></div></td><td id="hlcode" class="code">')
338 338 else:
339 339 yield 0, ('<table class="%stable">' % self.cssclass +
340 340 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
341 341 ls + '</pre></div></td><td id="hlcode" class="code">')
342 342 yield 0, dummyoutfile.getvalue()
343 343 yield 0, '</td></tr></table>'
344 344
345 345
346 346 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
347 347 def __init__(self, **kw):
348 348 # only show these line numbers if set
349 349 self.only_lines = kw.pop('only_line_numbers', [])
350 350 self.query_terms = kw.pop('query_terms', [])
351 351 self.max_lines = kw.pop('max_lines', 5)
352 352 self.line_context = kw.pop('line_context', 3)
353 353 self.url = kw.pop('url', None)
354 354
355 355 super(CodeHtmlFormatter, self).__init__(**kw)
356 356
357 357 def _wrap_code(self, source):
358 358 for cnt, it in enumerate(source):
359 359 i, t = it
360 360 t = '<pre>%s</pre>' % t
361 361 yield i, t
362 362
363 363 def _wrap_tablelinenos(self, inner):
364 364 yield 0, '<table class="code-highlight %stable">' % self.cssclass
365 365
366 366 last_shown_line_number = 0
367 367 current_line_number = 1
368 368
369 369 for t, line in inner:
370 370 if not t:
371 371 yield t, line
372 372 continue
373 373
374 374 if current_line_number in self.only_lines:
375 375 if last_shown_line_number + 1 != current_line_number:
376 376 yield 0, '<tr>'
377 377 yield 0, '<td class="line">...</td>'
378 378 yield 0, '<td id="hlcode" class="code"></td>'
379 379 yield 0, '</tr>'
380 380
381 381 yield 0, '<tr>'
382 382 if self.url:
383 383 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
384 384 self.url, current_line_number, current_line_number)
385 385 else:
386 386 yield 0, '<td class="line"><a href="">%i</a></td>' % (
387 387 current_line_number)
388 388 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
389 389 yield 0, '</tr>'
390 390
391 391 last_shown_line_number = current_line_number
392 392
393 393 current_line_number += 1
394 394
395 395
396 396 yield 0, '</table>'
397 397
398 398
399 399 def extract_phrases(text_query):
400 400 """
401 401 Extracts phrases from search term string making sure phrases
402 402 contained in double quotes are kept together - and discarding empty values
403 403 or fully whitespace values eg.
404 404
405 405 'some text "a phrase" more' => ['some', 'text', 'a phrase', 'more']
406 406
407 407 """
408 408
409 409 in_phrase = False
410 410 buf = ''
411 411 phrases = []
412 412 for char in text_query:
413 413 if in_phrase:
414 414 if char == '"': # end phrase
415 415 phrases.append(buf)
416 416 buf = ''
417 417 in_phrase = False
418 418 continue
419 419 else:
420 420 buf += char
421 421 continue
422 422 else:
423 423 if char == '"': # start phrase
424 424 in_phrase = True
425 425 phrases.append(buf)
426 426 buf = ''
427 427 continue
428 428 elif char == ' ':
429 429 phrases.append(buf)
430 430 buf = ''
431 431 continue
432 432 else:
433 433 buf += char
434 434
435 435 phrases.append(buf)
436 436 phrases = [phrase.strip() for phrase in phrases if phrase.strip()]
437 437 return phrases
438 438
439 439
440 440 def get_matching_offsets(text, phrases):
441 441 """
442 442 Returns a list of string offsets in `text` that the list of `terms` match
443 443
444 444 >>> get_matching_offsets('some text here', ['some', 'here'])
445 445 [(0, 4), (10, 14)]
446 446
447 447 """
448 448 offsets = []
449 449 for phrase in phrases:
450 450 for match in re.finditer(phrase, text):
451 451 offsets.append((match.start(), match.end()))
452 452
453 453 return offsets
454 454
455 455
456 456 def normalize_text_for_matching(x):
457 457 """
458 458 Replaces all non alnum characters to spaces and lower cases the string,
459 459 useful for comparing two text strings without punctuation
460 460 """
461 461 return re.sub(r'[^\w]', ' ', x.lower())
462 462
463 463
464 464 def get_matching_line_offsets(lines, terms):
465 465 """ Return a set of `lines` indices (starting from 1) matching a
466 466 text search query, along with `context` lines above/below matching lines
467 467
468 468 :param lines: list of strings representing lines
469 469 :param terms: search term string to match in lines eg. 'some text'
470 470 :param context: number of lines above/below a matching line to add to result
471 471 :param max_lines: cut off for lines of interest
472 472 eg.
473 473
474 474 text = '''
475 475 words words words
476 476 words words words
477 477 some text some
478 478 words words words
479 479 words words words
480 480 text here what
481 481 '''
482 482 get_matching_line_offsets(text, 'text', context=1)
483 483 {3: [(5, 9)], 6: [(0, 4)]]
484 484
485 485 """
486 486 matching_lines = {}
487 487 phrases = [normalize_text_for_matching(phrase)
488 488 for phrase in extract_phrases(terms)]
489 489
490 490 for line_index, line in enumerate(lines, start=1):
491 491 match_offsets = get_matching_offsets(
492 492 normalize_text_for_matching(line), phrases)
493 493 if match_offsets:
494 494 matching_lines[line_index] = match_offsets
495 495
496 496 return matching_lines
497 497
498 498
499 499 def get_lexer_safe(mimetype=None, filepath=None):
500 500 """
501 501 Tries to return a relevant pygments lexer using mimetype/filepath name,
502 502 defaulting to plain text if none could be found
503 503 """
504 504 lexer = None
505 505 try:
506 506 if mimetype:
507 507 lexer = get_lexer_for_mimetype(mimetype)
508 508 if not lexer:
509 509 lexer = get_lexer_for_filename(filepath)
510 510 except pygments.util.ClassNotFound:
511 511 pass
512 512
513 513 if not lexer:
514 514 lexer = get_lexer_by_name('text')
515 515
516 516 return lexer
517 517
518 518
519 519 def pygmentize(filenode, **kwargs):
520 520 """
521 521 pygmentize function using pygments
522 522
523 523 :param filenode:
524 524 """
525 525 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
526 526 return literal(code_highlight(filenode.content, lexer,
527 527 CodeHtmlFormatter(**kwargs)))
528 528
529 529
530 530 def pygmentize_annotation(repo_name, filenode, **kwargs):
531 531 """
532 532 pygmentize function for annotation
533 533
534 534 :param filenode:
535 535 """
536 536
537 537 color_dict = {}
538 538
539 539 def gen_color(n=10000):
540 540 """generator for getting n of evenly distributed colors using
541 541 hsv color and golden ratio. It always return same order of colors
542 542
543 543 :returns: RGB tuple
544 544 """
545 545
546 546 def hsv_to_rgb(h, s, v):
547 547 if s == 0.0:
548 548 return v, v, v
549 549 i = int(h * 6.0) # XXX assume int() truncates!
550 550 f = (h * 6.0) - i
551 551 p = v * (1.0 - s)
552 552 q = v * (1.0 - s * f)
553 553 t = v * (1.0 - s * (1.0 - f))
554 554 i = i % 6
555 555 if i == 0:
556 556 return v, t, p
557 557 if i == 1:
558 558 return q, v, p
559 559 if i == 2:
560 560 return p, v, t
561 561 if i == 3:
562 562 return p, q, v
563 563 if i == 4:
564 564 return t, p, v
565 565 if i == 5:
566 566 return v, p, q
567 567
568 568 golden_ratio = 0.618033988749895
569 569 h = 0.22717784590367374
570 570
571 571 for _ in xrange(n):
572 572 h += golden_ratio
573 573 h %= 1
574 574 HSV_tuple = [h, 0.95, 0.95]
575 575 RGB_tuple = hsv_to_rgb(*HSV_tuple)
576 576 yield map(lambda x: str(int(x * 256)), RGB_tuple)
577 577
578 578 cgenerator = gen_color()
579 579
580 580 def get_color_string(commit_id):
581 581 if commit_id in color_dict:
582 582 col = color_dict[commit_id]
583 583 else:
584 584 col = color_dict[commit_id] = cgenerator.next()
585 585 return "color: rgb(%s)! important;" % (', '.join(col))
586 586
587 587 def url_func(repo_name):
588 588
589 589 def _url_func(commit):
590 590 author = commit.author
591 591 date = commit.date
592 592 message = tooltip(commit.message)
593 593
594 594 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
595 595 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
596 596 "</b> %s<br/></div>")
597 597
598 598 tooltip_html = tooltip_html % (author, date, message)
599 599 lnk_format = '%5s:%s' % ('r%s' % commit.idx, commit.short_id)
600 600 uri = link_to(
601 601 lnk_format,
602 602 url('changeset_home', repo_name=repo_name,
603 603 revision=commit.raw_id),
604 604 style=get_color_string(commit.raw_id),
605 605 class_='tooltip',
606 606 title=tooltip_html
607 607 )
608 608
609 609 uri += '\n'
610 610 return uri
611 611 return _url_func
612 612
613 613 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
614 614
615 615
616 616 def is_following_repo(repo_name, user_id):
617 617 from rhodecode.model.scm import ScmModel
618 618 return ScmModel().is_following_repo(repo_name, user_id)
619 619
620 620
621 621 class _Message(object):
622 622 """A message returned by ``Flash.pop_messages()``.
623 623
624 624 Converting the message to a string returns the message text. Instances
625 625 also have the following attributes:
626 626
627 627 * ``message``: the message text.
628 628 * ``category``: the category specified when the message was created.
629 629 """
630 630
631 631 def __init__(self, category, message):
632 632 self.category = category
633 633 self.message = message
634 634
635 635 def __str__(self):
636 636 return self.message
637 637
638 638 __unicode__ = __str__
639 639
640 640 def __html__(self):
641 641 return escape(safe_unicode(self.message))
642 642
643 643
644 644 class Flash(_Flash):
645 645
646 646 def pop_messages(self):
647 647 """Return all accumulated messages and delete them from the session.
648 648
649 649 The return value is a list of ``Message`` objects.
650 650 """
651 651 from pylons import session
652 652
653 653 messages = []
654 654
655 655 # Pop the 'old' pylons flash messages. They are tuples of the form
656 656 # (category, message)
657 657 for cat, msg in session.pop(self.session_key, []):
658 658 messages.append(_Message(cat, msg))
659 659
660 660 # Pop the 'new' pyramid flash messages for each category as list
661 661 # of strings.
662 662 for cat in self.categories:
663 663 for msg in session.pop_flash(queue=cat):
664 664 messages.append(_Message(cat, msg))
665 665 # Map messages from the default queue to the 'notice' category.
666 666 for msg in session.pop_flash():
667 667 messages.append(_Message('notice', msg))
668 668
669 669 session.save()
670 670 return messages
671 671
672 672 flash = Flash()
673 673
674 674 #==============================================================================
675 675 # SCM FILTERS available via h.
676 676 #==============================================================================
677 677 from rhodecode.lib.vcs.utils import author_name, author_email
678 678 from rhodecode.lib.utils2 import credentials_filter, age as _age
679 679 from rhodecode.model.db import User, ChangesetStatus
680 680
681 681 age = _age
682 682 capitalize = lambda x: x.capitalize()
683 683 email = author_email
684 684 short_id = lambda x: x[:12]
685 685 hide_credentials = lambda x: ''.join(credentials_filter(x))
686 686
687 687
688 688 def age_component(datetime_iso, value=None, time_is_local=False):
689 689 title = value or format_date(datetime_iso)
690 690
691 691 # detect if we have a timezone info, otherwise, add it
692 692 if isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
693 693 tzinfo = '+00:00'
694 694
695 695 if time_is_local:
696 696 tzinfo = time.strftime("+%H:%M",
697 697 time.gmtime(
698 698 (datetime.now() - datetime.utcnow()).seconds + 1
699 699 )
700 700 )
701 701
702 702 return literal(
703 703 '<time class="timeago tooltip" '
704 704 'title="{1}" datetime="{0}{2}">{1}</time>'.format(
705 705 datetime_iso, title, tzinfo))
706 706
707 707
708 708 def _shorten_commit_id(commit_id):
709 709 from rhodecode import CONFIG
710 710 def_len = safe_int(CONFIG.get('rhodecode_show_sha_length', 12))
711 711 return commit_id[:def_len]
712 712
713 713
714 714 def show_id(commit):
715 715 """
716 716 Configurable function that shows ID
717 717 by default it's r123:fffeeefffeee
718 718
719 719 :param commit: commit instance
720 720 """
721 721 from rhodecode import CONFIG
722 722 show_idx = str2bool(CONFIG.get('rhodecode_show_revision_number', True))
723 723
724 724 raw_id = _shorten_commit_id(commit.raw_id)
725 725 if show_idx:
726 726 return 'r%s:%s' % (commit.idx, raw_id)
727 727 else:
728 728 return '%s' % (raw_id, )
729 729
730 730
731 731 def format_date(date):
732 732 """
733 733 use a standardized formatting for dates used in RhodeCode
734 734
735 735 :param date: date/datetime object
736 736 :return: formatted date
737 737 """
738 738
739 739 if date:
740 740 _fmt = "%a, %d %b %Y %H:%M:%S"
741 741 return safe_unicode(date.strftime(_fmt))
742 742
743 743 return u""
744 744
745 745
746 746 class _RepoChecker(object):
747 747
748 748 def __init__(self, backend_alias):
749 749 self._backend_alias = backend_alias
750 750
751 751 def __call__(self, repository):
752 752 if hasattr(repository, 'alias'):
753 753 _type = repository.alias
754 754 elif hasattr(repository, 'repo_type'):
755 755 _type = repository.repo_type
756 756 else:
757 757 _type = repository
758 758 return _type == self._backend_alias
759 759
760 760 is_git = _RepoChecker('git')
761 761 is_hg = _RepoChecker('hg')
762 762 is_svn = _RepoChecker('svn')
763 763
764 764
765 765 def get_repo_type_by_name(repo_name):
766 766 repo = Repository.get_by_repo_name(repo_name)
767 767 return repo.repo_type
768 768
769 769
770 770 def is_svn_without_proxy(repository):
771 771 from rhodecode import CONFIG
772 772 if is_svn(repository):
773 773 if not CONFIG.get('rhodecode_proxy_subversion_http_requests', False):
774 774 return True
775 775 return False
776 776
777 777
778 778 def discover_user(author):
779 779 """
780 780 Tries to discover RhodeCode User based on the autho string. Author string
781 781 is typically `FirstName LastName <email@address.com>`
782 782 """
783 783
784 784 # if author is already an instance use it for extraction
785 785 if isinstance(author, User):
786 786 return author
787 787
788 788 # Valid email in the attribute passed, see if they're in the system
789 789 _email = author_email(author)
790 790 if _email != '':
791 791 user = User.get_by_email(_email, case_insensitive=True, cache=True)
792 792 if user is not None:
793 793 return user
794 794
795 795 # Maybe it's a username, we try to extract it and fetch by username ?
796 796 _author = author_name(author)
797 797 user = User.get_by_username(_author, case_insensitive=True, cache=True)
798 798 if user is not None:
799 799 return user
800 800
801 801 return None
802 802
803 803
804 804 def email_or_none(author):
805 805 # extract email from the commit string
806 806 _email = author_email(author)
807 807
808 808 # If we have an email, use it, otherwise
809 809 # see if it contains a username we can get an email from
810 810 if _email != '':
811 811 return _email
812 812 else:
813 813 user = User.get_by_username(
814 814 author_name(author), case_insensitive=True, cache=True)
815 815
816 816 if user is not None:
817 817 return user.email
818 818
819 819 # No valid email, not a valid user in the system, none!
820 820 return None
821 821
822 822
823 823 def link_to_user(author, length=0, **kwargs):
824 824 user = discover_user(author)
825 825 # user can be None, but if we have it already it means we can re-use it
826 826 # in the person() function, so we save 1 intensive-query
827 827 if user:
828 828 author = user
829 829
830 830 display_person = person(author, 'username_or_name_or_email')
831 831 if length:
832 832 display_person = shorter(display_person, length)
833 833
834 834 if user:
835 835 return link_to(
836 836 escape(display_person),
837 837 url('user_profile', username=user.username),
838 838 **kwargs)
839 839 else:
840 840 return escape(display_person)
841 841
842 842
843 843 def person(author, show_attr="username_and_name"):
844 844 user = discover_user(author)
845 845 if user:
846 846 return getattr(user, show_attr)
847 847 else:
848 848 _author = author_name(author)
849 849 _email = email(author)
850 850 return _author or _email
851 851
852 852
853 853 def author_string(email):
854 854 if email:
855 855 user = User.get_by_email(email, case_insensitive=True, cache=True)
856 856 if user:
857 857 if user.firstname or user.lastname:
858 858 return '%s %s &lt;%s&gt;' % (user.firstname, user.lastname, email)
859 859 else:
860 860 return email
861 861 else:
862 862 return email
863 863 else:
864 864 return None
865 865
866 866
867 867 def person_by_id(id_, show_attr="username_and_name"):
868 868 # attr to return from fetched user
869 869 person_getter = lambda usr: getattr(usr, show_attr)
870 870
871 871 #maybe it's an ID ?
872 872 if str(id_).isdigit() or isinstance(id_, int):
873 873 id_ = int(id_)
874 874 user = User.get(id_)
875 875 if user is not None:
876 876 return person_getter(user)
877 877 return id_
878 878
879 879
880 880 def gravatar_with_user(author, show_disabled=False):
881 881 from rhodecode.lib.utils import PartialRenderer
882 882 _render = PartialRenderer('base/base.html')
883 883 return _render('gravatar_with_user', author, show_disabled=show_disabled)
884 884
885 885
886 886 def desc_stylize(value):
887 887 """
888 888 converts tags from value into html equivalent
889 889
890 890 :param value:
891 891 """
892 892 if not value:
893 893 return ''
894 894
895 895 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
896 896 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
897 897 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
898 898 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
899 899 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
900 900 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
901 901 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
902 902 '<div class="metatag" tag="lang">\\2</div>', value)
903 903 value = re.sub(r'\[([a-z]+)\]',
904 904 '<div class="metatag" tag="\\1">\\1</div>', value)
905 905
906 906 return value
907 907
908 908
909 909 def escaped_stylize(value):
910 910 """
911 911 converts tags from value into html equivalent, but escaping its value first
912 912 """
913 913 if not value:
914 914 return ''
915 915
916 916 # Using default webhelper escape method, but has to force it as a
917 917 # plain unicode instead of a markup tag to be used in regex expressions
918 918 value = unicode(escape(safe_unicode(value)))
919 919
920 920 value = re.sub(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
921 921 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
922 922 value = re.sub(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
923 923 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
924 924 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]',
925 925 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
926 926 value = re.sub(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+]*)\]',
927 927 '<div class="metatag" tag="lang">\\2</div>', value)
928 928 value = re.sub(r'\[([a-z]+)\]',
929 929 '<div class="metatag" tag="\\1">\\1</div>', value)
930 930
931 931 return value
932 932
933 933
934 934 def bool2icon(value):
935 935 """
936 936 Returns boolean value of a given value, represented as html element with
937 937 classes that will represent icons
938 938
939 939 :param value: given value to convert to html node
940 940 """
941 941
942 942 if value: # does bool conversion
943 943 return HTML.tag('i', class_="icon-true")
944 944 else: # not true as bool
945 945 return HTML.tag('i', class_="icon-false")
946 946
947 947
948 948 #==============================================================================
949 949 # PERMS
950 950 #==============================================================================
951 951 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
952 952 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
953 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token
953 HasRepoGroupPermissionAny, HasRepoPermissionAnyApi, get_csrf_token, \
954 csrf_token_key
954 955
955 956
956 957 #==============================================================================
957 958 # GRAVATAR URL
958 959 #==============================================================================
959 960 class InitialsGravatar(object):
960 961 def __init__(self, email_address, first_name, last_name, size=30,
961 962 background=None, text_color='#fff'):
962 963 self.size = size
963 964 self.first_name = first_name
964 965 self.last_name = last_name
965 966 self.email_address = email_address
966 967 self.background = background or self.str2color(email_address)
967 968 self.text_color = text_color
968 969
969 970 def get_color_bank(self):
970 971 """
971 972 returns a predefined list of colors that gravatars can use.
972 973 Those are randomized distinct colors that guarantee readability and
973 974 uniqueness.
974 975
975 976 generated with: http://phrogz.net/css/distinct-colors.html
976 977 """
977 978 return [
978 979 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
979 980 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
980 981 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
981 982 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
982 983 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
983 984 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
984 985 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
985 986 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
986 987 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
987 988 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
988 989 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
989 990 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
990 991 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
991 992 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
992 993 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
993 994 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
994 995 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
995 996 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
996 997 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
997 998 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
998 999 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
999 1000 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
1000 1001 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
1001 1002 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
1002 1003 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
1003 1004 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
1004 1005 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
1005 1006 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
1006 1007 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
1007 1008 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
1008 1009 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
1009 1010 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
1010 1011 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
1011 1012 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
1012 1013 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
1013 1014 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
1014 1015 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
1015 1016 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
1016 1017 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
1017 1018 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
1018 1019 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
1019 1020 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
1020 1021 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
1021 1022 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
1022 1023 '#4f8c46', '#368dd9', '#5c0073'
1023 1024 ]
1024 1025
1025 1026 def rgb_to_hex_color(self, rgb_tuple):
1026 1027 """
1027 1028 Converts an rgb_tuple passed to an hex color.
1028 1029
1029 1030 :param rgb_tuple: tuple with 3 ints represents rgb color space
1030 1031 """
1031 1032 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
1032 1033
1033 1034 def email_to_int_list(self, email_str):
1034 1035 """
1035 1036 Get every byte of the hex digest value of email and turn it to integer.
1036 1037 It's going to be always between 0-255
1037 1038 """
1038 1039 digest = md5_safe(email_str.lower())
1039 1040 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
1040 1041
1041 1042 def pick_color_bank_index(self, email_str, color_bank):
1042 1043 return self.email_to_int_list(email_str)[0] % len(color_bank)
1043 1044
1044 1045 def str2color(self, email_str):
1045 1046 """
1046 1047 Tries to map in a stable algorithm an email to color
1047 1048
1048 1049 :param email_str:
1049 1050 """
1050 1051 color_bank = self.get_color_bank()
1051 1052 # pick position (module it's length so we always find it in the
1052 1053 # bank even if it's smaller than 256 values
1053 1054 pos = self.pick_color_bank_index(email_str, color_bank)
1054 1055 return color_bank[pos]
1055 1056
1056 1057 def normalize_email(self, email_address):
1057 1058 import unicodedata
1058 1059 # default host used to fill in the fake/missing email
1059 1060 default_host = u'localhost'
1060 1061
1061 1062 if not email_address:
1062 1063 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1063 1064
1064 1065 email_address = safe_unicode(email_address)
1065 1066
1066 1067 if u'@' not in email_address:
1067 1068 email_address = u'%s@%s' % (email_address, default_host)
1068 1069
1069 1070 if email_address.endswith(u'@'):
1070 1071 email_address = u'%s%s' % (email_address, default_host)
1071 1072
1072 1073 email_address = unicodedata.normalize('NFKD', email_address)\
1073 1074 .encode('ascii', 'ignore')
1074 1075 return email_address
1075 1076
1076 1077 def get_initials(self):
1077 1078 """
1078 1079 Returns 2 letter initials calculated based on the input.
1079 1080 The algorithm picks first given email address, and takes first letter
1080 1081 of part before @, and then the first letter of server name. In case
1081 1082 the part before @ is in a format of `somestring.somestring2` it replaces
1082 1083 the server letter with first letter of somestring2
1083 1084
1084 1085 In case function was initialized with both first and lastname, this
1085 1086 overrides the extraction from email by first letter of the first and
1086 1087 last name. We add special logic to that functionality, In case Full name
1087 1088 is compound, like Guido Von Rossum, we use last part of the last name
1088 1089 (Von Rossum) picking `R`.
1089 1090
1090 1091 Function also normalizes the non-ascii characters to they ascii
1091 1092 representation, eg Δ„ => A
1092 1093 """
1093 1094 import unicodedata
1094 1095 # replace non-ascii to ascii
1095 1096 first_name = unicodedata.normalize(
1096 1097 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1097 1098 last_name = unicodedata.normalize(
1098 1099 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1099 1100
1100 1101 # do NFKD encoding, and also make sure email has proper format
1101 1102 email_address = self.normalize_email(self.email_address)
1102 1103
1103 1104 # first push the email initials
1104 1105 prefix, server = email_address.split('@', 1)
1105 1106
1106 1107 # check if prefix is maybe a 'firstname.lastname' syntax
1107 1108 _dot_split = prefix.rsplit('.', 1)
1108 1109 if len(_dot_split) == 2:
1109 1110 initials = [_dot_split[0][0], _dot_split[1][0]]
1110 1111 else:
1111 1112 initials = [prefix[0], server[0]]
1112 1113
1113 1114 # then try to replace either firtname or lastname
1114 1115 fn_letter = (first_name or " ")[0].strip()
1115 1116 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1116 1117
1117 1118 if fn_letter:
1118 1119 initials[0] = fn_letter
1119 1120
1120 1121 if ln_letter:
1121 1122 initials[1] = ln_letter
1122 1123
1123 1124 return ''.join(initials).upper()
1124 1125
1125 1126 def get_img_data_by_type(self, font_family, img_type):
1126 1127 default_user = """
1127 1128 <svg xmlns="http://www.w3.org/2000/svg"
1128 1129 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1129 1130 viewBox="-15 -10 439.165 429.164"
1130 1131
1131 1132 xml:space="preserve"
1132 1133 style="background:{background};" >
1133 1134
1134 1135 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1135 1136 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1136 1137 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1137 1138 168.596,153.916,216.671,
1138 1139 204.583,216.671z" fill="{text_color}"/>
1139 1140 <path d="M407.164,374.717L360.88,
1140 1141 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1141 1142 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1142 1143 15.366-44.203,23.488-69.076,23.488c-24.877,
1143 1144 0-48.762-8.122-69.078-23.488
1144 1145 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1145 1146 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1146 1147 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1147 1148 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1148 1149 19.402-10.527 C409.699,390.129,
1149 1150 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1150 1151 </svg>""".format(
1151 1152 size=self.size,
1152 1153 background='#979797', # @grey4
1153 1154 text_color=self.text_color,
1154 1155 font_family=font_family)
1155 1156
1156 1157 return {
1157 1158 "default_user": default_user
1158 1159 }[img_type]
1159 1160
1160 1161 def get_img_data(self, svg_type=None):
1161 1162 """
1162 1163 generates the svg metadata for image
1163 1164 """
1164 1165
1165 1166 font_family = ','.join([
1166 1167 'proximanovaregular',
1167 1168 'Proxima Nova Regular',
1168 1169 'Proxima Nova',
1169 1170 'Arial',
1170 1171 'Lucida Grande',
1171 1172 'sans-serif'
1172 1173 ])
1173 1174 if svg_type:
1174 1175 return self.get_img_data_by_type(font_family, svg_type)
1175 1176
1176 1177 initials = self.get_initials()
1177 1178 img_data = """
1178 1179 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1179 1180 width="{size}" height="{size}"
1180 1181 style="width: 100%; height: 100%; background-color: {background}"
1181 1182 viewBox="0 0 {size} {size}">
1182 1183 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1183 1184 pointer-events="auto" fill="{text_color}"
1184 1185 font-family="{font_family}"
1185 1186 style="font-weight: 400; font-size: {f_size}px;">{text}
1186 1187 </text>
1187 1188 </svg>""".format(
1188 1189 size=self.size,
1189 1190 f_size=self.size/1.85, # scale the text inside the box nicely
1190 1191 background=self.background,
1191 1192 text_color=self.text_color,
1192 1193 text=initials.upper(),
1193 1194 font_family=font_family)
1194 1195
1195 1196 return img_data
1196 1197
1197 1198 def generate_svg(self, svg_type=None):
1198 1199 img_data = self.get_img_data(svg_type)
1199 1200 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1200 1201
1201 1202
1202 1203 def initials_gravatar(email_address, first_name, last_name, size=30):
1203 1204 svg_type = None
1204 1205 if email_address == User.DEFAULT_USER_EMAIL:
1205 1206 svg_type = 'default_user'
1206 1207 klass = InitialsGravatar(email_address, first_name, last_name, size)
1207 1208 return klass.generate_svg(svg_type=svg_type)
1208 1209
1209 1210
1210 1211 def gravatar_url(email_address, size=30):
1211 1212 # doh, we need to re-import those to mock it later
1212 1213 from pylons import tmpl_context as c
1213 1214
1214 1215 _use_gravatar = c.visual.use_gravatar
1215 1216 _gravatar_url = c.visual.gravatar_url or User.DEFAULT_GRAVATAR_URL
1216 1217
1217 1218 email_address = email_address or User.DEFAULT_USER_EMAIL
1218 1219 if isinstance(email_address, unicode):
1219 1220 # hashlib crashes on unicode items
1220 1221 email_address = safe_str(email_address)
1221 1222
1222 1223 # empty email or default user
1223 1224 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1224 1225 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1225 1226
1226 1227 if _use_gravatar:
1227 1228 # TODO: Disuse pyramid thread locals. Think about another solution to
1228 1229 # get the host and schema here.
1229 1230 request = get_current_request()
1230 1231 tmpl = safe_str(_gravatar_url)
1231 1232 tmpl = tmpl.replace('{email}', email_address)\
1232 1233 .replace('{md5email}', md5_safe(email_address.lower())) \
1233 1234 .replace('{netloc}', request.host)\
1234 1235 .replace('{scheme}', request.scheme)\
1235 1236 .replace('{size}', safe_str(size))
1236 1237 return tmpl
1237 1238 else:
1238 1239 return initials_gravatar(email_address, '', '', size=size)
1239 1240
1240 1241
1241 1242 class Page(_Page):
1242 1243 """
1243 1244 Custom pager to match rendering style with paginator
1244 1245 """
1245 1246
1246 1247 def _get_pos(self, cur_page, max_page, items):
1247 1248 edge = (items / 2) + 1
1248 1249 if (cur_page <= edge):
1249 1250 radius = max(items / 2, items - cur_page)
1250 1251 elif (max_page - cur_page) < edge:
1251 1252 radius = (items - 1) - (max_page - cur_page)
1252 1253 else:
1253 1254 radius = items / 2
1254 1255
1255 1256 left = max(1, (cur_page - (radius)))
1256 1257 right = min(max_page, cur_page + (radius))
1257 1258 return left, cur_page, right
1258 1259
1259 1260 def _range(self, regexp_match):
1260 1261 """
1261 1262 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1262 1263
1263 1264 Arguments:
1264 1265
1265 1266 regexp_match
1266 1267 A "re" (regular expressions) match object containing the
1267 1268 radius of linked pages around the current page in
1268 1269 regexp_match.group(1) as a string
1269 1270
1270 1271 This function is supposed to be called as a callable in
1271 1272 re.sub.
1272 1273
1273 1274 """
1274 1275 radius = int(regexp_match.group(1))
1275 1276
1276 1277 # Compute the first and last page number within the radius
1277 1278 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1278 1279 # -> leftmost_page = 5
1279 1280 # -> rightmost_page = 9
1280 1281 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1281 1282 self.last_page,
1282 1283 (radius * 2) + 1)
1283 1284 nav_items = []
1284 1285
1285 1286 # Create a link to the first page (unless we are on the first page
1286 1287 # or there would be no need to insert '..' spacers)
1287 1288 if self.page != self.first_page and self.first_page < leftmost_page:
1288 1289 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1289 1290
1290 1291 # Insert dots if there are pages between the first page
1291 1292 # and the currently displayed page range
1292 1293 if leftmost_page - self.first_page > 1:
1293 1294 # Wrap in a SPAN tag if nolink_attr is set
1294 1295 text = '..'
1295 1296 if self.dotdot_attr:
1296 1297 text = HTML.span(c=text, **self.dotdot_attr)
1297 1298 nav_items.append(text)
1298 1299
1299 1300 for thispage in xrange(leftmost_page, rightmost_page + 1):
1300 1301 # Hilight the current page number and do not use a link
1301 1302 if thispage == self.page:
1302 1303 text = '%s' % (thispage,)
1303 1304 # Wrap in a SPAN tag if nolink_attr is set
1304 1305 if self.curpage_attr:
1305 1306 text = HTML.span(c=text, **self.curpage_attr)
1306 1307 nav_items.append(text)
1307 1308 # Otherwise create just a link to that page
1308 1309 else:
1309 1310 text = '%s' % (thispage,)
1310 1311 nav_items.append(self._pagerlink(thispage, text))
1311 1312
1312 1313 # Insert dots if there are pages between the displayed
1313 1314 # page numbers and the end of the page range
1314 1315 if self.last_page - rightmost_page > 1:
1315 1316 text = '..'
1316 1317 # Wrap in a SPAN tag if nolink_attr is set
1317 1318 if self.dotdot_attr:
1318 1319 text = HTML.span(c=text, **self.dotdot_attr)
1319 1320 nav_items.append(text)
1320 1321
1321 1322 # Create a link to the very last page (unless we are on the last
1322 1323 # page or there would be no need to insert '..' spacers)
1323 1324 if self.page != self.last_page and rightmost_page < self.last_page:
1324 1325 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1325 1326
1326 1327 ## prerender links
1327 1328 #_page_link = url.current()
1328 1329 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1329 1330 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1330 1331 return self.separator.join(nav_items)
1331 1332
1332 1333 def pager(self, format='~2~', page_param='page', partial_param='partial',
1333 1334 show_if_single_page=False, separator=' ', onclick=None,
1334 1335 symbol_first='<<', symbol_last='>>',
1335 1336 symbol_previous='<', symbol_next='>',
1336 1337 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1337 1338 curpage_attr={'class': 'pager_curpage'},
1338 1339 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1339 1340
1340 1341 self.curpage_attr = curpage_attr
1341 1342 self.separator = separator
1342 1343 self.pager_kwargs = kwargs
1343 1344 self.page_param = page_param
1344 1345 self.partial_param = partial_param
1345 1346 self.onclick = onclick
1346 1347 self.link_attr = link_attr
1347 1348 self.dotdot_attr = dotdot_attr
1348 1349
1349 1350 # Don't show navigator if there is no more than one page
1350 1351 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1351 1352 return ''
1352 1353
1353 1354 from string import Template
1354 1355 # Replace ~...~ in token format by range of pages
1355 1356 result = re.sub(r'~(\d+)~', self._range, format)
1356 1357
1357 1358 # Interpolate '%' variables
1358 1359 result = Template(result).safe_substitute({
1359 1360 'first_page': self.first_page,
1360 1361 'last_page': self.last_page,
1361 1362 'page': self.page,
1362 1363 'page_count': self.page_count,
1363 1364 'items_per_page': self.items_per_page,
1364 1365 'first_item': self.first_item,
1365 1366 'last_item': self.last_item,
1366 1367 'item_count': self.item_count,
1367 1368 'link_first': self.page > self.first_page and \
1368 1369 self._pagerlink(self.first_page, symbol_first) or '',
1369 1370 'link_last': self.page < self.last_page and \
1370 1371 self._pagerlink(self.last_page, symbol_last) or '',
1371 1372 'link_previous': self.previous_page and \
1372 1373 self._pagerlink(self.previous_page, symbol_previous) \
1373 1374 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1374 1375 'link_next': self.next_page and \
1375 1376 self._pagerlink(self.next_page, symbol_next) \
1376 1377 or HTML.span(symbol_next, class_="pg-next disabled")
1377 1378 })
1378 1379
1379 1380 return literal(result)
1380 1381
1381 1382
1382 1383 #==============================================================================
1383 1384 # REPO PAGER, PAGER FOR REPOSITORY
1384 1385 #==============================================================================
1385 1386 class RepoPage(Page):
1386 1387
1387 1388 def __init__(self, collection, page=1, items_per_page=20,
1388 1389 item_count=None, url=None, **kwargs):
1389 1390
1390 1391 """Create a "RepoPage" instance. special pager for paging
1391 1392 repository
1392 1393 """
1393 1394 self._url_generator = url
1394 1395
1395 1396 # Safe the kwargs class-wide so they can be used in the pager() method
1396 1397 self.kwargs = kwargs
1397 1398
1398 1399 # Save a reference to the collection
1399 1400 self.original_collection = collection
1400 1401
1401 1402 self.collection = collection
1402 1403
1403 1404 # The self.page is the number of the current page.
1404 1405 # The first page has the number 1!
1405 1406 try:
1406 1407 self.page = int(page) # make it int() if we get it as a string
1407 1408 except (ValueError, TypeError):
1408 1409 self.page = 1
1409 1410
1410 1411 self.items_per_page = items_per_page
1411 1412
1412 1413 # Unless the user tells us how many items the collections has
1413 1414 # we calculate that ourselves.
1414 1415 if item_count is not None:
1415 1416 self.item_count = item_count
1416 1417 else:
1417 1418 self.item_count = len(self.collection)
1418 1419
1419 1420 # Compute the number of the first and last available page
1420 1421 if self.item_count > 0:
1421 1422 self.first_page = 1
1422 1423 self.page_count = int(math.ceil(float(self.item_count) /
1423 1424 self.items_per_page))
1424 1425 self.last_page = self.first_page + self.page_count - 1
1425 1426
1426 1427 # Make sure that the requested page number is the range of
1427 1428 # valid pages
1428 1429 if self.page > self.last_page:
1429 1430 self.page = self.last_page
1430 1431 elif self.page < self.first_page:
1431 1432 self.page = self.first_page
1432 1433
1433 1434 # Note: the number of items on this page can be less than
1434 1435 # items_per_page if the last page is not full
1435 1436 self.first_item = max(0, (self.item_count) - (self.page *
1436 1437 items_per_page))
1437 1438 self.last_item = ((self.item_count - 1) - items_per_page *
1438 1439 (self.page - 1))
1439 1440
1440 1441 self.items = list(self.collection[self.first_item:self.last_item + 1])
1441 1442
1442 1443 # Links to previous and next page
1443 1444 if self.page > self.first_page:
1444 1445 self.previous_page = self.page - 1
1445 1446 else:
1446 1447 self.previous_page = None
1447 1448
1448 1449 if self.page < self.last_page:
1449 1450 self.next_page = self.page + 1
1450 1451 else:
1451 1452 self.next_page = None
1452 1453
1453 1454 # No items available
1454 1455 else:
1455 1456 self.first_page = None
1456 1457 self.page_count = 0
1457 1458 self.last_page = None
1458 1459 self.first_item = None
1459 1460 self.last_item = None
1460 1461 self.previous_page = None
1461 1462 self.next_page = None
1462 1463 self.items = []
1463 1464
1464 1465 # This is a subclass of the 'list' type. Initialise the list now.
1465 1466 list.__init__(self, reversed(self.items))
1466 1467
1467 1468
1468 1469 def changed_tooltip(nodes):
1469 1470 """
1470 1471 Generates a html string for changed nodes in commit page.
1471 1472 It limits the output to 30 entries
1472 1473
1473 1474 :param nodes: LazyNodesGenerator
1474 1475 """
1475 1476 if nodes:
1476 1477 pref = ': <br/> '
1477 1478 suf = ''
1478 1479 if len(nodes) > 30:
1479 1480 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
1480 1481 return literal(pref + '<br/> '.join([safe_unicode(x.path)
1481 1482 for x in nodes[:30]]) + suf)
1482 1483 else:
1483 1484 return ': ' + _('No Files')
1484 1485
1485 1486
1486 1487 def breadcrumb_repo_link(repo):
1487 1488 """
1488 1489 Makes a breadcrumbs path link to repo
1489 1490
1490 1491 ex::
1491 1492 group >> subgroup >> repo
1492 1493
1493 1494 :param repo: a Repository instance
1494 1495 """
1495 1496
1496 1497 path = [
1497 1498 link_to(group.name, url('repo_group_home', group_name=group.group_name))
1498 1499 for group in repo.groups_with_parents
1499 1500 ] + [
1500 1501 link_to(repo.just_name, url('summary_home', repo_name=repo.repo_name))
1501 1502 ]
1502 1503
1503 1504 return literal(' &raquo; '.join(path))
1504 1505
1505 1506
1506 1507 def format_byte_size_binary(file_size):
1507 1508 """
1508 1509 Formats file/folder sizes to standard.
1509 1510 """
1510 1511 formatted_size = format_byte_size(file_size, binary=True)
1511 1512 return formatted_size
1512 1513
1513 1514
1514 1515 def fancy_file_stats(stats):
1515 1516 """
1516 1517 Displays a fancy two colored bar for number of added/deleted
1517 1518 lines of code on file
1518 1519
1519 1520 :param stats: two element list of added/deleted lines of code
1520 1521 """
1521 1522 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
1522 1523 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
1523 1524
1524 1525 def cgen(l_type, a_v, d_v):
1525 1526 mapping = {'tr': 'top-right-rounded-corner-mid',
1526 1527 'tl': 'top-left-rounded-corner-mid',
1527 1528 'br': 'bottom-right-rounded-corner-mid',
1528 1529 'bl': 'bottom-left-rounded-corner-mid'}
1529 1530 map_getter = lambda x: mapping[x]
1530 1531
1531 1532 if l_type == 'a' and d_v:
1532 1533 #case when added and deleted are present
1533 1534 return ' '.join(map(map_getter, ['tl', 'bl']))
1534 1535
1535 1536 if l_type == 'a' and not d_v:
1536 1537 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1537 1538
1538 1539 if l_type == 'd' and a_v:
1539 1540 return ' '.join(map(map_getter, ['tr', 'br']))
1540 1541
1541 1542 if l_type == 'd' and not a_v:
1542 1543 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1543 1544
1544 1545 a, d = stats['added'], stats['deleted']
1545 1546 width = 100
1546 1547
1547 1548 if stats['binary']: # binary operations like chmod/rename etc
1548 1549 lbl = []
1549 1550 bin_op = 0 # undefined
1550 1551
1551 1552 # prefix with bin for binary files
1552 1553 if BIN_FILENODE in stats['ops']:
1553 1554 lbl += ['bin']
1554 1555
1555 1556 if NEW_FILENODE in stats['ops']:
1556 1557 lbl += [_('new file')]
1557 1558 bin_op = NEW_FILENODE
1558 1559 elif MOD_FILENODE in stats['ops']:
1559 1560 lbl += [_('mod')]
1560 1561 bin_op = MOD_FILENODE
1561 1562 elif DEL_FILENODE in stats['ops']:
1562 1563 lbl += [_('del')]
1563 1564 bin_op = DEL_FILENODE
1564 1565 elif RENAMED_FILENODE in stats['ops']:
1565 1566 lbl += [_('rename')]
1566 1567 bin_op = RENAMED_FILENODE
1567 1568
1568 1569 # chmod can go with other operations, so we add a + to lbl if needed
1569 1570 if CHMOD_FILENODE in stats['ops']:
1570 1571 lbl += [_('chmod')]
1571 1572 if bin_op == 0:
1572 1573 bin_op = CHMOD_FILENODE
1573 1574
1574 1575 lbl = '+'.join(lbl)
1575 1576 b_a = '<div class="bin bin%s %s" style="width:100%%">%s</div>' \
1576 1577 % (bin_op, cgen('a', a_v='', d_v=0), lbl)
1577 1578 b_d = '<div class="bin bin1" style="width:0%%"></div>'
1578 1579 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
1579 1580
1580 1581 t = stats['added'] + stats['deleted']
1581 1582 unit = float(width) / (t or 1)
1582 1583
1583 1584 # needs > 9% of width to be visible or 0 to be hidden
1584 1585 a_p = max(9, unit * a) if a > 0 else 0
1585 1586 d_p = max(9, unit * d) if d > 0 else 0
1586 1587 p_sum = a_p + d_p
1587 1588
1588 1589 if p_sum > width:
1589 1590 #adjust the percentage to be == 100% since we adjusted to 9
1590 1591 if a_p > d_p:
1591 1592 a_p = a_p - (p_sum - width)
1592 1593 else:
1593 1594 d_p = d_p - (p_sum - width)
1594 1595
1595 1596 a_v = a if a > 0 else ''
1596 1597 d_v = d if d > 0 else ''
1597 1598
1598 1599 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
1599 1600 cgen('a', a_v, d_v), a_p, a_v
1600 1601 )
1601 1602 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
1602 1603 cgen('d', a_v, d_v), d_p, d_v
1603 1604 )
1604 1605 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
1605 1606
1606 1607
1607 1608 def urlify_text(text_, safe=True):
1608 1609 """
1609 1610 Extrac urls from text and make html links out of them
1610 1611
1611 1612 :param text_:
1612 1613 """
1613 1614
1614 1615 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1615 1616 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1616 1617
1617 1618 def url_func(match_obj):
1618 1619 url_full = match_obj.groups()[0]
1619 1620 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1620 1621 _newtext = url_pat.sub(url_func, text_)
1621 1622 if safe:
1622 1623 return literal(_newtext)
1623 1624 return _newtext
1624 1625
1625 1626
1626 1627 def urlify_commits(text_, repository):
1627 1628 """
1628 1629 Extract commit ids from text and make link from them
1629 1630
1630 1631 :param text_:
1631 1632 :param repository: repo name to build the URL with
1632 1633 """
1633 1634 from pylons import url # doh, we need to re-import url to mock it later
1634 1635 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1635 1636
1636 1637 def url_func(match_obj):
1637 1638 commit_id = match_obj.groups()[1]
1638 1639 pref = match_obj.groups()[0]
1639 1640 suf = match_obj.groups()[2]
1640 1641
1641 1642 tmpl = (
1642 1643 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1643 1644 '%(commit_id)s</a>%(suf)s'
1644 1645 )
1645 1646 return tmpl % {
1646 1647 'pref': pref,
1647 1648 'cls': 'revision-link',
1648 1649 'url': url('changeset_home', repo_name=repository,
1649 1650 revision=commit_id, qualified=True),
1650 1651 'commit_id': commit_id,
1651 1652 'suf': suf
1652 1653 }
1653 1654
1654 1655 newtext = URL_PAT.sub(url_func, text_)
1655 1656
1656 1657 return newtext
1657 1658
1658 1659
1659 1660 def _process_url_func(match_obj, repo_name, uid, entry,
1660 1661 return_raw_data=False):
1661 1662 pref = ''
1662 1663 if match_obj.group().startswith(' '):
1663 1664 pref = ' '
1664 1665
1665 1666 issue_id = ''.join(match_obj.groups())
1666 1667 tmpl = (
1667 1668 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1668 1669 '%(issue-prefix)s%(id-repr)s'
1669 1670 '</a>')
1670 1671
1671 1672 (repo_name_cleaned,
1672 1673 parent_group_name) = RepoGroupModel().\
1673 1674 _get_group_name_and_parent(repo_name)
1674 1675
1675 1676 # variables replacement
1676 1677 named_vars = {
1677 1678 'id': issue_id,
1678 1679 'repo': repo_name,
1679 1680 'repo_name': repo_name_cleaned,
1680 1681 'group_name': parent_group_name
1681 1682 }
1682 1683 # named regex variables
1683 1684 named_vars.update(match_obj.groupdict())
1684 1685 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1685 1686
1686 1687 data = {
1687 1688 'pref': pref,
1688 1689 'cls': 'issue-tracker-link',
1689 1690 'url': _url,
1690 1691 'id-repr': issue_id,
1691 1692 'issue-prefix': entry['pref'],
1692 1693 'serv': entry['url'],
1693 1694 }
1694 1695 if return_raw_data:
1695 1696 return {
1696 1697 'id': issue_id,
1697 1698 'url': _url
1698 1699 }
1699 1700 return tmpl % data
1700 1701
1701 1702
1702 1703 def process_patterns(text_string, repo_name, config=None):
1703 1704 repo = None
1704 1705 if repo_name:
1705 1706 # Retrieving repo_name to avoid invalid repo_name to explode on
1706 1707 # IssueTrackerSettingsModel but still passing invalid name further down
1707 1708 repo = Repository.get_by_repo_name(repo_name, cache=True)
1708 1709
1709 1710 settings_model = IssueTrackerSettingsModel(repo=repo)
1710 1711 active_entries = settings_model.get_settings(cache=True)
1711 1712
1712 1713 issues_data = []
1713 1714 newtext = text_string
1714 1715 for uid, entry in active_entries.items():
1715 1716 log.debug('found issue tracker entry with uid %s' % (uid,))
1716 1717
1717 1718 if not (entry['pat'] and entry['url']):
1718 1719 log.debug('skipping due to missing data')
1719 1720 continue
1720 1721
1721 1722 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s'
1722 1723 % (uid, entry['pat'], entry['url'], entry['pref']))
1723 1724
1724 1725 try:
1725 1726 pattern = re.compile(r'%s' % entry['pat'])
1726 1727 except re.error:
1727 1728 log.exception(
1728 1729 'issue tracker pattern: `%s` failed to compile',
1729 1730 entry['pat'])
1730 1731 continue
1731 1732
1732 1733 data_func = partial(
1733 1734 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1734 1735 return_raw_data=True)
1735 1736
1736 1737 for match_obj in pattern.finditer(text_string):
1737 1738 issues_data.append(data_func(match_obj))
1738 1739
1739 1740 url_func = partial(
1740 1741 _process_url_func, repo_name=repo_name, entry=entry, uid=uid)
1741 1742
1742 1743 newtext = pattern.sub(url_func, newtext)
1743 1744 log.debug('processed prefix:uid `%s`' % (uid,))
1744 1745
1745 1746 return newtext, issues_data
1746 1747
1747 1748
1748 1749 def urlify_commit_message(commit_text, repository=None):
1749 1750 """
1750 1751 Parses given text message and makes proper links.
1751 1752 issues are linked to given issue-server, and rest is a commit link
1752 1753
1753 1754 :param commit_text:
1754 1755 :param repository:
1755 1756 """
1756 1757 from pylons import url # doh, we need to re-import url to mock it later
1757 1758
1758 1759 def escaper(string):
1759 1760 return string.replace('<', '&lt;').replace('>', '&gt;')
1760 1761
1761 1762 newtext = escaper(commit_text)
1762 1763
1763 1764 # extract http/https links and make them real urls
1764 1765 newtext = urlify_text(newtext, safe=False)
1765 1766
1766 1767 # urlify commits - extract commit ids and make link out of them, if we have
1767 1768 # the scope of repository present.
1768 1769 if repository:
1769 1770 newtext = urlify_commits(newtext, repository)
1770 1771
1771 1772 # process issue tracker patterns
1772 1773 newtext, issues = process_patterns(newtext, repository or '')
1773 1774
1774 1775 return literal(newtext)
1775 1776
1776 1777
1777 1778 def rst(source, mentions=False):
1778 1779 return literal('<div class="rst-block">%s</div>' %
1779 1780 MarkupRenderer.rst(source, mentions=mentions))
1780 1781
1781 1782
1782 1783 def markdown(source, mentions=False):
1783 1784 return literal('<div class="markdown-block">%s</div>' %
1784 1785 MarkupRenderer.markdown(source, flavored=True,
1785 1786 mentions=mentions))
1786 1787
1787 1788 def renderer_from_filename(filename, exclude=None):
1788 1789 return MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1789 1790
1790 1791
1791 1792 def render(source, renderer='rst', mentions=False):
1792 1793 if renderer == 'rst':
1793 1794 return rst(source, mentions=mentions)
1794 1795 if renderer == 'markdown':
1795 1796 return markdown(source, mentions=mentions)
1796 1797
1797 1798
1798 1799 def commit_status(repo, commit_id):
1799 1800 return ChangesetStatusModel().get_status(repo, commit_id)
1800 1801
1801 1802
1802 1803 def commit_status_lbl(commit_status):
1803 1804 return dict(ChangesetStatus.STATUSES).get(commit_status)
1804 1805
1805 1806
1806 1807 def commit_time(repo_name, commit_id):
1807 1808 repo = Repository.get_by_repo_name(repo_name)
1808 1809 commit = repo.get_commit(commit_id=commit_id)
1809 1810 return commit.date
1810 1811
1811 1812
1812 1813 def get_permission_name(key):
1813 1814 return dict(Permission.PERMS).get(key)
1814 1815
1815 1816
1816 1817 def journal_filter_help():
1817 1818 return _(
1818 1819 'Example filter terms:\n' +
1819 1820 ' repository:vcs\n' +
1820 1821 ' username:marcin\n' +
1821 1822 ' action:*push*\n' +
1822 1823 ' ip:127.0.0.1\n' +
1823 1824 ' date:20120101\n' +
1824 1825 ' date:[20120101100000 TO 20120102]\n' +
1825 1826 '\n' +
1826 1827 'Generate wildcards using \'*\' character:\n' +
1827 1828 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1828 1829 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1829 1830 '\n' +
1830 1831 'Optional AND / OR operators in queries\n' +
1831 1832 ' "repository:vcs OR repository:test"\n' +
1832 1833 ' "username:test AND repository:test*"\n'
1833 1834 )
1834 1835
1835 1836
1836 1837 def not_mapped_error(repo_name):
1837 1838 flash(_('%s repository is not mapped to db perhaps'
1838 1839 ' it was created or renamed from the filesystem'
1839 1840 ' please run the application again'
1840 1841 ' in order to rescan repositories') % repo_name, category='error')
1841 1842
1842 1843
1843 1844 def ip_range(ip_addr):
1844 1845 from rhodecode.model.db import UserIpMap
1845 1846 s, e = UserIpMap._get_ip_range(ip_addr)
1846 1847 return '%s - %s' % (s, e)
1847 1848
1848 1849
1849 1850 def form(url, method='post', needs_csrf_token=True, **attrs):
1850 1851 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1851 1852 if method.lower() != 'get' and needs_csrf_token:
1852 1853 raise Exception(
1853 1854 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1854 1855 'CSRF token. If the endpoint does not require such token you can ' +
1855 1856 'explicitly set the parameter needs_csrf_token to false.')
1856 1857
1857 1858 return wh_form(url, method=method, **attrs)
1858 1859
1859 1860
1860 1861 def secure_form(url, method="POST", multipart=False, **attrs):
1861 1862 """Start a form tag that points the action to an url. This
1862 1863 form tag will also include the hidden field containing
1863 1864 the auth token.
1864 1865
1865 1866 The url options should be given either as a string, or as a
1866 1867 ``url()`` function. The method for the form defaults to POST.
1867 1868
1868 1869 Options:
1869 1870
1870 1871 ``multipart``
1871 1872 If set to True, the enctype is set to "multipart/form-data".
1872 1873 ``method``
1873 1874 The method to use when submitting the form, usually either
1874 1875 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1875 1876 hidden input with name _method is added to simulate the verb
1876 1877 over POST.
1877 1878
1878 1879 """
1879 1880 from webhelpers.pylonslib.secure_form import insecure_form
1880 from rhodecode.lib.auth import get_csrf_token, csrf_token_key
1881 1881 form = insecure_form(url, method, multipart, **attrs)
1882 token = HTML.div(hidden(csrf_token_key, get_csrf_token()), style="display: none;")
1882 token = csrf_input()
1883 1883 return literal("%s\n%s" % (form, token))
1884 1884
1885 def csrf_input():
1886 return literal(
1887 '<input type="hidden" id="{}" name="{}" value="{}">'.format(
1888 csrf_token_key, csrf_token_key, get_csrf_token()))
1889
1885 1890 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1886 1891 select_html = select(name, selected, options, **attrs)
1887 1892 select2 = """
1888 1893 <script>
1889 1894 $(document).ready(function() {
1890 1895 $('#%s').select2({
1891 1896 containerCssClass: 'drop-menu',
1892 1897 dropdownCssClass: 'drop-menu-dropdown',
1893 1898 dropdownAutoWidth: true%s
1894 1899 });
1895 1900 });
1896 1901 </script>
1897 1902 """
1898 1903 filter_option = """,
1899 1904 minimumResultsForSearch: -1
1900 1905 """
1901 1906 input_id = attrs.get('id') or name
1902 1907 filter_enabled = "" if enable_filter else filter_option
1903 1908 select_script = literal(select2 % (input_id, filter_enabled))
1904 1909
1905 1910 return literal(select_html+select_script)
1906 1911
1907 1912
1908 1913 def get_visual_attr(tmpl_context_var, attr_name):
1909 1914 """
1910 1915 A safe way to get a variable from visual variable of template context
1911 1916
1912 1917 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
1913 1918 :param attr_name: name of the attribute we fetch from the c.visual
1914 1919 """
1915 1920 visual = getattr(tmpl_context_var, 'visual', None)
1916 1921 if not visual:
1917 1922 return
1918 1923 else:
1919 1924 return getattr(visual, attr_name, None)
1920 1925
1921 1926
1922 1927 def get_last_path_part(file_node):
1923 1928 if not file_node.path:
1924 1929 return u''
1925 1930
1926 1931 path = safe_unicode(file_node.path.split('/')[-1])
1927 1932 return u'../' + path
1928 1933
1929 1934
1930 1935 def route_path(*args, **kwds):
1931 1936 """
1932 1937 Wrapper around pyramids `route_path` function. It is used to generate
1933 1938 URLs from within pylons views or templates. This will be removed when
1934 1939 pyramid migration if finished.
1935 1940 """
1936 1941 req = get_current_request()
1937 1942 return req.route_path(*args, **kwds)
1938 1943
1939 1944
1945 def static_url(*args, **kwds):
1946 """
1947 Wrapper around pyramids `route_path` function. It is used to generate
1948 URLs from within pylons views or templates. This will be removed when
1949 pyramid migration if finished.
1950 """
1951 req = get_current_request()
1952 return req.static_url(*args, **kwds)
1953
1954
1940 1955 def resource_path(*args, **kwds):
1941 1956 """
1942 1957 Wrapper around pyramids `route_path` function. It is used to generate
1943 1958 URLs from within pylons views or templates. This will be removed when
1944 1959 pyramid migration if finished.
1945 1960 """
1946 1961 req = get_current_request()
1947 1962 return req.resource_path(*args, **kwds)
@@ -1,561 +1,571 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 this is forms validation classes
23 23 http://formencode.org/module-formencode.validators.html
24 24 for list off all availible validators
25 25
26 26 we can create our own validators
27 27
28 28 The table below outlines the options which can be used in a schema in addition to the validators themselves
29 29 pre_validators [] These validators will be applied before the schema
30 30 chained_validators [] These validators will be applied after the schema
31 31 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
32 32 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
33 33 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
34 34 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
35 35
36 36
37 37 <name> = formencode.validators.<name of validator>
38 38 <name> must equal form name
39 39 list=[1,2,3,4,5]
40 40 for SELECT use formencode.All(OneOf(list), Int())
41 41
42 42 """
43 43
44 import deform
45 from pkg_resources import resource_filename
46
47 deform_templates = resource_filename('deform', 'templates')
48 rhodecode_templates = resource_filename('rhodecode', 'templates/forms')
49 search_path = (rhodecode_templates, deform_templates)
50
51 deform.Form.set_zpt_renderer(search_path)
52
53
44 54 import logging
45 55
46 56 import formencode
47 57 from formencode import All, Pipe
48 58
49 59 from pylons.i18n.translation import _
50 60
51 61 from rhodecode import BACKENDS
52 62 from rhodecode.model import validators as v
53 63
54 64 log = logging.getLogger(__name__)
55 65
56 66
57 67 def LoginForm():
58 68 class _LoginForm(formencode.Schema):
59 69 allow_extra_fields = True
60 70 filter_extra_fields = True
61 71 username = v.UnicodeString(
62 72 strip=True,
63 73 min=1,
64 74 not_empty=True,
65 75 messages={
66 76 'empty': _(u'Please enter a login'),
67 77 'tooShort': _(u'Enter a value %(min)i characters long or more')
68 78 }
69 79 )
70 80
71 81 password = v.UnicodeString(
72 82 strip=False,
73 83 min=3,
74 84 not_empty=True,
75 85 messages={
76 86 'empty': _(u'Please enter a password'),
77 87 'tooShort': _(u'Enter %(min)i characters or more')}
78 88 )
79 89
80 90 remember = v.StringBoolean(if_missing=False)
81 91
82 92 chained_validators = [v.ValidAuth()]
83 93 return _LoginForm
84 94
85 95
86 96 def PasswordChangeForm(username):
87 97 class _PasswordChangeForm(formencode.Schema):
88 98 allow_extra_fields = True
89 99 filter_extra_fields = True
90 100
91 101 current_password = v.ValidOldPassword(username)(not_empty=True)
92 102 new_password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
93 103 new_password_confirmation = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
94 104
95 105 chained_validators = [v.ValidPasswordsMatch('new_password',
96 106 'new_password_confirmation')]
97 107 return _PasswordChangeForm
98 108
99 109
100 110 def UserForm(edit=False, available_languages=[], old_data={}):
101 111 class _UserForm(formencode.Schema):
102 112 allow_extra_fields = True
103 113 filter_extra_fields = True
104 114 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
105 115 v.ValidUsername(edit, old_data))
106 116 if edit:
107 117 new_password = All(
108 118 v.ValidPassword(),
109 119 v.UnicodeString(strip=False, min=6, not_empty=False)
110 120 )
111 121 password_confirmation = All(
112 122 v.ValidPassword(),
113 123 v.UnicodeString(strip=False, min=6, not_empty=False),
114 124 )
115 125 admin = v.StringBoolean(if_missing=False)
116 126 else:
117 127 password = All(
118 128 v.ValidPassword(),
119 129 v.UnicodeString(strip=False, min=6, not_empty=True)
120 130 )
121 131 password_confirmation = All(
122 132 v.ValidPassword(),
123 133 v.UnicodeString(strip=False, min=6, not_empty=False)
124 134 )
125 135
126 136 password_change = v.StringBoolean(if_missing=False)
127 137 create_repo_group = v.StringBoolean(if_missing=False)
128 138
129 139 active = v.StringBoolean(if_missing=False)
130 140 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
131 141 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
132 142 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
133 143 extern_name = v.UnicodeString(strip=True)
134 144 extern_type = v.UnicodeString(strip=True)
135 145 language = v.OneOf(available_languages, hideList=False,
136 146 testValueList=True, if_missing=None)
137 147 chained_validators = [v.ValidPasswordsMatch()]
138 148 return _UserForm
139 149
140 150
141 151 def UserGroupForm(edit=False, old_data=None, available_members=None,
142 152 allow_disabled=False):
143 153 old_data = old_data or {}
144 154 available_members = available_members or []
145 155
146 156 class _UserGroupForm(formencode.Schema):
147 157 allow_extra_fields = True
148 158 filter_extra_fields = True
149 159
150 160 users_group_name = All(
151 161 v.UnicodeString(strip=True, min=1, not_empty=True),
152 162 v.ValidUserGroup(edit, old_data)
153 163 )
154 164 user_group_description = v.UnicodeString(strip=True, min=1,
155 165 not_empty=False)
156 166
157 167 users_group_active = v.StringBoolean(if_missing=False)
158 168
159 169 if edit:
160 170 users_group_members = v.OneOf(
161 171 available_members, hideList=False, testValueList=True,
162 172 if_missing=None, not_empty=False
163 173 )
164 174 # this is user group owner
165 175 user = All(
166 176 v.UnicodeString(not_empty=True),
167 177 v.ValidRepoUser(allow_disabled))
168 178 return _UserGroupForm
169 179
170 180
171 181 def RepoGroupForm(edit=False, old_data=None, available_groups=None,
172 182 can_create_in_root=False, allow_disabled=False):
173 183 old_data = old_data or {}
174 184 available_groups = available_groups or []
175 185
176 186 class _RepoGroupForm(formencode.Schema):
177 187 allow_extra_fields = True
178 188 filter_extra_fields = False
179 189
180 190 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
181 191 v.SlugifyName(),)
182 192 group_description = v.UnicodeString(strip=True, min=1,
183 193 not_empty=False)
184 194 group_copy_permissions = v.StringBoolean(if_missing=False)
185 195
186 196 group_parent_id = v.OneOf(available_groups, hideList=False,
187 197 testValueList=True, not_empty=True)
188 198 enable_locking = v.StringBoolean(if_missing=False)
189 199 chained_validators = [
190 200 v.ValidRepoGroup(edit, old_data, can_create_in_root)]
191 201
192 202 if edit:
193 203 # this is repo group owner
194 204 user = All(
195 205 v.UnicodeString(not_empty=True),
196 206 v.ValidRepoUser(allow_disabled))
197 207
198 208 return _RepoGroupForm
199 209
200 210
201 211 def RegisterForm(edit=False, old_data={}):
202 212 class _RegisterForm(formencode.Schema):
203 213 allow_extra_fields = True
204 214 filter_extra_fields = True
205 215 username = All(
206 216 v.ValidUsername(edit, old_data),
207 217 v.UnicodeString(strip=True, min=1, not_empty=True)
208 218 )
209 219 password = All(
210 220 v.ValidPassword(),
211 221 v.UnicodeString(strip=False, min=6, not_empty=True)
212 222 )
213 223 password_confirmation = All(
214 224 v.ValidPassword(),
215 225 v.UnicodeString(strip=False, min=6, not_empty=True)
216 226 )
217 227 active = v.StringBoolean(if_missing=False)
218 228 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
219 229 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
220 230 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
221 231
222 232 chained_validators = [v.ValidPasswordsMatch()]
223 233
224 234 return _RegisterForm
225 235
226 236
227 237 def PasswordResetForm():
228 238 class _PasswordResetForm(formencode.Schema):
229 239 allow_extra_fields = True
230 240 filter_extra_fields = True
231 241 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
232 242 return _PasswordResetForm
233 243
234 244
235 245 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None,
236 246 allow_disabled=False):
237 247 old_data = old_data or {}
238 248 repo_groups = repo_groups or []
239 249 landing_revs = landing_revs or []
240 250 supported_backends = BACKENDS.keys()
241 251
242 252 class _RepoForm(formencode.Schema):
243 253 allow_extra_fields = True
244 254 filter_extra_fields = False
245 255 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
246 256 v.SlugifyName())
247 257 repo_group = All(v.CanWriteGroup(old_data),
248 258 v.OneOf(repo_groups, hideList=True))
249 259 repo_type = v.OneOf(supported_backends, required=False,
250 260 if_missing=old_data.get('repo_type'))
251 261 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
252 262 repo_private = v.StringBoolean(if_missing=False)
253 263 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
254 264 repo_copy_permissions = v.StringBoolean(if_missing=False)
255 265 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
256 266
257 267 repo_enable_statistics = v.StringBoolean(if_missing=False)
258 268 repo_enable_downloads = v.StringBoolean(if_missing=False)
259 269 repo_enable_locking = v.StringBoolean(if_missing=False)
260 270
261 271 if edit:
262 272 # this is repo owner
263 273 user = All(
264 274 v.UnicodeString(not_empty=True),
265 275 v.ValidRepoUser(allow_disabled))
266 276 clone_uri_change = v.UnicodeString(
267 277 not_empty=False, if_missing=v.Missing)
268 278
269 279 chained_validators = [v.ValidCloneUri(),
270 280 v.ValidRepoName(edit, old_data)]
271 281 return _RepoForm
272 282
273 283
274 284 def RepoPermsForm():
275 285 class _RepoPermsForm(formencode.Schema):
276 286 allow_extra_fields = True
277 287 filter_extra_fields = False
278 288 chained_validators = [v.ValidPerms(type_='repo')]
279 289 return _RepoPermsForm
280 290
281 291
282 292 def RepoGroupPermsForm(valid_recursive_choices):
283 293 class _RepoGroupPermsForm(formencode.Schema):
284 294 allow_extra_fields = True
285 295 filter_extra_fields = False
286 296 recursive = v.OneOf(valid_recursive_choices)
287 297 chained_validators = [v.ValidPerms(type_='repo_group')]
288 298 return _RepoGroupPermsForm
289 299
290 300
291 301 def UserGroupPermsForm():
292 302 class _UserPermsForm(formencode.Schema):
293 303 allow_extra_fields = True
294 304 filter_extra_fields = False
295 305 chained_validators = [v.ValidPerms(type_='user_group')]
296 306 return _UserPermsForm
297 307
298 308
299 309 def RepoFieldForm():
300 310 class _RepoFieldForm(formencode.Schema):
301 311 filter_extra_fields = True
302 312 allow_extra_fields = True
303 313
304 314 new_field_key = All(v.FieldKey(),
305 315 v.UnicodeString(strip=True, min=3, not_empty=True))
306 316 new_field_value = v.UnicodeString(not_empty=False, if_missing=u'')
307 317 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
308 318 if_missing='str')
309 319 new_field_label = v.UnicodeString(not_empty=False)
310 320 new_field_desc = v.UnicodeString(not_empty=False)
311 321
312 322 return _RepoFieldForm
313 323
314 324
315 325 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
316 326 repo_groups=[], landing_revs=[]):
317 327 class _RepoForkForm(formencode.Schema):
318 328 allow_extra_fields = True
319 329 filter_extra_fields = False
320 330 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
321 331 v.SlugifyName())
322 332 repo_group = All(v.CanWriteGroup(),
323 333 v.OneOf(repo_groups, hideList=True))
324 334 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
325 335 description = v.UnicodeString(strip=True, min=1, not_empty=True)
326 336 private = v.StringBoolean(if_missing=False)
327 337 copy_permissions = v.StringBoolean(if_missing=False)
328 338 fork_parent_id = v.UnicodeString()
329 339 chained_validators = [v.ValidForkName(edit, old_data)]
330 340 landing_rev = v.OneOf(landing_revs, hideList=True)
331 341
332 342 return _RepoForkForm
333 343
334 344
335 345 def ApplicationSettingsForm():
336 346 class _ApplicationSettingsForm(formencode.Schema):
337 347 allow_extra_fields = True
338 348 filter_extra_fields = False
339 349 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
340 350 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
341 351 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
342 352 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
343 353 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
344 354 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
345 355
346 356 return _ApplicationSettingsForm
347 357
348 358
349 359 def ApplicationVisualisationForm():
350 360 class _ApplicationVisualisationForm(formencode.Schema):
351 361 allow_extra_fields = True
352 362 filter_extra_fields = False
353 363 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
354 364 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
355 365 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
356 366
357 367 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
358 368 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
359 369 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
360 370 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
361 371 rhodecode_show_version = v.StringBoolean(if_missing=False)
362 372 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
363 373 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
364 374 rhodecode_gravatar_url = v.UnicodeString(min=3)
365 375 rhodecode_clone_uri_tmpl = v.UnicodeString(min=3)
366 376 rhodecode_support_url = v.UnicodeString()
367 377 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
368 378 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
369 379
370 380 return _ApplicationVisualisationForm
371 381
372 382
373 383 class _BaseVcsSettingsForm(formencode.Schema):
374 384 allow_extra_fields = True
375 385 filter_extra_fields = False
376 386 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
377 387 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
378 388 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
379 389
380 390 extensions_largefiles = v.StringBoolean(if_missing=False)
381 391 phases_publish = v.StringBoolean(if_missing=False)
382 392
383 393 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
384 394 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
385 395 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
386 396
387 397
388 398 def ApplicationUiSettingsForm():
389 399 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
390 400 web_push_ssl = v.StringBoolean(if_missing=False)
391 401 paths_root_path = All(
392 402 v.ValidPath(),
393 403 v.UnicodeString(strip=True, min=1, not_empty=True)
394 404 )
395 405 extensions_hgsubversion = v.StringBoolean(if_missing=False)
396 406 extensions_hggit = v.StringBoolean(if_missing=False)
397 407 new_svn_branch = v.ValidSvnPattern(section='vcs_svn_branch')
398 408 new_svn_tag = v.ValidSvnPattern(section='vcs_svn_tag')
399 409
400 410 return _ApplicationUiSettingsForm
401 411
402 412
403 413 def RepoVcsSettingsForm(repo_name):
404 414 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
405 415 inherit_global_settings = v.StringBoolean(if_missing=False)
406 416 new_svn_branch = v.ValidSvnPattern(
407 417 section='vcs_svn_branch', repo_name=repo_name)
408 418 new_svn_tag = v.ValidSvnPattern(
409 419 section='vcs_svn_tag', repo_name=repo_name)
410 420
411 421 return _RepoVcsSettingsForm
412 422
413 423
414 424 def LabsSettingsForm():
415 425 class _LabSettingsForm(formencode.Schema):
416 426 allow_extra_fields = True
417 427 filter_extra_fields = False
418 428
419 429 rhodecode_proxy_subversion_http_requests = v.StringBoolean(
420 430 if_missing=False)
421 431 rhodecode_subversion_http_server_url = v.UnicodeString(
422 432 strip=True, if_missing=None)
423 433
424 434 return _LabSettingsForm
425 435
426 436
427 437 def ApplicationPermissionsForm(register_choices, extern_activate_choices):
428 438 class _DefaultPermissionsForm(formencode.Schema):
429 439 allow_extra_fields = True
430 440 filter_extra_fields = True
431 441
432 442 anonymous = v.StringBoolean(if_missing=False)
433 443 default_register = v.OneOf(register_choices)
434 444 default_register_message = v.UnicodeString()
435 445 default_extern_activate = v.OneOf(extern_activate_choices)
436 446
437 447 return _DefaultPermissionsForm
438 448
439 449
440 450 def ObjectPermissionsForm(repo_perms_choices, group_perms_choices,
441 451 user_group_perms_choices):
442 452 class _ObjectPermissionsForm(formencode.Schema):
443 453 allow_extra_fields = True
444 454 filter_extra_fields = True
445 455 overwrite_default_repo = v.StringBoolean(if_missing=False)
446 456 overwrite_default_group = v.StringBoolean(if_missing=False)
447 457 overwrite_default_user_group = v.StringBoolean(if_missing=False)
448 458 default_repo_perm = v.OneOf(repo_perms_choices)
449 459 default_group_perm = v.OneOf(group_perms_choices)
450 460 default_user_group_perm = v.OneOf(user_group_perms_choices)
451 461
452 462 return _ObjectPermissionsForm
453 463
454 464
455 465 def UserPermissionsForm(create_choices, create_on_write_choices,
456 466 repo_group_create_choices, user_group_create_choices,
457 467 fork_choices, inherit_default_permissions_choices):
458 468 class _DefaultPermissionsForm(formencode.Schema):
459 469 allow_extra_fields = True
460 470 filter_extra_fields = True
461 471
462 472 anonymous = v.StringBoolean(if_missing=False)
463 473
464 474 default_repo_create = v.OneOf(create_choices)
465 475 default_repo_create_on_write = v.OneOf(create_on_write_choices)
466 476 default_user_group_create = v.OneOf(user_group_create_choices)
467 477 default_repo_group_create = v.OneOf(repo_group_create_choices)
468 478 default_fork_create = v.OneOf(fork_choices)
469 479 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
470 480
471 481 return _DefaultPermissionsForm
472 482
473 483
474 484 def UserIndividualPermissionsForm():
475 485 class _DefaultPermissionsForm(formencode.Schema):
476 486 allow_extra_fields = True
477 487 filter_extra_fields = True
478 488
479 489 inherit_default_permissions = v.StringBoolean(if_missing=False)
480 490
481 491 return _DefaultPermissionsForm
482 492
483 493
484 494 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
485 495 class _DefaultsForm(formencode.Schema):
486 496 allow_extra_fields = True
487 497 filter_extra_fields = True
488 498 default_repo_type = v.OneOf(supported_backends)
489 499 default_repo_private = v.StringBoolean(if_missing=False)
490 500 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
491 501 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
492 502 default_repo_enable_locking = v.StringBoolean(if_missing=False)
493 503
494 504 return _DefaultsForm
495 505
496 506
497 507 def AuthSettingsForm():
498 508 class _AuthSettingsForm(formencode.Schema):
499 509 allow_extra_fields = True
500 510 filter_extra_fields = True
501 511 auth_plugins = All(v.ValidAuthPlugins(),
502 512 v.UniqueListFromString()(not_empty=True))
503 513
504 514 return _AuthSettingsForm
505 515
506 516
507 517 def UserExtraEmailForm():
508 518 class _UserExtraEmailForm(formencode.Schema):
509 519 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
510 520 return _UserExtraEmailForm
511 521
512 522
513 523 def UserExtraIpForm():
514 524 class _UserExtraIpForm(formencode.Schema):
515 525 ip = v.ValidIp()(not_empty=True)
516 526 return _UserExtraIpForm
517 527
518 528
519 529 def PullRequestForm(repo_id):
520 530 class _PullRequestForm(formencode.Schema):
521 531 allow_extra_fields = True
522 532 filter_extra_fields = True
523 533
524 534 user = v.UnicodeString(strip=True, required=True)
525 535 source_repo = v.UnicodeString(strip=True, required=True)
526 536 source_ref = v.UnicodeString(strip=True, required=True)
527 537 target_repo = v.UnicodeString(strip=True, required=True)
528 538 target_ref = v.UnicodeString(strip=True, required=True)
529 539 revisions = All(#v.NotReviewedRevisions(repo_id)(),
530 540 v.UniqueList()(not_empty=True))
531 541 review_members = v.UniqueList(convert=int)(not_empty=True)
532 542
533 543 pullrequest_title = v.UnicodeString(strip=True, required=True)
534 544 pullrequest_desc = v.UnicodeString(strip=True, required=False)
535 545
536 546 return _PullRequestForm
537 547
538 548
539 549 def GistForm(lifetime_options, acl_level_options):
540 550 class _GistForm(formencode.Schema):
541 551
542 552 gistid = All(v.UniqGistId(), v.UnicodeString(strip=True, min=3, not_empty=False, if_missing=None))
543 553 filename = All(v.BasePath()(),
544 554 v.UnicodeString(strip=True, required=False))
545 555 description = v.UnicodeString(required=False, if_missing=u'')
546 556 lifetime = v.OneOf(lifetime_options)
547 557 mimetype = v.UnicodeString(required=False, if_missing=None)
548 558 content = v.UnicodeString(required=True, not_empty=True)
549 559 public = v.UnicodeString(required=False, if_missing=u'')
550 560 private = v.UnicodeString(required=False, if_missing=u'')
551 561 acl_level = v.OneOf(acl_level_options)
552 562
553 563 return _GistForm
554 564
555 565
556 566 def IssueTrackerPatternsForm():
557 567 class _IssueTrackerPatternsForm(formencode.Schema):
558 568 allow_extra_fields = True
559 569 filter_extra_fields = False
560 570 chained_validators = [v.ValidPattern()]
561 571 return _IssueTrackerPatternsForm
@@ -1,2099 +1,2100 b''
1 1 //Primary CSS
2 2
3 3 //--- IMPORTS ------------------//
4 4
5 5 @import 'helpers';
6 6 @import 'mixins';
7 7 @import 'rcicons';
8 8 @import 'fonts';
9 9 @import 'variables';
10 10 @import 'bootstrap-variables';
11 11 @import 'form-bootstrap';
12 12 @import 'codemirror';
13 13 @import 'legacy_code_styles';
14 14 @import 'progress-bar';
15 15
16 16 @import 'type';
17 17 @import 'alerts';
18 18 @import 'buttons';
19 19 @import 'tags';
20 20 @import 'code-block';
21 21 @import 'examples';
22 22 @import 'login';
23 23 @import 'main-content';
24 24 @import 'select2';
25 25 @import 'comments';
26 26 @import 'panels-bootstrap';
27 27 @import 'panels';
28 @import 'deform';
28 29
29 30
30 31 //--- BASE ------------------//
31 32 .noscript-error {
32 33 top: 0;
33 34 left: 0;
34 35 width: 100%;
35 36 z-index: 101;
36 37 text-align: center;
37 38 font-family: @text-semibold;
38 39 font-size: 120%;
39 40 color: white;
40 41 background-color: @alert2;
41 42 padding: 5px 0 5px 0;
42 43 }
43 44
44 45 html {
45 46 display: table;
46 47 height: 100%;
47 48 width: 100%;
48 49 }
49 50
50 51 body {
51 52 display: table-cell;
52 53 width: 100%;
53 54 }
54 55
55 56 //--- LAYOUT ------------------//
56 57
57 58 .hidden{
58 59 display: none !important;
59 60 }
60 61
61 62 .box{
62 63 float: left;
63 64 width: 100%;
64 65 }
65 66
66 67 .browser-header {
67 68 clear: both;
68 69 }
69 70 .main {
70 71 clear: both;
71 72 padding:0 0 @pagepadding;
72 73 height: auto;
73 74
74 75 &:after { //clearfix
75 76 content:"";
76 77 clear:both;
77 78 width:100%;
78 79 display:block;
79 80 }
80 81 }
81 82
82 83 .action-link{
83 84 margin-left: @padding;
84 85 padding-left: @padding;
85 86 border-left: @border-thickness solid @border-default-color;
86 87 }
87 88
88 89 input + .action-link, .action-link.first{
89 90 border-left: none;
90 91 }
91 92
92 93 .action-link.last{
93 94 margin-right: @padding;
94 95 padding-right: @padding;
95 96 }
96 97
97 98 .action-link.active,
98 99 .action-link.active a{
99 100 color: @grey4;
100 101 }
101 102
102 103 ul.simple-list{
103 104 list-style: none;
104 105 margin: 0;
105 106 padding: 0;
106 107 }
107 108
108 109 .main-content {
109 110 padding-bottom: @pagepadding;
110 111 }
111 112
112 113 .wrapper {
113 114 position: relative;
114 115 max-width: @wrapper-maxwidth;
115 116 margin: 0 auto;
116 117 }
117 118
118 119 #content {
119 120 clear: both;
120 121 padding: 0 @contentpadding;
121 122 }
122 123
123 124 .advanced-settings-fields{
124 125 input{
125 126 margin-left: @textmargin;
126 127 margin-right: @padding/2;
127 128 }
128 129 }
129 130
130 131 .cs_files_title {
131 132 margin: @pagepadding 0 0;
132 133 }
133 134
134 135 input.inline[type="file"] {
135 136 display: inline;
136 137 }
137 138
138 139 .error_page {
139 140 margin: 10% auto;
140 141
141 142 h1 {
142 143 color: @grey2;
143 144 }
144
145
145 146 .error-branding {
146 147 font-family: @text-semibold;
147 148 color: @grey4;
148 149 }
149 150
150 151 .error_message {
151 152 font-family: @text-regular;
152 153 }
153 154
154 155 .sidebar {
155 156 min-height: 275px;
156 157 margin: 0;
157 158 padding: 0 0 @sidebarpadding @sidebarpadding;
158 159 border: none;
159 160 }
160 161
161 162 .main-content {
162 163 position: relative;
163 164 margin: 0 @sidebarpadding @sidebarpadding;
164 165 padding: 0 0 0 @sidebarpadding;
165 166 border-left: @border-thickness solid @grey5;
166 167
167 168 @media (max-width:767px) {
168 169 clear: both;
169 170 width: 100%;
170 171 margin: 0;
171 172 border: none;
172 173 }
173 174 }
174 175
175 176 .inner-column {
176 177 float: left;
177 178 width: 29.75%;
178 179 min-height: 150px;
179 180 margin: @sidebarpadding 2% 0 0;
180 181 padding: 0 2% 0 0;
181 182 border-right: @border-thickness solid @grey5;
182 183
183 184 @media (max-width:767px) {
184 185 clear: both;
185 186 width: 100%;
186 187 border: none;
187 188 }
188 189
189 190 ul {
190 191 padding-left: 1.25em;
191 192 }
192 193
193 194 &:last-child {
194 195 margin: @sidebarpadding 0 0;
195 196 border: none;
196 197 }
197 198
198 199 h4 {
199 200 margin: 0 0 @padding;
200 201 font-family: @text-semibold;
201 202 }
202 203 }
203 204 }
204 205 .error-page-logo {
205 206 width: 130px;
206 207 height: 160px;
207 208 }
208 209
209 210 // HEADER
210 211 .header {
211 212
212 213 // TODO: johbo: Fix login pages, so that they work without a min-height
213 214 // for the header and then remove the min-height. I chose a smaller value
214 215 // intentionally here to avoid rendering issues in the main navigation.
215 216 min-height: 49px;
216 217
217 218 position: relative;
218 219 vertical-align: bottom;
219 220 padding: 0 @header-padding;
220 221 background-color: @grey2;
221 222 color: @grey5;
222 223
223 224 .title {
224 225 overflow: visible;
225 226 }
226 227
227 228 &:before,
228 229 &:after {
229 230 content: "";
230 231 clear: both;
231 232 width: 100%;
232 233 }
233 234
234 235 // TODO: johbo: Avoids breaking "Repositories" chooser
235 236 .select2-container .select2-choice .select2-arrow {
236 237 display: none;
237 238 }
238 239 }
239 240
240 241 #header-inner {
241 242 &.title {
242 243 margin: 0;
243 244 }
244 245 &:before,
245 246 &:after {
246 247 content: "";
247 248 clear: both;
248 249 }
249 250 }
250 251
251 252 // Gists
252 253 #files_data {
253 254 clear: both; //for firefox
254 255 }
255 256 #gistid {
256 257 margin-right: @padding;
257 258 }
258 259
259 260 // Global Settings Editor
260 261 .textarea.editor {
261 262 float: left;
262 263 position: relative;
263 264 max-width: @texteditor-width;
264 265
265 266 select {
266 267 position: absolute;
267 268 top:10px;
268 269 right:0;
269 270 }
270 271
271 272 .CodeMirror {
272 273 margin: 0;
273 274 }
274 275
275 276 .help-block {
276 277 margin: 0 0 @padding;
277 278 padding:.5em;
278 279 background-color: @grey6;
279 280 }
280 281 }
281 282
282 283 ul.auth_plugins {
283 284 margin: @padding 0 @padding @legend-width;
284 285 padding: 0;
285 286
286 287 li {
287 288 margin-bottom: @padding;
288 289 line-height: 1em;
289 290 list-style-type: none;
290 291
291 292 .auth_buttons .btn {
292 293 margin-right: @padding;
293 294 }
294 295
295 296 &:before { content: none; }
296 297 }
297 298 }
298 299
299 300
300 301 // My Account PR list
301 302
302 303 #show_closed {
303 304 margin: 0 1em 0 0;
304 305 }
305 306
306 307 .pullrequestlist {
307 308 .closed {
308 309 background-color: @grey6;
309 310 }
310 311 .td-status {
311 312 padding-left: .5em;
312 313 }
313 314 .log-container .truncate {
314 315 height: 2.75em;
315 316 white-space: pre-line;
316 317 }
317 318 table.rctable .user {
318 319 padding-left: 0;
319 320 }
320 321 table.rctable {
321 322 td.td-description,
322 323 .rc-user {
323 324 min-width: auto;
324 325 }
325 326 }
326 327 }
327 328
328 329 // Pull Requests
329 330
330 331 .pullrequests_section_head {
331 332 display: block;
332 333 clear: both;
333 334 margin: @padding 0;
334 335 font-family: @text-bold;
335 336 }
336 337
337 338 .pr-origininfo, .pr-targetinfo {
338 339 position: relative;
339 340
340 341 .tag {
341 342 display: inline-block;
342 343 margin: 0 1em .5em 0;
343 344 }
344 345
345 346 .clone-url {
346 347 display: inline-block;
347 348 margin: 0 0 .5em 0;
348 349 padding: 0;
349 350 line-height: 1.2em;
350 351 }
351 352 }
352 353
353 354 .pr-pullinfo {
354 355 clear: both;
355 356 margin: .5em 0;
356 357 }
357 358
358 359 #pr-title-input {
359 360 width: 72%;
360 361 font-size: 1em;
361 362 font-family: @text-bold;
362 363 margin: 0;
363 364 padding: 0 0 0 @padding/4;
364 365 line-height: 1.7em;
365 366 color: @text-color;
366 367 letter-spacing: .02em;
367 368 }
368 369
369 370 #pullrequest_title {
370 371 width: 100%;
371 372 box-sizing: border-box;
372 373 }
373 374
374 375 #pr_open_message {
375 376 border: @border-thickness solid #fff;
376 377 border-radius: @border-radius;
377 378 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
378 379 text-align: right;
379 380 overflow: hidden;
380 381 }
381 382
382 383 .pr-submit-button {
383 384 float: right;
384 385 margin: 0 0 0 5px;
385 386 }
386 387
387 388 .pr-spacing-container {
388 389 padding: 20px;
389 390 clear: both
390 391 }
391 392
392 393 #pr-description-input {
393 394 margin-bottom: 0;
394 395 }
395 396
396 397 .pr-description-label {
397 398 vertical-align: top;
398 399 }
399 400
400 401 .perms_section_head {
401 402 min-width: 625px;
402 403
403 404 h2 {
404 405 margin-bottom: 0;
405 406 }
406 407
407 408 .label-checkbox {
408 409 float: left;
409 410 }
410 411
411 412 &.field {
412 413 margin: @space 0 @padding;
413 414 }
414 415
415 416 &:first-child.field {
416 417 margin-top: 0;
417 418
418 419 .label {
419 420 margin-top: 0;
420 421 padding-top: 0;
421 422 }
422 423
423 424 .radios {
424 425 padding-top: 0;
425 426 }
426 427 }
427 428
428 429 .radios {
429 430 float: right;
430 431 position: relative;
431 432 width: 405px;
432 433 }
433 434 }
434 435
435 436 //--- MODULES ------------------//
436 437
437 438
438 439 // Fixed Sidebar Column
439 440 .sidebar-col-wrapper {
440 441 padding-left: @sidebar-all-width;
441 442
442 443 .sidebar {
443 444 width: @sidebar-width;
444 445 margin-left: -@sidebar-all-width;
445 446 }
446 447 }
447 448
448 449 .sidebar-col-wrapper.scw-small {
449 450 padding-left: @sidebar-small-all-width;
450 451
451 452 .sidebar {
452 453 width: @sidebar-small-width;
453 454 margin-left: -@sidebar-small-all-width;
454 455 }
455 456 }
456 457
457 458
458 459 // FOOTER
459 460 #footer {
460 461 padding: 0;
461 462 text-align: center;
462 463 vertical-align: middle;
463 464 color: @grey2;
464 465 background-color: @grey6;
465 466
466 467 p {
467 468 margin: 0;
468 469 padding: 1em;
469 470 line-height: 1em;
470 471 }
471 472
472 473 .server-instance { //server instance
473 474 display: none;
474 475 }
475 476
476 477 .title {
477 478 float: none;
478 479 margin: 0 auto;
479 480 }
480 481 }
481 482
482 483 button.close {
483 484 padding: 0;
484 485 cursor: pointer;
485 486 background: transparent;
486 487 border: 0;
487 488 .box-shadow(none);
488 489 -webkit-appearance: none;
489 490 }
490 491
491 492 .close {
492 493 float: right;
493 494 font-size: 21px;
494 495 font-family: @text-bootstrap;
495 496 line-height: 1em;
496 497 font-weight: bold;
497 498 color: @grey2;
498 499
499 500 &:hover,
500 501 &:focus {
501 502 color: @grey1;
502 503 text-decoration: none;
503 504 cursor: pointer;
504 505 }
505 506 }
506 507
507 508 // GRID
508 509 .sorting,
509 510 .sorting_desc,
510 511 .sorting_asc {
511 512 cursor: pointer;
512 513 }
513 514 .sorting_desc:after {
514 515 content: "\00A0\25B2";
515 516 font-size: .75em;
516 517 }
517 518 .sorting_asc:after {
518 519 content: "\00A0\25BC";
519 520 font-size: .68em;
520 521 }
521 522
522 523
523 524 .user_auth_tokens {
524 525
525 526 &.truncate {
526 527 white-space: nowrap;
527 528 overflow: hidden;
528 529 text-overflow: ellipsis;
529 530 }
530 531
531 532 .fields .field .input {
532 533 margin: 0;
533 534 }
534 535
535 536 input#description {
536 537 width: 100px;
537 538 margin: 0;
538 539 }
539 540
540 541 .drop-menu {
541 542 // TODO: johbo: Remove this, should work out of the box when
542 543 // having multiple inputs inline
543 544 margin: 0 0 0 5px;
544 545 }
545 546 }
546 547 #user_list_table {
547 548 .closed {
548 549 background-color: @grey6;
549 550 }
550 551 }
551 552
552 553
553 554 input {
554 555 &.disabled {
555 556 opacity: .5;
556 557 }
557 558 }
558 559
559 560 // remove extra padding in firefox
560 561 input::-moz-focus-inner { border:0; padding:0 }
561 562
562 563 .adjacent input {
563 564 margin-bottom: @padding;
564 565 }
565 566
566 567 .permissions_boxes {
567 568 display: block;
568 569 }
569 570
570 571 //TODO: lisa: this should be in tables
571 572 .show_more_col {
572 573 width: 20px;
573 574 }
574 575
575 576 //FORMS
576 577
577 578 .medium-inline,
578 579 input#description.medium-inline {
579 580 display: inline;
580 581 width: @medium-inline-input-width;
581 582 min-width: 100px;
582 583 }
583 584
584 585 select {
585 586 //reset
586 587 -webkit-appearance: none;
587 588 -moz-appearance: none;
588 589
589 590 display: inline-block;
590 591 height: 28px;
591 592 width: auto;
592 593 margin: 0 @padding @padding 0;
593 594 padding: 0 18px 0 8px;
594 595 line-height:1em;
595 596 font-size: @basefontsize;
596 597 border: @border-thickness solid @rcblue;
597 598 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
598 599 color: @rcblue;
599 600
600 601 &:after {
601 602 content: "\00A0\25BE";
602 603 }
603 604
604 605 &:focus {
605 606 outline: none;
606 607 }
607 608 }
608 609
609 610 option {
610 611 &:focus {
611 612 outline: none;
612 613 }
613 614 }
614 615
615 616 input,
616 617 textarea {
617 618 padding: @input-padding;
618 619 border: @input-border-thickness solid @border-highlight-color;
619 620 .border-radius (@border-radius);
620 621 font-family: @text-light;
621 622 font-size: @basefontsize;
622 623
623 624 &.input-sm {
624 625 padding: 5px;
625 626 }
626 627
627 628 &#description {
628 629 min-width: @input-description-minwidth;
629 630 min-height: 1em;
630 631 padding: 10px;
631 632 }
632 633 }
633 634
634 635 .field-sm {
635 636 input,
636 637 textarea {
637 638 padding: 5px;
638 639 }
639 640 }
640 641
641 642 textarea {
642 643 display: block;
643 644 clear: both;
644 645 width: 100%;
645 646 min-height: 100px;
646 647 margin-bottom: @padding;
647 648 .box-sizing(border-box);
648 649 overflow: auto;
649 650 }
650 651
651 652 label {
652 653 font-family: @text-light;
653 654 }
654 655
655 656 // GRAVATARS
656 657 // centers gravatar on username to the right
657 658
658 659 .gravatar {
659 660 display: inline;
660 661 min-width: 16px;
661 662 min-height: 16px;
662 663 margin: -5px 0;
663 664 padding: 0;
664 665 line-height: 1em;
665 666 border: 1px solid @grey4;
666 667
667 668 &.gravatar-large {
668 669 margin: -0.5em .25em -0.5em 0;
669 670 }
670 671
671 672 & + .user {
672 673 display: inline;
673 674 margin: 0;
674 675 padding: 0 0 0 .17em;
675 676 line-height: 1em;
676 677 }
677 678 }
678 679
679 680 .user-inline-data {
680 681 display: inline-block;
681 682 float: left;
682 683 padding-left: .5em;
683 684 line-height: 1.3em;
684 685 }
685 686
686 687 .rc-user { // gravatar + user wrapper
687 688 float: left;
688 689 position: relative;
689 690 min-width: 100px;
690 691 max-width: 200px;
691 692 min-height: (@gravatar-size + @border-thickness * 2); // account for border
692 693 display: block;
693 694 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
694 695
695 696
696 697 .gravatar {
697 698 display: block;
698 699 position: absolute;
699 700 top: 0;
700 701 left: 0;
701 702 min-width: @gravatar-size;
702 703 min-height: @gravatar-size;
703 704 margin: 0;
704 705 }
705 706
706 707 .user {
707 708 display: block;
708 709 max-width: 175px;
709 710 padding-top: 2px;
710 711 overflow: hidden;
711 712 text-overflow: ellipsis;
712 713 }
713 714 }
714 715
715 716 .gist-gravatar,
716 717 .journal_container {
717 718 .gravatar-large {
718 719 margin: 0 .5em -10px 0;
719 720 }
720 721 }
721 722
722 723
723 724 // ADMIN SETTINGS
724 725
725 726 // Tag Patterns
726 727 .tag_patterns {
727 728 .tag_input {
728 729 margin-bottom: @padding;
729 730 }
730 731 }
731 732
732 733 .locked_input {
733 734 position: relative;
734 735
735 736 input {
736 737 display: inline;
737 738 margin-top: 3px;
738 739 }
739 740
740 741 br {
741 742 display: none;
742 743 }
743 744
744 745 .error-message {
745 746 float: left;
746 747 width: 100%;
747 748 }
748 749
749 750 .lock_input_button {
750 751 display: inline;
751 752 }
752 753
753 754 .help-block {
754 755 clear: both;
755 756 }
756 757 }
757 758
758 759 // Notifications
759 760
760 761 .notifications_buttons {
761 762 margin: 0 0 @space 0;
762 763 padding: 0;
763 764
764 765 .btn {
765 766 display: inline-block;
766 767 }
767 768 }
768 769
769 770 .notification-list {
770 771
771 772 div {
772 773 display: inline-block;
773 774 vertical-align: middle;
774 775 }
775 776
776 777 .container {
777 778 display: block;
778 779 margin: 0 0 @padding 0;
779 780 }
780 781
781 782 .delete-notifications {
782 783 margin-left: @padding;
783 784 text-align: right;
784 785 cursor: pointer;
785 786 }
786 787
787 788 .read-notifications {
788 789 margin-left: @padding/2;
789 790 text-align: right;
790 791 width: 35px;
791 792 cursor: pointer;
792 793 }
793 794
794 795 .icon-minus-sign {
795 796 color: @alert2;
796 797 }
797 798
798 799 .icon-ok-sign {
799 800 color: @alert1;
800 801 }
801 802 }
802 803
803 804 .user_settings {
804 805 float: left;
805 806 clear: both;
806 807 display: block;
807 808 width: 100%;
808 809
809 810 .gravatar_box {
810 811 margin-bottom: @padding;
811 812
812 813 &:after {
813 814 content: " ";
814 815 clear: both;
815 816 width: 100%;
816 817 }
817 818 }
818 819
819 820 .fields .field {
820 821 clear: both;
821 822 }
822 823 }
823 824
824 825 .advanced_settings {
825 826 margin-bottom: @space;
826 827
827 828 .help-block {
828 829 margin-left: 0;
829 830 }
830 831
831 832 button + .help-block {
832 833 margin-top: @padding;
833 834 }
834 835 }
835 836
836 837 // admin settings radio buttons and labels
837 838 .label-2 {
838 839 float: left;
839 840 width: @label2-width;
840 841
841 842 label {
842 843 color: @grey1;
843 844 }
844 845 }
845 846 .checkboxes {
846 847 float: left;
847 848 width: @checkboxes-width;
848 849 margin-bottom: @padding;
849 850
850 851 .checkbox {
851 852 width: 100%;
852 853
853 854 label {
854 855 margin: 0;
855 856 padding: 0;
856 857 }
857 858 }
858 859
859 860 .checkbox + .checkbox {
860 861 display: inline-block;
861 862 }
862 863
863 864 label {
864 865 margin-right: 1em;
865 866 }
866 867 }
867 868
868 869 // CHANGELOG
869 870 .container_header {
870 871 float: left;
871 872 display: block;
872 873 width: 100%;
873 874 margin: @padding 0 @padding;
874 875
875 876 #filter_changelog {
876 877 float: left;
877 878 margin-right: @padding;
878 879 }
879 880
880 881 .breadcrumbs_light {
881 882 display: inline-block;
882 883 }
883 884 }
884 885
885 886 .info_box {
886 887 float: right;
887 888 }
888 889
889 890
890 891 #graph_nodes {
891 892 padding-top: 43px;
892 893 }
893 894
894 895 #graph_content{
895 896
896 897 // adjust for table headers so that graph renders properly
897 898 // #graph_nodes padding - table cell padding
898 899 padding-top: (@space - (@basefontsize * 2.4));
899 900
900 901 &.graph_full_width {
901 902 width: 100%;
902 903 max-width: 100%;
903 904 }
904 905 }
905 906
906 907 #graph {
907 908 .flag_status {
908 909 margin: 0;
909 910 }
910 911
911 912 .pagination-left {
912 913 float: left;
913 914 clear: both;
914 915 }
915 916
916 917 .log-container {
917 918 max-width: 345px;
918 919
919 920 .message{
920 921 max-width: 340px;
921 922 }
922 923 }
923 924
924 925 .graph-col-wrapper {
925 926 padding-left: 110px;
926 927
927 928 #graph_nodes {
928 929 width: 100px;
929 930 margin-left: -110px;
930 931 float: left;
931 932 clear: left;
932 933 }
933 934 }
934 935 }
935 936
936 937 #filter_changelog {
937 938 float: left;
938 939 }
939 940
940 941
941 942 //--- THEME ------------------//
942 943
943 944 #logo {
944 945 float: left;
945 946 margin: 9px 0 0 0;
946 947
947 948 .header {
948 949 background-color: transparent;
949 950 }
950 951
951 952 a {
952 953 display: inline-block;
953 954 }
954 955
955 956 img {
956 957 height:30px;
957 958 }
958 959 }
959 960
960 961 .logo-wrapper {
961 962 float:left;
962 963 }
963 964
964 965 .branding{
965 966 float: left;
966 967 padding: 9px 2px;
967 968 line-height: 1em;
968 969 font-size: @navigation-fontsize;
969 970 }
970 971
971 972 img {
972 973 border: none;
973 974 outline: none;
974 975 }
975 976 user-profile-header
976 977 label {
977 978
978 979 input[type="checkbox"] {
979 980 margin-right: 1em;
980 981 }
981 982 input[type="radio"] {
982 983 margin-right: 1em;
983 984 }
984 985 }
985 986
986 987 .flag_status {
987 988 margin: 2px 8px 6px 2px;
988 989 &.under_review {
989 990 .circle(5px, @alert3);
990 991 }
991 992 &.approved {
992 993 .circle(5px, @alert1);
993 994 }
994 995 &.rejected,
995 996 &.forced_closed{
996 997 .circle(5px, @alert2);
997 998 }
998 999 &.not_reviewed {
999 1000 .circle(5px, @grey5);
1000 1001 }
1001 1002 }
1002 1003
1003 1004 .flag_status_comment_box {
1004 1005 margin: 5px 6px 0px 2px;
1005 1006 }
1006 1007 .test_pattern_preview {
1007 1008 margin: @space 0;
1008 1009
1009 1010 p {
1010 1011 margin-bottom: 0;
1011 1012 border-bottom: @border-thickness solid @border-default-color;
1012 1013 color: @grey3;
1013 1014 }
1014 1015
1015 1016 .btn {
1016 1017 margin-bottom: @padding;
1017 1018 }
1018 1019 }
1019 1020 #test_pattern_result {
1020 1021 display: none;
1021 1022 &:extend(pre);
1022 1023 padding: .9em;
1023 1024 color: @grey3;
1024 1025 background-color: @grey7;
1025 border-right: @border-thickness solid @border-default-color;
1026 border-bottom: @border-thickness solid @border-default-color;
1027 border-left: @border-thickness solid @border-default-color;
1026 border-right: @border-thickness solid @border-default-color;
1027 border-bottom: @border-thickness solid @border-default-color;
1028 border-left: @border-thickness solid @border-default-color;
1028 1029 }
1029 1030
1030 1031 #repo_vcs_settings {
1031 1032 #inherit_overlay_vcs_default {
1032 1033 display: none;
1033 1034 }
1034 1035 #inherit_overlay_vcs_custom {
1035 1036 display: custom;
1036 1037 }
1037 1038 &.inherited {
1038 1039 #inherit_overlay_vcs_default {
1039 1040 display: block;
1040 1041 }
1041 1042 #inherit_overlay_vcs_custom {
1042 1043 display: none;
1043 1044 }
1044 1045 }
1045 1046 }
1046 1047
1047 1048 .issue-tracker-link {
1048 1049 color: @rcblue;
1049 1050 }
1050 1051
1051 1052 // Issue Tracker Table Show/Hide
1052 1053 #repo_issue_tracker {
1053 1054 #inherit_overlay {
1054 1055 display: none;
1055 1056 }
1056 1057 #custom_overlay {
1057 1058 display: custom;
1058 1059 }
1059 1060 &.inherited {
1060 1061 #inherit_overlay {
1061 1062 display: block;
1062 1063 }
1063 1064 #custom_overlay {
1064 1065 display: none;
1065 1066 }
1066 1067 }
1067 1068 }
1068 1069 table.issuetracker {
1069 1070 &.readonly {
1070 1071 tr, td {
1071 1072 color: @grey3;
1072 1073 }
1073 1074 }
1074 1075 .edit {
1075 1076 display: none;
1076 1077 }
1077 1078 .editopen {
1078 1079 .edit {
1079 1080 display: inline;
1080 1081 }
1081 1082 .entry {
1082 1083 display: none;
1083 1084 }
1084 1085 }
1085 1086 tr td.td-action {
1086 1087 min-width: 117px;
1087 1088 }
1088 1089 td input {
1089 1090 max-width: none;
1090 1091 min-width: 30px;
1091 1092 width: 80%;
1092 1093 }
1093 1094 .issuetracker_pref input {
1094 1095 width: 40%;
1095 1096 }
1096 1097 input.edit_issuetracker_update {
1097 1098 margin-right: 0;
1098 1099 width: auto;
1099 1100 }
1100 1101 }
1101 1102
1102 1103
1103 1104 //Permissions Settings
1104 1105 #add_perm {
1105 1106 margin: 0 0 @padding;
1106 1107 cursor: pointer;
1107 1108 }
1108 1109
1109 1110 .perm_ac {
1110 1111 input {
1111 1112 width: 95%;
1112 1113 }
1113 1114 }
1114 1115
1115 1116 .autocomplete-suggestions {
1116 1117 width: auto !important; // overrides autocomplete.js
1117 1118 margin: 0;
1118 1119 border: @border-thickness solid @rcblue;
1119 1120 border-radius: @border-radius;
1120 1121 color: @rcblue;
1121 1122 background-color: white;
1122 1123 }
1123 1124 .autocomplete-selected {
1124 1125 background: #F0F0F0;
1125 1126 }
1126 1127 .ac-container-wrap {
1127 1128 margin: 0;
1128 1129 padding: 8px;
1129 1130 border-bottom: @border-thickness solid @rclightblue;
1130 1131 list-style-type: none;
1131 1132 cursor: pointer;
1132 1133
1133 1134 &:hover {
1134 1135 background-color: @rclightblue;
1135 1136 }
1136 1137
1137 1138 img {
1138 1139 margin-right: 1em;
1139 1140 }
1140 1141
1141 1142 strong {
1142 1143 font-weight: normal;
1143 1144 }
1144 1145 }
1145 1146
1146 1147 // Settings Dropdown
1147 1148 .user-menu .container {
1148 1149 padding: 0 4px;
1149 1150 margin: 0;
1150 1151 }
1151 1152
1152 1153 .user-menu .gravatar {
1153 1154 cursor: pointer;
1154 1155 }
1155 1156
1156 1157 .codeblock {
1157 1158 margin-bottom: @padding;
1158 1159 clear: both;
1159 1160
1160 1161 .stats{
1161 1162 overflow: hidden;
1162 1163 }
1163 1164
1164 1165 .message{
1165 1166 textarea{
1166 1167 margin: 0;
1167 1168 }
1168 1169 }
1169 1170
1170 1171 .code-header {
1171 1172 .stats {
1172 1173 line-height: 2em;
1173 1174
1174 1175 .revision_id {
1175 1176 margin-left: 0;
1176 1177 }
1177 1178 .buttons {
1178 1179 padding-right: 0;
1179 1180 }
1180 1181 }
1181 1182
1182 1183 .item{
1183 1184 margin-right: 0.5em;
1184 1185 }
1185 1186 }
1186 1187
1187 1188 #editor_container{
1188 1189 position: relative;
1189 1190 margin: @padding;
1190 1191 }
1191 1192 }
1192 1193
1193 1194 #file_history_container {
1194 1195 display: none;
1195 1196 }
1196 1197
1197 1198 .file-history-inner {
1198 1199 margin-bottom: 10px;
1199 1200 }
1200 1201
1201 1202 // Pull Requests
1202 1203 .summary-details {
1203 1204 width: 72%;
1204 1205 }
1205 1206 .pr-summary {
1206 1207 border-bottom: @border-thickness solid @grey5;
1207 1208 margin-bottom: @space;
1208 1209 }
1209 1210 .reviewers-title {
1210 1211 width: 25%;
1211 1212 min-width: 200px;
1212 1213 }
1213 1214 .reviewers {
1214 1215 width: 25%;
1215 1216 min-width: 200px;
1216 1217 }
1217 1218 .reviewers ul li {
1218 1219 position: relative;
1219 1220 width: 100%;
1220 1221 margin-bottom: 8px;
1221 1222 }
1222 1223 .reviewers_member {
1223 1224 width: 100%;
1224 1225 overflow: auto;
1225 1226 }
1226 1227 .reviewer_status {
1227 1228 display: inline-block;
1228 1229 vertical-align: top;
1229 1230 width: 7%;
1230 1231 min-width: 20px;
1231 1232 height: 1.2em;
1232 1233 margin-top: 3px;
1233 1234 line-height: 1em;
1234 1235 }
1235 1236
1236 1237 .reviewer_name {
1237 1238 display: inline-block;
1238 1239 max-width: 83%;
1239 1240 padding-right: 20px;
1240 1241 vertical-align: middle;
1241 1242 line-height: 1;
1242 1243
1243 1244 .rc-user {
1244 1245 min-width: 0;
1245 1246 margin: -2px 1em 0 0;
1246 1247 }
1247 1248
1248 1249 .reviewer {
1249 1250 float: left;
1250 1251 }
1251 1252
1252 1253 &.to-delete {
1253 1254 .user,
1254 1255 .reviewer {
1255 1256 text-decoration: line-through;
1256 1257 }
1257 1258 }
1258 1259 }
1259 1260
1260 1261 .reviewer_member_remove {
1261 1262 position: absolute;
1262 1263 right: 0;
1263 1264 top: 0;
1264 1265 width: 16px;
1265 1266 margin-bottom: 10px;
1266 1267 padding: 0;
1267 1268 color: black;
1268 1269 }
1269 1270 .reviewer_member_status {
1270 1271 margin-top: 5px;
1271 1272 }
1272 1273 .pr-summary #summary{
1273 1274 width: 100%;
1274 1275 }
1275 1276 .pr-summary .action_button:hover {
1276 1277 border: 0;
1277 1278 cursor: pointer;
1278 1279 }
1279 1280 .pr-details-title {
1280 1281 padding-bottom: 8px;
1281 1282 border-bottom: @border-thickness solid @grey5;
1282 1283 .action_button {
1283 1284 color: @rcblue;
1284 1285 }
1285 1286 }
1286 1287 .pr-details-content {
1287 1288 margin-top: @textmargin;
1288 1289 margin-bottom: @textmargin;
1289 1290 }
1290 1291 .pr-description {
1291 1292 white-space:pre-wrap;
1292 1293 }
1293 1294 .group_members {
1294 1295 margin-top: 0;
1295 1296 padding: 0;
1296 1297 list-style: outside none none;
1297 1298 }
1298 1299 .reviewer_ac .ac-input {
1299 1300 width: 92%;
1300 1301 margin-bottom: 1em;
1301 1302 }
1302 1303 #update_commits {
1303 1304 float: right;
1304 1305 }
1305 1306 .compare_view_commits tr{
1306 1307 height: 20px;
1307 1308 }
1308 1309 .compare_view_commits td {
1309 1310 vertical-align: top;
1310 1311 padding-top: 10px;
1311 1312 }
1312 1313 .compare_view_commits .author {
1313 1314 margin-left: 5px;
1314 1315 }
1315 1316
1316 1317 .compare_view_files {
1317 1318 width: 100%;
1318 1319
1319 1320 td {
1320 1321 vertical-align: middle;
1321 1322 }
1322 1323 }
1323 1324
1324 1325 .compare_view_filepath {
1325 1326 color: @grey1;
1326 1327 }
1327 1328
1328 1329 .show_more {
1329 1330 display: inline-block;
1330 1331 position: relative;
1331 1332 vertical-align: middle;
1332 1333 width: 4px;
1333 1334 height: @basefontsize;
1334 1335
1335 1336 &:after {
1336 1337 content: "\00A0\25BE";
1337 1338 display: inline-block;
1338 1339 width:10px;
1339 1340 line-height: 5px;
1340 1341 font-size: 12px;
1341 1342 cursor: pointer;
1342 1343 }
1343 1344 }
1344 1345
1345 1346 .journal_more .show_more {
1346 1347 display: inline;
1347 1348
1348 1349 &:after {
1349 1350 content: none;
1350 1351 }
1351 1352 }
1352 1353
1353 1354 .open .show_more:after,
1354 1355 .select2-dropdown-open .show_more:after {
1355 1356 .rotate(180deg);
1356 1357 margin-left: 4px;
1357 1358 }
1358 1359
1359 1360
1360 1361 .compare_view_commits .collapse_commit:after {
1361 1362 cursor: pointer;
1362 1363 content: "\00A0\25B4";
1363 1364 margin-left: -3px;
1364 1365 font-size: 17px;
1365 1366 color: @grey4;
1366 1367 }
1367 1368
1368 1369 .diff_links {
1369 1370 margin-left: 8px;
1370 1371 }
1371 1372
1372 1373 p.ancestor {
1373 1374 margin: @padding 0;
1374 1375 }
1375 1376
1376 1377 .cs_icon_td input[type="checkbox"] {
1377 1378 display: none;
1378 1379 }
1379 1380
1380 1381 .cs_icon_td .expand_file_icon:after {
1381 1382 cursor: pointer;
1382 1383 content: "\00A0\25B6";
1383 1384 font-size: 12px;
1384 1385 color: @grey4;
1385 1386 }
1386 1387
1387 1388 .cs_icon_td .collapse_file_icon:after {
1388 1389 cursor: pointer;
1389 1390 content: "\00A0\25BC";
1390 1391 font-size: 12px;
1391 1392 color: @grey4;
1392 1393 }
1393 1394
1394 1395 /*new binary
1395 1396 NEW_FILENODE = 1
1396 1397 DEL_FILENODE = 2
1397 1398 MOD_FILENODE = 3
1398 1399 RENAMED_FILENODE = 4
1399 1400 COPIED_FILENODE = 5
1400 1401 CHMOD_FILENODE = 6
1401 1402 BIN_FILENODE = 7
1402 1403 */
1403 1404 .cs_files_expand {
1404 1405 font-size: @basefontsize + 5px;
1405 1406 line-height: 1.8em;
1406 1407 float: right;
1407 1408 }
1408 1409
1409 1410 .cs_files_expand span{
1410 1411 color: @rcblue;
1411 1412 cursor: pointer;
1412 1413 }
1413 1414 .cs_files {
1414 1415 clear: both;
1415 1416 padding-bottom: @padding;
1416 1417
1417 1418 .cur_cs {
1418 1419 margin: 10px 2px;
1419 1420 font-weight: bold;
1420 1421 }
1421 1422
1422 1423 .node {
1423 1424 float: left;
1424 1425 }
1425 1426
1426 1427 .changes {
1427 1428 float: right;
1428 1429 color: white;
1429 1430 font-size: @basefontsize - 4px;
1430 1431 margin-top: 4px;
1431 1432 opacity: 0.6;
1432 1433 filter: Alpha(opacity=60); /* IE8 and earlier */
1433 1434
1434 1435 .added {
1435 1436 background-color: @alert1;
1436 1437 float: left;
1437 1438 text-align: center;
1438 1439 }
1439 1440
1440 1441 .deleted {
1441 1442 background-color: @alert2;
1442 1443 float: left;
1443 1444 text-align: center;
1444 1445 }
1445 1446
1446 1447 .bin {
1447 1448 background-color: @alert1;
1448 1449 text-align: center;
1449 1450 }
1450 1451
1451 1452 /*new binary*/
1452 1453 .bin.bin1 {
1453 1454 background-color: @alert1;
1454 1455 text-align: center;
1455 1456 }
1456 1457
1457 1458 /*deleted binary*/
1458 1459 .bin.bin2 {
1459 1460 background-color: @alert2;
1460 1461 text-align: center;
1461 1462 }
1462 1463
1463 1464 /*mod binary*/
1464 1465 .bin.bin3 {
1465 1466 background-color: @grey2;
1466 1467 text-align: center;
1467 1468 }
1468 1469
1469 1470 /*rename file*/
1470 1471 .bin.bin4 {
1471 1472 background-color: @alert4;
1472 1473 text-align: center;
1473 1474 }
1474 1475
1475 1476 /*copied file*/
1476 1477 .bin.bin5 {
1477 1478 background-color: @alert4;
1478 1479 text-align: center;
1479 1480 }
1480 1481
1481 1482 /*chmod file*/
1482 1483 .bin.bin6 {
1483 1484 background-color: @grey2;
1484 1485 text-align: center;
1485 1486 }
1486 1487 }
1487 1488 }
1488 1489
1489 1490 .cs_files .cs_added, .cs_files .cs_A,
1490 1491 .cs_files .cs_added, .cs_files .cs_M,
1491 1492 .cs_files .cs_added, .cs_files .cs_D {
1492 1493 height: 16px;
1493 1494 padding-right: 10px;
1494 1495 margin-top: 7px;
1495 1496 text-align: left;
1496 1497 }
1497 1498
1498 1499 .cs_icon_td {
1499 1500 min-width: 16px;
1500 1501 width: 16px;
1501 1502 }
1502 1503
1503 1504 .pull-request-merge {
1504 1505 padding: 10px 0;
1505 1506 margin-top: 10px;
1506 1507 margin-bottom: 20px;
1507 1508 }
1508 1509
1509 1510 .pull-request-merge .pull-request-wrap {
1510 1511 height: 25px;
1511 1512 padding: 5px 0;
1512 1513 }
1513 1514
1514 1515 .pull-request-merge span {
1515 1516 margin-right: 10px;
1516 1517 }
1517 1518 #close_pull_request {
1518 1519 margin-right: 0px;
1519 1520 }
1520 1521
1521 1522 .empty_data {
1522 1523 color: @grey4;
1523 1524 }
1524 1525
1525 1526 #changeset_compare_view_content {
1526 1527 margin-bottom: @space;
1527 1528 clear: both;
1528 1529 width: 100%;
1529 1530 box-sizing: border-box;
1530 1531 .border-radius(@border-radius);
1531 1532
1532 1533 .help-block {
1533 1534 margin: @padding 0;
1534 1535 color: @text-color;
1535 1536 }
1536 1537
1537 1538 .empty_data {
1538 1539 margin: @padding 0;
1539 1540 }
1540 1541
1541 1542 .alert {
1542 1543 margin-bottom: @space;
1543 1544 }
1544 1545 }
1545 1546
1546 1547 .table_disp {
1547 1548 .status {
1548 1549 width: auto;
1549 1550
1550 1551 .flag_status {
1551 1552 float: left;
1552 1553 }
1553 1554 }
1554 1555 }
1555 1556
1556 1557 .status_box_menu {
1557 1558 margin: 0;
1558 1559 }
1559 1560
1560 1561 .notification-table{
1561 1562 margin-bottom: @space;
1562 1563 display: table;
1563 1564 width: 100%;
1564 1565
1565 1566 .container{
1566 1567 display: table-row;
1567 1568
1568 1569 .notification-header{
1569 1570 border-bottom: @border-thickness solid @border-default-color;
1570 1571 }
1571 1572
1572 1573 .notification-subject{
1573 1574 display: table-cell;
1574 1575 }
1575 1576 }
1576 1577 }
1577 1578
1578 1579 // Notifications
1579 1580 .notification-header{
1580 1581 display: table;
1581 1582 width: 100%;
1582 1583 padding: floor(@basefontsize/2) 0;
1583 1584 line-height: 1em;
1584 1585
1585 1586 .desc, .delete-notifications, .read-notifications{
1586 1587 display: table-cell;
1587 1588 text-align: left;
1588 1589 }
1589 1590
1590 1591 .desc{
1591 1592 width: 1163px;
1592 1593 }
1593 1594
1594 1595 .delete-notifications, .read-notifications{
1595 1596 width: 35px;
1596 1597 min-width: 35px; //fixes when only one button is displayed
1597 1598 }
1598 1599 }
1599 1600
1600 1601 .notification-body {
1601 1602 .markdown-block,
1602 1603 .rst-block {
1603 1604 padding: @padding 0;
1604 1605 }
1605 1606
1606 1607 .notification-subject {
1607 1608 padding: @textmargin 0;
1608 1609 border-bottom: @border-thickness solid @border-default-color;
1609 1610 }
1610 1611 }
1611 1612
1612 1613
1613 1614 .notifications_buttons{
1614 1615 float: right;
1615 1616 }
1616 1617
1617 1618 // Repositories
1618 1619
1619 1620 #summary.fields{
1620 1621 display: table;
1621 1622
1622 1623 .field{
1623 1624 display: table-row;
1624 1625
1625 1626 .label-summary{
1626 1627 display: table-cell;
1627 1628 min-width: @label-summary-minwidth;
1628 1629 padding-top: @padding/2;
1629 1630 padding-bottom: @padding/2;
1630 1631 padding-right: @padding/2;
1631 1632 }
1632 1633
1633 1634 .input{
1634 1635 display: table-cell;
1635 1636 padding: @padding/2;
1636 1637
1637 1638 input{
1638 1639 min-width: 29em;
1639 1640 padding: @padding/4;
1640 1641 }
1641 1642 }
1642 1643 .statistics, .downloads{
1643 1644 .disabled{
1644 1645 color: @grey4;
1645 1646 }
1646 1647 }
1647 1648 }
1648 1649 }
1649 1650
1650 1651 #summary{
1651 1652 width: 70%;
1652 1653 }
1653 1654
1654 1655
1655 1656 // Journal
1656 1657 .journal.title {
1657 1658 h5 {
1658 1659 float: left;
1659 1660 margin: 0;
1660 1661 width: 70%;
1661 1662 }
1662 1663
1663 1664 ul {
1664 1665 float: right;
1665 1666 display: inline-block;
1666 1667 margin: 0;
1667 1668 width: 30%;
1668 1669 text-align: right;
1669 1670
1670 1671 li {
1671 1672 display: inline;
1672 1673 font-size: @journal-fontsize;
1673 1674 line-height: 1em;
1674 1675
1675 1676 &:before { content: none; }
1676 1677 }
1677 1678 }
1678 1679 }
1679 1680
1680 1681 .filterexample {
1681 1682 position: absolute;
1682 1683 top: 95px;
1683 1684 left: @contentpadding;
1684 1685 color: @rcblue;
1685 1686 font-size: 11px;
1686 1687 font-family: @text-regular;
1687 1688 cursor: help;
1688 1689
1689 1690 &:hover {
1690 1691 color: @rcdarkblue;
1691 1692 }
1692 1693
1693 1694 @media (max-width:768px) {
1694 1695 position: relative;
1695 1696 top: auto;
1696 1697 left: auto;
1697 1698 display: block;
1698 1699 }
1699 1700 }
1700 1701
1701 1702
1702 1703 #journal{
1703 1704 margin-bottom: @space;
1704 1705
1705 1706 .journal_day{
1706 1707 margin-bottom: @textmargin/2;
1707 1708 padding-bottom: @textmargin/2;
1708 1709 font-size: @journal-fontsize;
1709 1710 border-bottom: @border-thickness solid @border-default-color;
1710 1711 }
1711 1712
1712 1713 .journal_container{
1713 1714 margin-bottom: @space;
1714 1715
1715 1716 .journal_user{
1716 1717 display: inline-block;
1717 1718 }
1718 1719 .journal_action_container{
1719 1720 display: block;
1720 1721 margin-top: @textmargin;
1721 1722
1722 1723 div{
1723 1724 display: inline;
1724 1725 }
1725 1726
1726 1727 div.journal_action_params{
1727 1728 display: block;
1728 1729 }
1729 1730
1730 1731 div.journal_repo:after{
1731 1732 content: "\A";
1732 1733 white-space: pre;
1733 1734 }
1734 1735
1735 1736 div.date{
1736 1737 display: block;
1737 1738 margin-bottom: @textmargin;
1738 1739 }
1739 1740 }
1740 1741 }
1741 1742 }
1742 1743
1743 1744 // Files
1744 1745 .edit-file-title {
1745 1746 border-bottom: @border-thickness solid @border-default-color;
1746 1747
1747 1748 .breadcrumbs {
1748 1749 margin-bottom: 0;
1749 1750 }
1750 1751 }
1751 1752
1752 1753 .edit-file-fieldset {
1753 1754 margin-top: @sidebarpadding;
1754 1755
1755 1756 .fieldset {
1756 1757 .left-label {
1757 1758 width: 13%;
1758 1759 }
1759 1760 .right-content {
1760 1761 width: 87%;
1761 1762 max-width: 100%;
1762 1763 }
1763 1764 .filename-label {
1764 1765 margin-top: 13px;
1765 1766 }
1766 1767 .commit-message-label {
1767 1768 margin-top: 4px;
1768 1769 }
1769 1770 .file-upload-input {
1770 1771 input {
1771 1772 display: none;
1772 1773 }
1773 1774 }
1774 1775 p {
1775 1776 margin-top: 5px;
1776 1777 }
1777 1778
1778 1779 }
1779 1780 .custom-path-link {
1780 1781 margin-left: 5px;
1781 1782 }
1782 1783 #commit {
1783 1784 resize: vertical;
1784 1785 }
1785 1786 }
1786 1787
1787 1788 .delete-file-preview {
1788 1789 max-height: 250px;
1789 1790 }
1790 1791
1791 1792 .new-file,
1792 1793 #filter_activate,
1793 1794 #filter_deactivate {
1794 1795 float: left;
1795 1796 margin: 0 0 0 15px;
1796 1797 }
1797 1798
1798 1799 h3.files_location{
1799 1800 line-height: 2.4em;
1800 1801 }
1801 1802
1802 1803 .browser-nav {
1803 1804 display: table;
1804 1805 margin-bottom: @space;
1805 1806
1806 1807
1807 1808 .info_box {
1808 1809 display: inline-table;
1809 1810 height: 2.5em;
1810 1811
1811 1812 .browser-cur-rev, .info_box_elem {
1812 1813 display: table-cell;
1813 1814 vertical-align: middle;
1814 1815 }
1815 1816
1816 1817 .info_box_elem {
1817 1818 border-top: @border-thickness solid @rcblue;
1818 1819 border-bottom: @border-thickness solid @rcblue;
1819 1820
1820 1821 #at_rev, a {
1821 1822 padding: 0.6em 0.9em;
1822 1823 margin: 0;
1823 1824 .box-shadow(none);
1824 1825 border: 0;
1825 1826 height: 12px;
1826 1827 }
1827 1828
1828 1829 input#at_rev {
1829 1830 max-width: 50px;
1830 1831 text-align: right;
1831 1832 }
1832 1833
1833 1834 &.previous {
1834 1835 border: @border-thickness solid @rcblue;
1835 1836 .disabled {
1836 1837 color: @grey4;
1837 1838 cursor: not-allowed;
1838 1839 }
1839 1840 }
1840 1841
1841 1842 &.next {
1842 1843 border: @border-thickness solid @rcblue;
1843 1844 .disabled {
1844 1845 color: @grey4;
1845 1846 cursor: not-allowed;
1846 1847 }
1847 1848 }
1848 1849 }
1849 1850
1850 1851 .browser-cur-rev {
1851 1852
1852 1853 span{
1853 1854 margin: 0;
1854 1855 color: @rcblue;
1855 1856 height: 12px;
1856 1857 display: inline-block;
1857 1858 padding: 0.7em 1em ;
1858 1859 border: @border-thickness solid @rcblue;
1859 1860 margin-right: @padding;
1860 1861 }
1861 1862 }
1862 1863 }
1863 1864
1864 1865 .search_activate {
1865 1866 display: table-cell;
1866 1867 vertical-align: middle;
1867 1868
1868 1869 input, label{
1869 1870 margin: 0;
1870 1871 padding: 0;
1871 1872 }
1872 1873
1873 1874 input{
1874 1875 margin-left: @textmargin;
1875 1876 }
1876 1877
1877 1878 }
1878 1879 }
1879 1880
1880 1881 .browser-cur-rev{
1881 1882 margin-bottom: @textmargin;
1882 1883 }
1883 1884
1884 1885 #node_filter_box_loading{
1885 1886 .info_text;
1886 1887 }
1887 1888
1888 1889 .browser-search {
1889 1890 margin: -25px 0px 5px 0px;
1890 1891 }
1891 1892
1892 1893 .node-filter {
1893 1894 font-size: @repo-title-fontsize;
1894 1895 padding: 4px 0px 0px 0px;
1895 1896
1896 1897 .node-filter-path {
1897 1898 float: left;
1898 1899 color: @grey4;
1899 1900 }
1900 1901 .node-filter-input {
1901 1902 float: left;
1902 1903 margin: -2px 0px 0px 2px;
1903 1904 input {
1904 1905 padding: 2px;
1905 1906 border: none;
1906 1907 font-size: @repo-title-fontsize;
1907 1908 }
1908 1909 }
1909 1910 }
1910 1911
1911 1912
1912 1913 .browser-result{
1913 1914 td a{
1914 1915 margin-left: 0.5em;
1915 1916 display: inline-block;
1916 1917
1917 1918 em{
1918 1919 font-family: @text-bold;
1919 1920 }
1920 1921 }
1921 1922 }
1922 1923
1923 1924 .browser-highlight{
1924 1925 background-color: @grey5-alpha;
1925 1926 }
1926 1927
1927 1928
1928 1929 // Search
1929 1930
1930 1931 .search-form{
1931 1932 #q {
1932 1933 width: @search-form-width;
1933 1934 }
1934 1935 .fields{
1935 1936 margin: 0 0 @space;
1936 1937 }
1937 1938
1938 1939 label{
1939 1940 display: inline-block;
1940 1941 margin-right: @textmargin;
1941 1942 padding-top: 0.25em;
1942 1943 }
1943 1944
1944 1945
1945 1946 .results{
1946 1947 clear: both;
1947 1948 margin: 0 0 @padding;
1948 1949 }
1949 1950 }
1950 1951
1951 1952 div.search-feedback-items {
1952 1953 display: inline-block;
1953 1954 padding:0px 0px 0px 96px;
1954 1955 }
1955 1956
1956 div.search-code-body {
1957 div.search-code-body {
1957 1958 background-color: #ffffff; padding: 5px 0 5px 10px;
1958 1959 pre {
1959 1960 .match { background-color: #faffa6;}
1960 1961 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
1961 1962 }
1962 1963 }
1963 1964
1964 1965 .expand_commit.search {
1965 1966 .show_more.open {
1966 1967 height: auto;
1967 1968 max-height: none;
1968 1969 }
1969 1970 }
1970 1971
1971 1972 .search-results {
1972 1973
1973 1974 h2 {
1974 1975 margin-bottom: 0;
1975 1976 }
1976 1977 .codeblock {
1977 1978 border: none;
1978 1979 background: transparent;
1979 1980 }
1980 1981
1981 1982 .codeblock-header {
1982 1983 border: none;
1983 1984 background: transparent;
1984 1985 }
1985 1986
1986 1987 .code-body {
1987 1988 border: @border-thickness solid @border-default-color;
1988 1989 .border-radius(@border-radius);
1989 1990 }
1990 1991
1991 1992 .td-commit {
1992 1993 &:extend(pre);
1993 1994 border-bottom: @border-thickness solid @border-default-color;
1994 1995 }
1995 1996
1996 1997 .message {
1997 1998 height: auto;
1998 1999 max-width: 350px;
1999 2000 white-space: normal;
2000 2001 text-overflow: initial;
2001 2002 overflow: visible;
2002 2003
2003 2004 .match { background-color: #faffa6;}
2004 2005 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2005 2006 }
2006 2007
2007 2008 }
2008 2009
2009 2010 table.rctable td.td-search-results div {
2010 2011 max-width: 100%;
2011 2012 }
2012 2013
2013 2014 #tip-box, .tip-box{
2014 2015 padding: @menupadding/2;
2015 2016 display: block;
2016 2017 border: @border-thickness solid @border-highlight-color;
2017 2018 .border-radius(@border-radius);
2018 2019 background-color: white;
2019 2020 z-index: 99;
2020 2021 white-space: pre-wrap;
2021 2022 }
2022 2023
2023 2024 #linktt {
2024 2025 width: 79px;
2025 2026 }
2026 2027
2027 2028 #help_kb .modal-content{
2028 2029 max-width: 750px;
2029 2030 margin: 10% auto;
2030 2031
2031 2032 table{
2032 2033 td,th{
2033 2034 border-bottom: none;
2034 2035 line-height: 2.5em;
2035 2036 }
2036 2037 th{
2037 2038 padding-bottom: @textmargin/2;
2038 2039 }
2039 2040 td.keys{
2040 2041 text-align: center;
2041 2042 }
2042 2043 }
2043 2044
2044 2045 .block-left{
2045 2046 width: 45%;
2046 2047 margin-right: 5%;
2047 2048 }
2048 2049 .modal-footer{
2049 2050 clear: both;
2050 2051 }
2051 2052 .key.tag{
2052 2053 padding: 0.5em;
2053 2054 background-color: @rcblue;
2054 2055 color: white;
2055 2056 border-color: @rcblue;
2056 2057 .box-shadow(none);
2057 2058 }
2058 2059 }
2059 2060
2060 2061
2061 2062
2062 2063 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2063 2064
2064 2065 @import 'statistics-graph';
2065 2066 @import 'tables';
2066 2067 @import 'forms';
2067 2068 @import 'diff';
2068 2069 @import 'summary';
2069 2070 @import 'navigation';
2070 2071
2071 2072 //--- SHOW/HIDE SECTIONS --//
2072 2073
2073 2074 .btn-collapse {
2074 2075 float: right;
2075 2076 text-align: right;
2076 2077 font-family: @text-light;
2077 2078 font-size: @basefontsize;
2078 2079 cursor: pointer;
2079 2080 border: none;
2080 2081 color: @rcblue;
2081 2082 }
2082 2083
2083 2084 table.rctable,
2084 2085 table.dataTable {
2085 2086 .btn-collapse {
2086 2087 float: right;
2087 2088 text-align: right;
2088 2089 }
2089 2090 }
2090 2091
2091 2092
2092 2093 // TODO: johbo: Fix for IE10, this avoids that we see a border
2093 2094 // and padding around checkboxes and radio boxes. Move to the right place,
2094 2095 // or better: Remove this once we did the form refactoring.
2095 2096 input[type=checkbox],
2096 2097 input[type=radio] {
2097 2098 padding: 0;
2098 2099 border: none;
2099 2100 }
@@ -1,108 +1,44 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="base.html"/>
3 3
4 4 <%def name="breadcrumbs_links()">
5 5 %if c.repo:
6 6 ${h.link_to('Settings',h.url('edit_repo', repo_name=c.repo.repo_name))}
7 7 &raquo;
8 8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 9 &raquo;
10 10 ${h.link_to(current_IntegrationType.display_name,
11 11 request.route_url(route_name='repo_integrations_list',
12 12 repo_name=c.repo.repo_name,
13 13 integration=current_IntegrationType.key))}
14 14 %else:
15 15 ${h.link_to(_('Admin'),h.url('admin_home'))}
16 16 &raquo;
17 17 ${h.link_to(_('Settings'),h.url('admin_settings'))}
18 18 &raquo;
19 19 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
20 20 &raquo;
21 21 ${h.link_to(current_IntegrationType.display_name,
22 22 request.route_url(route_name='global_integrations_list',
23 23 integration=current_IntegrationType.key))}
24 24 %endif
25 25 %if integration:
26 26 &raquo;
27 27 ${integration.name}
28 28 %endif
29 29 </%def>
30
31
32 30 <div class="panel panel-default">
33 31 <div class="panel-heading">
34 32 <h2 class="panel-title">
35 33 %if integration:
36 34 ${current_IntegrationType.display_name} - ${integration.name}
37 35 %else:
38 36 ${_('Create new %(integration_type)s integration') % {'integration_type': current_IntegrationType.display_name}}
39 37 %endif
40 38 </h2>
41 39 </div>
42 <div class="fields panel-body">
43 ${h.secure_form(request.url)}
44 <div class="form">
45 %for node in schema:
46 <% label_css_class = ("label-checkbox" if (node.widget == "bool") else "") %>
47 <div class="field">
48 <div class="label ${label_css_class}"><label for="${node.name}">${node.title}</label></div>
49 <div class="input">
50 %if node.widget in ["string", "int", "unicode"]:
51 ${h.text(node.name, defaults.get(node.name), class_="medium", placeholder=hasattr(node, 'placeholder') and node.placeholder or '')}
52 %elif node.widget in ["text"]:
53 ${h.textarea(node.name, defaults.get(node.name), class_="medium", placeholder=hasattr(node, 'placeholder') and node.placeholder or '')}
54 %elif node.widget == "password":
55 ${h.password(node.name, defaults.get(node.name), class_="medium")}
56 %elif node.widget == "bool":
57 <div class="checkbox">${h.checkbox(node.name, True, checked=defaults.get(node.name))}</div>
58 %elif node.widget == "select":
59 ${h.select(node.name, defaults.get(node.name), node.choices)}
60 %elif node.widget == "checkbox_list":
61 %for i, choice in enumerate(node.choices):
62 <%
63 name, value = choice, choice
64 if isinstance(choice, tuple):
65 choice, name = choice
66 %>
67 <div>
68 <input id="${node.name}-${choice}"
69 name="${node.name}"
70 value="${value}"
71 type="checkbox"
72 ${value in defaults.get(node.name, []) and 'checked' or ''}>
73 <label for="${node.name}-${value}">
74 ${name}
75 </label>
76 </div>
77 %endfor
78 %elif node.widget == "readonly":
79 ${node.default}
80 %else:
81 This field is of type ${node.typ}, which cannot be displayed. Must be one of [string|int|bool|select|password|text|checkbox_list].
82 %endif
83 %if node.name in errors:
84 <span class="error-message">${errors.get(node.name)}</span>
85 <br />
86 %endif
87 <p class="help-block">${node.description}</p>
88 </div>
89 </div>
90 %endfor
91
92 ## Allow derived templates to add something below the form
93 ## input fields
94 %if hasattr(next, 'below_form_fields'):
95 ${next.below_form_fields()}
96 %endif
97
98 <div class="buttons">
99 ${h.submit('save',_('Save'),class_="btn")}
100 %if integration:
101 ${h.submit('delete',_('Delete'),class_="btn btn-danger")}
102 %endif
103 </div>
104
105 </div>
106 ${h.end_form()}
40 <div class="panel-body">
41 ## TODO: dan: find way to put h in the deform context properly
42 ${form.render(h=h) | n}
107 43 </div>
108 </div> No newline at end of file
44 </div>
@@ -1,137 +1,139 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html>
3 3
4 4 <%
5 5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
6 6
7 7 if hasattr(c, 'rhodecode_db_repo'):
8 8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
9 9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
10 10
11 11 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
12 12 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
13 13 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
14 14
15 15 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
16 16 %>
17 17
18 18 <html xmlns="http://www.w3.org/1999/xhtml">
19 19 <head>
20 20 <title>${self.title()}</title>
21 21 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
22 22 <%def name="robots()">
23 23 <meta name="robots" content="index, nofollow"/>
24 24 </%def>
25 25 ${self.robots()}
26 26 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
27 27
28 28 ## CSS definitions
29 29 <%def name="css()">
30 30 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
31 31 <!--[if lt IE 9]>
32 32 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css', ver=c.rhodecode_version_hash)}" media="screen"/>
33 33 <![endif]-->
34 34 ## EXTRA FOR CSS
35 35 ${self.css_extra()}
36 36 </%def>
37 37 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
38 38 <%def name="css_extra()">
39 39 </%def>
40 40
41 41 ${self.css()}
42 42
43 43 ## JAVASCRIPT
44 44 <%def name="js()">
45 45 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
46 46 <script type="text/javascript">
47 47 // register templateContext to pass template variables to JS
48 48 var templateContext = ${h.json.dumps(c.template_context)|n};
49 49
50 50 var REPO_NAME = "${getattr(c, 'repo_name', '')}";
51 51 %if hasattr(c, 'rhodecode_db_repo'):
52 52 var REPO_LANDING_REV = '${c.rhodecode_db_repo.landing_rev[1]}';
53 53 var REPO_TYPE = '${c.rhodecode_db_repo.repo_type}';
54 54 %else:
55 55 var REPO_LANDING_REV = '';
56 56 var REPO_TYPE = '';
57 57 %endif
58 58 var APPLICATION_URL = "${h.url('home').rstrip('/')}";
59 59 var ASSET_URL = "${h.asset('')}";
60 60 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
61 61 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
62 62 % if getattr(c, 'rhodecode_user', None):
63 63 var USER = {name:'${c.rhodecode_user.username}'};
64 64 % else:
65 65 var USER = {name:null};
66 66 % endif
67 67
68 68 var APPENLIGHT = {
69 69 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
70 70 key: '${getattr(c, "appenlight_api_public_key", "")}',
71 71 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
72 72 requestInfo: {
73 73 % if getattr(c, 'rhodecode_user', None):
74 74 ip: '${c.rhodecode_user.ip_addr}',
75 75 username: '${c.rhodecode_user.username}'
76 76 % endif
77 77 }
78 78 };
79 79 </script>
80 80
81 81 <!--[if lt IE 9]>
82 82 <script language="javascript" type="text/javascript" src="${h.asset('js/excanvas.min.js')}"></script>
83 83 <![endif]-->
84 84 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
85 85 <script language="javascript" type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
86 <script language="javascript" type="text/javascript" src="${h.static_url('deform:static/scripts/deform.js')}"></script>
86 87 ## avoide esaping the %N
87 88 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js'}";</script>
88 89
90
89 91 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
90 92 ${self.js_extra()}
91 93
92 94 <script type="text/javascript">
93 95 $(document).ready(function(){
94 96 show_more_event();
95 97 timeagoActivate();
96 98 })
97 99 </script>
98 100
99 101 </%def>
100 102
101 103 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
102 104 <%def name="js_extra()"></%def>
103 105 ${self.js()}
104 106
105 107 <%def name="head_extra()"></%def>
106 108 ${self.head_extra()}
107 109
108 110 <%include file="/base/plugins_base.html"/>
109 111
110 112 ## extra stuff
111 113 %if c.pre_code:
112 114 ${c.pre_code|n}
113 115 %endif
114 116 </head>
115 117 <body id="body">
116 118 <noscript>
117 119 <div class="noscript-error">
118 120 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
119 121 </div>
120 122 </noscript>
121 123 ## IE hacks
122 124 <!--[if IE 7]>
123 125 <script>$(document.body).addClass('ie7')</script>
124 126 <![endif]-->
125 127 <!--[if IE 8]>
126 128 <script>$(document.body).addClass('ie8')</script>
127 129 <![endif]-->
128 130 <!--[if IE 9]>
129 131 <script>$(document.body).addClass('ie9')</script>
130 132 <![endif]-->
131 133
132 134 ${next.body()}
133 135 %if c.post_code:
134 136 ${c.post_code|n}
135 137 %endif
136 138 </body>
137 139 </html>
@@ -1,249 +1,250 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Import early to make sure things are patched up properly
4 4 from setuptools import setup, find_packages
5 5
6 6 import os
7 7 import sys
8 8 import platform
9 9
10 10 if sys.version_info < (2, 7):
11 11 raise Exception('RhodeCode requires Python 2.7 or later')
12 12
13 13
14 14 here = os.path.abspath(os.path.dirname(__file__))
15 15
16 16
17 17 def _get_meta_var(name, data, callback_handler=None):
18 18 import re
19 19 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
20 20 if matches:
21 21 if not callable(callback_handler):
22 22 callback_handler = lambda v: v
23 23
24 24 return callback_handler(eval(matches.groups()[0]))
25 25
26 26 _meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb')
27 27 _metadata = _meta.read()
28 28 _meta.close()
29 29
30 30 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
31 31 __version__ = open(os.path.join('rhodecode', 'VERSION')).read().strip()
32 32 __license__ = _get_meta_var('__license__', _metadata)
33 33 __author__ = _get_meta_var('__author__', _metadata)
34 34 __url__ = _get_meta_var('__url__', _metadata)
35 35 # defines current platform
36 36 __platform__ = platform.system()
37 37
38 38 # Cygwin has different platform identifiers, but they all contain the
39 39 # term "CYGWIN"
40 40 is_windows = __platform__ == 'Windows' or 'CYGWIN' in __platform__
41 41
42 42 requirements = [
43 43 'Babel',
44 44 'Beaker',
45 45 'FormEncode',
46 46 'Mako',
47 47 'Markdown',
48 48 'MarkupSafe',
49 49 'MySQL-python',
50 50 'Paste',
51 51 'PasteDeploy',
52 52 'PasteScript',
53 53 'Pygments',
54 54 'Pylons',
55 55 'Pyro4',
56 56 'Routes',
57 57 'SQLAlchemy',
58 58 'Tempita',
59 59 'URLObject',
60 60 'WebError',
61 61 'WebHelpers',
62 62 'WebHelpers2',
63 63 'WebOb',
64 64 'WebTest',
65 65 'Whoosh',
66 66 'alembic',
67 67 'amqplib',
68 68 'anyjson',
69 69 'appenlight-client',
70 70 'authomatic',
71 71 'backport_ipaddress',
72 72 'celery',
73 73 'colander',
74 74 'decorator',
75 'deform',
75 76 'docutils',
76 77 'gunicorn',
77 78 'infrae.cache',
78 79 'ipython',
79 80 'iso8601',
80 81 'kombu',
81 82 'msgpack-python',
82 83 'packaging',
83 84 'psycopg2',
84 85 'py-gfm',
85 86 'pycrypto',
86 87 'pycurl',
87 88 'pyparsing',
88 89 'pyramid',
89 90 'pyramid-debugtoolbar',
90 91 'pyramid-mako',
91 92 'pyramid-beaker',
92 93 'pysqlite',
93 94 'python-dateutil',
94 95 'python-ldap',
95 96 'python-memcached',
96 97 'python-pam',
97 98 'recaptcha-client',
98 99 'repoze.lru',
99 100 'requests',
100 101 'simplejson',
101 102 'waitress',
102 103 'zope.cachedescriptors',
103 104 'dogpile.cache',
104 105 'dogpile.core'
105 106 ]
106 107
107 108 if is_windows:
108 109 pass
109 110 else:
110 111 requirements.append('psutil')
111 112 requirements.append('py-bcrypt')
112 113
113 114 test_requirements = [
114 115 'WebTest',
115 116 'configobj',
116 117 'cssselect',
117 118 'flake8',
118 119 'lxml',
119 120 'mock',
120 121 'pytest',
121 122 'pytest-cov',
122 123 'pytest-runner',
123 124 ]
124 125
125 126 setup_requirements = [
126 127 'PasteScript',
127 128 'pytest-runner',
128 129 ]
129 130
130 131 dependency_links = [
131 132 ]
132 133
133 134 classifiers = [
134 135 'Development Status :: 6 - Mature',
135 136 'Environment :: Web Environment',
136 137 'Framework :: Pylons',
137 138 'Intended Audience :: Developers',
138 139 'Operating System :: OS Independent',
139 140 'Programming Language :: Python',
140 141 'Programming Language :: Python :: 2.7',
141 142 ]
142 143
143 144
144 145 # additional files from project that goes somewhere in the filesystem
145 146 # relative to sys.prefix
146 147 data_files = []
147 148
148 149 # additional files that goes into package itself
149 150 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
150 151
151 152 description = ('RhodeCode is a fast and powerful management tool '
152 153 'for Mercurial and GIT with a built in push/pull server, '
153 154 'full text search and code-review.')
154 155
155 156 keywords = ' '.join([
156 157 'rhodecode', 'rhodiumcode', 'mercurial', 'git', 'code review',
157 158 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
158 159 'hgwebdir', 'gitweb replacement', 'serving hgweb',
159 160 ])
160 161
161 162 # long description
162 163 README_FILE = 'README.rst'
163 164 CHANGELOG_FILE = 'CHANGES.rst'
164 165 try:
165 166 long_description = open(README_FILE).read() + '\n\n' + \
166 167 open(CHANGELOG_FILE).read()
167 168
168 169 except IOError, err:
169 170 sys.stderr.write(
170 171 '[WARNING] Cannot find file specified as long_description (%s)\n or '
171 172 'changelog (%s) skipping that file' % (README_FILE, CHANGELOG_FILE)
172 173 )
173 174 long_description = description
174 175
175 176 # packages
176 177 packages = find_packages()
177 178
178 179 paster_commands = [
179 180 'make-config=rhodecode.lib.paster_commands.make_config:Command',
180 181 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command',
181 182 'update-repoinfo=rhodecode.lib.paster_commands.update_repoinfo:Command',
182 183 'cache-keys=rhodecode.lib.paster_commands.cache_keys:Command',
183 184 'ishell=rhodecode.lib.paster_commands.ishell:Command',
184 185 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb',
185 186 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand',
186 187 ]
187 188
188 189 setup(
189 190 name='rhodecode-enterprise-ce',
190 191 version=__version__,
191 192 description=description,
192 193 long_description=long_description,
193 194 keywords=keywords,
194 195 license=__license__,
195 196 author=__author__,
196 197 author_email='marcin@rhodecode.com',
197 198 dependency_links=dependency_links,
198 199 url=__url__,
199 200 install_requires=requirements,
200 201 tests_require=test_requirements,
201 202 classifiers=classifiers,
202 203 setup_requires=setup_requirements,
203 204 data_files=data_files,
204 205 packages=packages,
205 206 include_package_data=True,
206 207 package_data=package_data,
207 208 message_extractors={
208 209 'rhodecode': [
209 210 ('**.py', 'python', None),
210 211 ('**.js', 'javascript', None),
211 212 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
212 213 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
213 214 ('public/**', 'ignore', None),
214 215 ]
215 216 },
216 217 zip_safe=False,
217 218 paster_plugins=['PasteScript', 'Pylons'],
218 219 entry_points={
219 220 'enterprise.plugins1': [
220 221 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory',
221 222 'headers=rhodecode.authentication.plugins.auth_headers:plugin_factory',
222 223 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory',
223 224 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory',
224 225 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory',
225 226 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory',
226 227 'token=rhodecode.authentication.plugins.auth_token:plugin_factory',
227 228 ],
228 229 'paste.app_factory': [
229 230 'main=rhodecode.config.middleware:make_pyramid_app',
230 231 'pylons=rhodecode.config.middleware:make_app',
231 232 ],
232 233 'paste.app_install': [
233 234 'main=pylons.util:PylonsInstaller',
234 235 'pylons=pylons.util:PylonsInstaller',
235 236 ],
236 237 'paste.global_paster_command': paster_commands,
237 238 'pytest11': [
238 239 'pylons=rhodecode.tests.pylons_plugin',
239 240 'enterprise=rhodecode.tests.plugin',
240 241 ],
241 242 'console_scripts': [
242 243 'rcserver=rhodecode.rcserver:main',
243 244 ],
244 245 'beaker.backends': [
245 246 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase',
246 247 'memorylru_debug=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerDebug'
247 248 ]
248 249 },
249 250 )
General Comments 0
You need to be logged in to leave comments. Login now