##// 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 Babel = super.buildPythonPackage {
2 Babel = super.buildPythonPackage {
3 name = "Babel-1.3";
3 name = "Babel-1.3";
4 buildInputs = with self; [];
4 buildInputs = with self; [];
5 doCheck = false;
5 doCheck = false;
6 propagatedBuildInputs = with self; [pytz];
6 propagatedBuildInputs = with self; [pytz];
7 src = fetchurl {
7 src = fetchurl {
8 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
8 url = "https://pypi.python.org/packages/33/27/e3978243a03a76398c384c83f7ca879bc6e8f1511233a621fcada135606e/Babel-1.3.tar.gz";
9 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
9 md5 = "5264ceb02717843cbc9ffce8e6e06bdb";
10 };
10 };
11 meta = {
11 meta = {
12 license = [ pkgs.lib.licenses.bsdOriginal ];
12 license = [ pkgs.lib.licenses.bsdOriginal ];
13 };
13 };
14 };
14 };
15 Beaker = super.buildPythonPackage {
15 Beaker = super.buildPythonPackage {
16 name = "Beaker-1.7.0";
16 name = "Beaker-1.7.0";
17 buildInputs = with self; [];
17 buildInputs = with self; [];
18 doCheck = false;
18 doCheck = false;
19 propagatedBuildInputs = with self; [];
19 propagatedBuildInputs = with self; [];
20 src = fetchurl {
20 src = fetchurl {
21 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
21 url = "https://pypi.python.org/packages/97/8e/409d2e7c009b8aa803dc9e6f239f1db7c3cdf578249087a404e7c27a505d/Beaker-1.7.0.tar.gz";
22 md5 = "386be3f7fe427358881eee4622b428b3";
22 md5 = "386be3f7fe427358881eee4622b428b3";
23 };
23 };
24 meta = {
24 meta = {
25 license = [ pkgs.lib.licenses.bsdOriginal ];
25 license = [ pkgs.lib.licenses.bsdOriginal ];
26 };
26 };
27 };
27 };
28 CProfileV = super.buildPythonPackage {
28 CProfileV = super.buildPythonPackage {
29 name = "CProfileV-1.0.6";
29 name = "CProfileV-1.0.6";
30 buildInputs = with self; [];
30 buildInputs = with self; [];
31 doCheck = false;
31 doCheck = false;
32 propagatedBuildInputs = with self; [bottle];
32 propagatedBuildInputs = with self; [bottle];
33 src = fetchurl {
33 src = fetchurl {
34 url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz";
34 url = "https://pypi.python.org/packages/eb/df/983a0b6cfd3ac94abf023f5011cb04f33613ace196e33f53c86cf91850d5/CProfileV-1.0.6.tar.gz";
35 md5 = "08c7c242b6e64237bc53c5d13537e03d";
35 md5 = "08c7c242b6e64237bc53c5d13537e03d";
36 };
36 };
37 meta = {
37 meta = {
38 license = [ pkgs.lib.licenses.mit ];
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 Fabric = super.buildPythonPackage {
54 Fabric = super.buildPythonPackage {
42 name = "Fabric-1.10.0";
55 name = "Fabric-1.10.0";
43 buildInputs = with self; [];
56 buildInputs = with self; [];
44 doCheck = false;
57 doCheck = false;
45 propagatedBuildInputs = with self; [paramiko];
58 propagatedBuildInputs = with self; [paramiko];
46 src = fetchurl {
59 src = fetchurl {
47 url = "https://pypi.python.org/packages/e3/5f/b6ebdb5241d5ec9eab582a5c8a01255c1107da396f849e538801d2fe64a5/Fabric-1.10.0.tar.gz";
60 url = "https://pypi.python.org/packages/e3/5f/b6ebdb5241d5ec9eab582a5c8a01255c1107da396f849e538801d2fe64a5/Fabric-1.10.0.tar.gz";
48 md5 = "2cb96473387f0e7aa035210892352f4a";
61 md5 = "2cb96473387f0e7aa035210892352f4a";
49 };
62 };
50 meta = {
63 meta = {
51 license = [ pkgs.lib.licenses.bsdOriginal ];
64 license = [ pkgs.lib.licenses.bsdOriginal ];
52 };
65 };
53 };
66 };
54 FormEncode = super.buildPythonPackage {
67 FormEncode = super.buildPythonPackage {
55 name = "FormEncode-1.2.4";
68 name = "FormEncode-1.2.4";
56 buildInputs = with self; [];
69 buildInputs = with self; [];
57 doCheck = false;
70 doCheck = false;
58 propagatedBuildInputs = with self; [];
71 propagatedBuildInputs = with self; [];
59 src = fetchurl {
72 src = fetchurl {
60 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
73 url = "https://pypi.python.org/packages/8e/59/0174271a6f004512e0201188593e6d319db139d14cb7490e488bbb078015/FormEncode-1.2.4.tar.gz";
61 md5 = "6bc17fb9aed8aea198975e888e2077f4";
74 md5 = "6bc17fb9aed8aea198975e888e2077f4";
62 };
75 };
63 meta = {
76 meta = {
64 license = [ pkgs.lib.licenses.psfl ];
77 license = [ pkgs.lib.licenses.psfl ];
65 };
78 };
66 };
79 };
67 Jinja2 = super.buildPythonPackage {
80 Jinja2 = super.buildPythonPackage {
68 name = "Jinja2-2.7.3";
81 name = "Jinja2-2.7.3";
69 buildInputs = with self; [];
82 buildInputs = with self; [];
70 doCheck = false;
83 doCheck = false;
71 propagatedBuildInputs = with self; [MarkupSafe];
84 propagatedBuildInputs = with self; [MarkupSafe];
72 src = fetchurl {
85 src = fetchurl {
73 url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz";
86 url = "https://pypi.python.org/packages/b0/73/eab0bca302d6d6a0b5c402f47ad1760dc9cb2dd14bbc1873ad48db258e4d/Jinja2-2.7.3.tar.gz";
74 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
87 md5 = "b9dffd2f3b43d673802fe857c8445b1a";
75 };
88 };
76 meta = {
89 meta = {
77 license = [ pkgs.lib.licenses.bsdOriginal ];
90 license = [ pkgs.lib.licenses.bsdOriginal ];
78 };
91 };
79 };
92 };
80 Mako = super.buildPythonPackage {
93 Mako = super.buildPythonPackage {
81 name = "Mako-1.0.1";
94 name = "Mako-1.0.1";
82 buildInputs = with self; [];
95 buildInputs = with self; [];
83 doCheck = false;
96 doCheck = false;
84 propagatedBuildInputs = with self; [MarkupSafe];
97 propagatedBuildInputs = with self; [MarkupSafe];
85 src = fetchurl {
98 src = fetchurl {
86 url = "https://pypi.python.org/packages/8e/a4/aa56533ecaa5f22ca92428f74e074d0c9337282933c722391902c8f9e0f8/Mako-1.0.1.tar.gz";
99 url = "https://pypi.python.org/packages/8e/a4/aa56533ecaa5f22ca92428f74e074d0c9337282933c722391902c8f9e0f8/Mako-1.0.1.tar.gz";
87 md5 = "9f0aafd177b039ef67b90ea350497a54";
100 md5 = "9f0aafd177b039ef67b90ea350497a54";
88 };
101 };
89 meta = {
102 meta = {
90 license = [ pkgs.lib.licenses.mit ];
103 license = [ pkgs.lib.licenses.mit ];
91 };
104 };
92 };
105 };
93 Markdown = super.buildPythonPackage {
106 Markdown = super.buildPythonPackage {
94 name = "Markdown-2.6.2";
107 name = "Markdown-2.6.2";
95 buildInputs = with self; [];
108 buildInputs = with self; [];
96 doCheck = false;
109 doCheck = false;
97 propagatedBuildInputs = with self; [];
110 propagatedBuildInputs = with self; [];
98 src = fetchurl {
111 src = fetchurl {
99 url = "https://pypi.python.org/packages/62/8b/83658b5f6c220d5fcde9f9852d46ea54765d734cfbc5a9f4c05bfc36db4d/Markdown-2.6.2.tar.gz";
112 url = "https://pypi.python.org/packages/62/8b/83658b5f6c220d5fcde9f9852d46ea54765d734cfbc5a9f4c05bfc36db4d/Markdown-2.6.2.tar.gz";
100 md5 = "256d19afcc564dc4ce4c229bb762f7ae";
113 md5 = "256d19afcc564dc4ce4c229bb762f7ae";
101 };
114 };
102 meta = {
115 meta = {
103 license = [ pkgs.lib.licenses.bsdOriginal ];
116 license = [ pkgs.lib.licenses.bsdOriginal ];
104 };
117 };
105 };
118 };
106 MarkupSafe = super.buildPythonPackage {
119 MarkupSafe = super.buildPythonPackage {
107 name = "MarkupSafe-0.23";
120 name = "MarkupSafe-0.23";
108 buildInputs = with self; [];
121 buildInputs = with self; [];
109 doCheck = false;
122 doCheck = false;
110 propagatedBuildInputs = with self; [];
123 propagatedBuildInputs = with self; [];
111 src = fetchurl {
124 src = fetchurl {
112 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
125 url = "https://pypi.python.org/packages/c0/41/bae1254e0396c0cc8cf1751cb7d9afc90a602353695af5952530482c963f/MarkupSafe-0.23.tar.gz";
113 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
126 md5 = "f5ab3deee4c37cd6a922fb81e730da6e";
114 };
127 };
115 meta = {
128 meta = {
116 license = [ pkgs.lib.licenses.bsdOriginal ];
129 license = [ pkgs.lib.licenses.bsdOriginal ];
117 };
130 };
118 };
131 };
119 MySQL-python = super.buildPythonPackage {
132 MySQL-python = super.buildPythonPackage {
120 name = "MySQL-python-1.2.5";
133 name = "MySQL-python-1.2.5";
121 buildInputs = with self; [];
134 buildInputs = with self; [];
122 doCheck = false;
135 doCheck = false;
123 propagatedBuildInputs = with self; [];
136 propagatedBuildInputs = with self; [];
124 src = fetchurl {
137 src = fetchurl {
125 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
138 url = "https://pypi.python.org/packages/a5/e9/51b544da85a36a68debe7a7091f068d802fc515a3a202652828c73453cad/MySQL-python-1.2.5.zip";
126 md5 = "654f75b302db6ed8dc5a898c625e030c";
139 md5 = "654f75b302db6ed8dc5a898c625e030c";
127 };
140 };
128 meta = {
141 meta = {
129 license = [ pkgs.lib.licenses.gpl1 ];
142 license = [ pkgs.lib.licenses.gpl1 ];
130 };
143 };
131 };
144 };
132 Paste = super.buildPythonPackage {
145 Paste = super.buildPythonPackage {
133 name = "Paste-2.0.2";
146 name = "Paste-2.0.2";
134 buildInputs = with self; [];
147 buildInputs = with self; [];
135 doCheck = false;
148 doCheck = false;
136 propagatedBuildInputs = with self; [six];
149 propagatedBuildInputs = with self; [six];
137 src = fetchurl {
150 src = fetchurl {
138 url = "https://pypi.python.org/packages/d5/8d/0f8ac40687b97ff3e07ebd1369be20bdb3f93864d2dc3c2ff542edb4ce50/Paste-2.0.2.tar.gz";
151 url = "https://pypi.python.org/packages/d5/8d/0f8ac40687b97ff3e07ebd1369be20bdb3f93864d2dc3c2ff542edb4ce50/Paste-2.0.2.tar.gz";
139 md5 = "4bfc8a7eaf858f6309d2ac0f40fc951c";
152 md5 = "4bfc8a7eaf858f6309d2ac0f40fc951c";
140 };
153 };
141 meta = {
154 meta = {
142 license = [ pkgs.lib.licenses.mit ];
155 license = [ pkgs.lib.licenses.mit ];
143 };
156 };
144 };
157 };
145 PasteDeploy = super.buildPythonPackage {
158 PasteDeploy = super.buildPythonPackage {
146 name = "PasteDeploy-1.5.2";
159 name = "PasteDeploy-1.5.2";
147 buildInputs = with self; [];
160 buildInputs = with self; [];
148 doCheck = false;
161 doCheck = false;
149 propagatedBuildInputs = with self; [];
162 propagatedBuildInputs = with self; [];
150 src = fetchurl {
163 src = fetchurl {
151 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
164 url = "https://pypi.python.org/packages/0f/90/8e20cdae206c543ea10793cbf4136eb9a8b3f417e04e40a29d72d9922cbd/PasteDeploy-1.5.2.tar.gz";
152 md5 = "352b7205c78c8de4987578d19431af3b";
165 md5 = "352b7205c78c8de4987578d19431af3b";
153 };
166 };
154 meta = {
167 meta = {
155 license = [ pkgs.lib.licenses.mit ];
168 license = [ pkgs.lib.licenses.mit ];
156 };
169 };
157 };
170 };
158 PasteScript = super.buildPythonPackage {
171 PasteScript = super.buildPythonPackage {
159 name = "PasteScript-1.7.5";
172 name = "PasteScript-1.7.5";
160 buildInputs = with self; [];
173 buildInputs = with self; [];
161 doCheck = false;
174 doCheck = false;
162 propagatedBuildInputs = with self; [Paste PasteDeploy];
175 propagatedBuildInputs = with self; [Paste PasteDeploy];
163 src = fetchurl {
176 src = fetchurl {
164 url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz";
177 url = "https://pypi.python.org/packages/a5/05/fc60efa7c2f17a1dbaeccb2a903a1e90902d92b9d00eebabe3095829d806/PasteScript-1.7.5.tar.gz";
165 md5 = "4c72d78dcb6bb993f30536842c16af4d";
178 md5 = "4c72d78dcb6bb993f30536842c16af4d";
166 };
179 };
167 meta = {
180 meta = {
168 license = [ pkgs.lib.licenses.mit ];
181 license = [ pkgs.lib.licenses.mit ];
169 };
182 };
170 };
183 };
171 Pygments = super.buildPythonPackage {
184 Pygments = super.buildPythonPackage {
172 name = "Pygments-2.1.3";
185 name = "Pygments-2.1.3";
173 buildInputs = with self; [];
186 buildInputs = with self; [];
174 doCheck = false;
187 doCheck = false;
175 propagatedBuildInputs = with self; [];
188 propagatedBuildInputs = with self; [];
176 src = fetchurl {
189 src = fetchurl {
177 url = "https://pypi.python.org/packages/b8/67/ab177979be1c81bc99c8d0592ef22d547e70bb4c6815c383286ed5dec504/Pygments-2.1.3.tar.gz";
190 url = "https://pypi.python.org/packages/b8/67/ab177979be1c81bc99c8d0592ef22d547e70bb4c6815c383286ed5dec504/Pygments-2.1.3.tar.gz";
178 md5 = "ed3fba2467c8afcda4d317e4ef2c6150";
191 md5 = "ed3fba2467c8afcda4d317e4ef2c6150";
179 };
192 };
180 meta = {
193 meta = {
181 license = [ pkgs.lib.licenses.bsdOriginal ];
194 license = [ pkgs.lib.licenses.bsdOriginal ];
182 };
195 };
183 };
196 };
184 Pylons = super.buildPythonPackage {
197 Pylons = super.buildPythonPackage {
185 name = "Pylons-1.0.1";
198 name = "Pylons-1.0.1";
186 buildInputs = with self; [];
199 buildInputs = with self; [];
187 doCheck = false;
200 doCheck = false;
188 propagatedBuildInputs = with self; [Routes WebHelpers Beaker Paste PasteDeploy PasteScript FormEncode simplejson decorator nose Mako WebError WebTest Tempita MarkupSafe WebOb];
201 propagatedBuildInputs = with self; [Routes WebHelpers Beaker Paste PasteDeploy PasteScript FormEncode simplejson decorator nose Mako WebError WebTest Tempita MarkupSafe WebOb];
189 src = fetchurl {
202 src = fetchurl {
190 url = "https://pypi.python.org/packages/a2/69/b835a6bad00acbfeed3f33c6e44fa3f936efc998c795bfb15c61a79ecf62/Pylons-1.0.1.tar.gz";
203 url = "https://pypi.python.org/packages/a2/69/b835a6bad00acbfeed3f33c6e44fa3f936efc998c795bfb15c61a79ecf62/Pylons-1.0.1.tar.gz";
191 md5 = "6cb880d75fa81213192142b07a6e4915";
204 md5 = "6cb880d75fa81213192142b07a6e4915";
192 };
205 };
193 meta = {
206 meta = {
194 license = [ pkgs.lib.licenses.bsdOriginal ];
207 license = [ pkgs.lib.licenses.bsdOriginal ];
195 };
208 };
196 };
209 };
197 Pyro4 = super.buildPythonPackage {
210 Pyro4 = super.buildPythonPackage {
198 name = "Pyro4-4.41";
211 name = "Pyro4-4.41";
199 buildInputs = with self; [];
212 buildInputs = with self; [];
200 doCheck = false;
213 doCheck = false;
201 propagatedBuildInputs = with self; [serpent];
214 propagatedBuildInputs = with self; [serpent];
202 src = fetchurl {
215 src = fetchurl {
203 url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz";
216 url = "https://pypi.python.org/packages/56/2b/89b566b4bf3e7f8ba790db2d1223852f8cb454c52cab7693dd41f608ca2a/Pyro4-4.41.tar.gz";
204 md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c";
217 md5 = "ed69e9bfafa9c06c049a87cb0c4c2b6c";
205 };
218 };
206 meta = {
219 meta = {
207 license = [ pkgs.lib.licenses.mit ];
220 license = [ pkgs.lib.licenses.mit ];
208 };
221 };
209 };
222 };
210 Routes = super.buildPythonPackage {
223 Routes = super.buildPythonPackage {
211 name = "Routes-1.13";
224 name = "Routes-1.13";
212 buildInputs = with self; [];
225 buildInputs = with self; [];
213 doCheck = false;
226 doCheck = false;
214 propagatedBuildInputs = with self; [repoze.lru];
227 propagatedBuildInputs = with self; [repoze.lru];
215 src = fetchurl {
228 src = fetchurl {
216 url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz";
229 url = "https://pypi.python.org/packages/88/d3/259c3b3cde8837eb9441ab5f574a660e8a4acea8f54a078441d4d2acac1c/Routes-1.13.tar.gz";
217 md5 = "d527b0ab7dd9172b1275a41f97448783";
230 md5 = "d527b0ab7dd9172b1275a41f97448783";
218 };
231 };
219 meta = {
232 meta = {
220 license = [ pkgs.lib.licenses.bsdOriginal ];
233 license = [ pkgs.lib.licenses.bsdOriginal ];
221 };
234 };
222 };
235 };
223 SQLAlchemy = super.buildPythonPackage {
236 SQLAlchemy = super.buildPythonPackage {
224 name = "SQLAlchemy-0.9.9";
237 name = "SQLAlchemy-0.9.9";
225 buildInputs = with self; [];
238 buildInputs = with self; [];
226 doCheck = false;
239 doCheck = false;
227 propagatedBuildInputs = with self; [];
240 propagatedBuildInputs = with self; [];
228 src = fetchurl {
241 src = fetchurl {
229 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
242 url = "https://pypi.python.org/packages/28/f7/1bbfd0d8597e8c358d5e15a166a486ad82fc5579b4e67b6ef7c05b1d182b/SQLAlchemy-0.9.9.tar.gz";
230 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
243 md5 = "8a10a9bd13ed3336ef7333ac2cc679ff";
231 };
244 };
232 meta = {
245 meta = {
233 license = [ pkgs.lib.licenses.mit ];
246 license = [ pkgs.lib.licenses.mit ];
234 };
247 };
235 };
248 };
236 Sphinx = super.buildPythonPackage {
249 Sphinx = super.buildPythonPackage {
237 name = "Sphinx-1.2.2";
250 name = "Sphinx-1.2.2";
238 buildInputs = with self; [];
251 buildInputs = with self; [];
239 doCheck = false;
252 doCheck = false;
240 propagatedBuildInputs = with self; [Pygments docutils Jinja2];
253 propagatedBuildInputs = with self; [Pygments docutils Jinja2];
241 src = fetchurl {
254 src = fetchurl {
242 url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz";
255 url = "https://pypi.python.org/packages/0a/50/34017e6efcd372893a416aba14b84a1a149fc7074537b0e9cb6ca7b7abe9/Sphinx-1.2.2.tar.gz";
243 md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";
256 md5 = "3dc73ccaa8d0bfb2d62fb671b1f7e8a4";
244 };
257 };
245 meta = {
258 meta = {
246 license = [ pkgs.lib.licenses.bsdOriginal ];
259 license = [ pkgs.lib.licenses.bsdOriginal ];
247 };
260 };
248 };
261 };
249 Tempita = super.buildPythonPackage {
262 Tempita = super.buildPythonPackage {
250 name = "Tempita-0.5.2";
263 name = "Tempita-0.5.2";
251 buildInputs = with self; [];
264 buildInputs = with self; [];
252 doCheck = false;
265 doCheck = false;
253 propagatedBuildInputs = with self; [];
266 propagatedBuildInputs = with self; [];
254 src = fetchurl {
267 src = fetchurl {
255 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
268 url = "https://pypi.python.org/packages/56/c8/8ed6eee83dbddf7b0fc64dd5d4454bc05e6ccaafff47991f73f2894d9ff4/Tempita-0.5.2.tar.gz";
256 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
269 md5 = "4c2f17bb9d481821c41b6fbee904cea1";
257 };
270 };
258 meta = {
271 meta = {
259 license = [ pkgs.lib.licenses.mit ];
272 license = [ pkgs.lib.licenses.mit ];
260 };
273 };
261 };
274 };
262 URLObject = super.buildPythonPackage {
275 URLObject = super.buildPythonPackage {
263 name = "URLObject-2.4.0";
276 name = "URLObject-2.4.0";
264 buildInputs = with self; [];
277 buildInputs = with self; [];
265 doCheck = false;
278 doCheck = false;
266 propagatedBuildInputs = with self; [];
279 propagatedBuildInputs = with self; [];
267 src = fetchurl {
280 src = fetchurl {
268 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
281 url = "https://pypi.python.org/packages/cb/b6/e25e58500f9caef85d664bec71ec67c116897bfebf8622c32cb75d1ca199/URLObject-2.4.0.tar.gz";
269 md5 = "2ed819738a9f0a3051f31dc9924e3065";
282 md5 = "2ed819738a9f0a3051f31dc9924e3065";
270 };
283 };
271 meta = {
284 meta = {
272 license = [ ];
285 license = [ ];
273 };
286 };
274 };
287 };
275 WebError = super.buildPythonPackage {
288 WebError = super.buildPythonPackage {
276 name = "WebError-0.10.3";
289 name = "WebError-0.10.3";
277 buildInputs = with self; [];
290 buildInputs = with self; [];
278 doCheck = false;
291 doCheck = false;
279 propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste];
292 propagatedBuildInputs = with self; [WebOb Tempita Pygments Paste];
280 src = fetchurl {
293 src = fetchurl {
281 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
294 url = "https://pypi.python.org/packages/35/76/e7e5c2ce7e9c7f31b54c1ff295a495886d1279a002557d74dd8957346a79/WebError-0.10.3.tar.gz";
282 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
295 md5 = "84b9990b0baae6fd440b1e60cdd06f9a";
283 };
296 };
284 meta = {
297 meta = {
285 license = [ pkgs.lib.licenses.mit ];
298 license = [ pkgs.lib.licenses.mit ];
286 };
299 };
287 };
300 };
288 WebHelpers = super.buildPythonPackage {
301 WebHelpers = super.buildPythonPackage {
289 name = "WebHelpers-1.3";
302 name = "WebHelpers-1.3";
290 buildInputs = with self; [];
303 buildInputs = with self; [];
291 doCheck = false;
304 doCheck = false;
292 propagatedBuildInputs = with self; [MarkupSafe];
305 propagatedBuildInputs = with self; [MarkupSafe];
293 src = fetchurl {
306 src = fetchurl {
294 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
307 url = "https://pypi.python.org/packages/ee/68/4d07672821d514184357f1552f2dad923324f597e722de3b016ca4f7844f/WebHelpers-1.3.tar.gz";
295 md5 = "32749ffadfc40fea51075a7def32588b";
308 md5 = "32749ffadfc40fea51075a7def32588b";
296 };
309 };
297 meta = {
310 meta = {
298 license = [ pkgs.lib.licenses.bsdOriginal ];
311 license = [ pkgs.lib.licenses.bsdOriginal ];
299 };
312 };
300 };
313 };
301 WebHelpers2 = super.buildPythonPackage {
314 WebHelpers2 = super.buildPythonPackage {
302 name = "WebHelpers2-2.0";
315 name = "WebHelpers2-2.0";
303 buildInputs = with self; [];
316 buildInputs = with self; [];
304 doCheck = false;
317 doCheck = false;
305 propagatedBuildInputs = with self; [MarkupSafe six];
318 propagatedBuildInputs = with self; [MarkupSafe six];
306 src = fetchurl {
319 src = fetchurl {
307 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
320 url = "https://pypi.python.org/packages/ff/30/56342c6ea522439e3662427c8d7b5e5b390dff4ff2dc92d8afcb8ab68b75/WebHelpers2-2.0.tar.gz";
308 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
321 md5 = "0f6b68d70c12ee0aed48c00b24da13d3";
309 };
322 };
310 meta = {
323 meta = {
311 license = [ pkgs.lib.licenses.mit ];
324 license = [ pkgs.lib.licenses.mit ];
312 };
325 };
313 };
326 };
314 WebOb = super.buildPythonPackage {
327 WebOb = super.buildPythonPackage {
315 name = "WebOb-1.3.1";
328 name = "WebOb-1.3.1";
316 buildInputs = with self; [];
329 buildInputs = with self; [];
317 doCheck = false;
330 doCheck = false;
318 propagatedBuildInputs = with self; [];
331 propagatedBuildInputs = with self; [];
319 src = fetchurl {
332 src = fetchurl {
320 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
333 url = "https://pypi.python.org/packages/16/78/adfc0380b8a0d75b2d543fa7085ba98a573b1ae486d9def88d172b81b9fa/WebOb-1.3.1.tar.gz";
321 md5 = "20918251c5726956ba8fef22d1556177";
334 md5 = "20918251c5726956ba8fef22d1556177";
322 };
335 };
323 meta = {
336 meta = {
324 license = [ pkgs.lib.licenses.mit ];
337 license = [ pkgs.lib.licenses.mit ];
325 };
338 };
326 };
339 };
327 WebTest = super.buildPythonPackage {
340 WebTest = super.buildPythonPackage {
328 name = "WebTest-1.4.3";
341 name = "WebTest-1.4.3";
329 buildInputs = with self; [];
342 buildInputs = with self; [];
330 doCheck = false;
343 doCheck = false;
331 propagatedBuildInputs = with self; [WebOb];
344 propagatedBuildInputs = with self; [WebOb];
332 src = fetchurl {
345 src = fetchurl {
333 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
346 url = "https://pypi.python.org/packages/51/3d/84fd0f628df10b30c7db87895f56d0158e5411206b721ca903cb51bfd948/WebTest-1.4.3.zip";
334 md5 = "631ce728bed92c681a4020a36adbc353";
347 md5 = "631ce728bed92c681a4020a36adbc353";
335 };
348 };
336 meta = {
349 meta = {
337 license = [ pkgs.lib.licenses.mit ];
350 license = [ pkgs.lib.licenses.mit ];
338 };
351 };
339 };
352 };
340 Whoosh = super.buildPythonPackage {
353 Whoosh = super.buildPythonPackage {
341 name = "Whoosh-2.7.0";
354 name = "Whoosh-2.7.0";
342 buildInputs = with self; [];
355 buildInputs = with self; [];
343 doCheck = false;
356 doCheck = false;
344 propagatedBuildInputs = with self; [];
357 propagatedBuildInputs = with self; [];
345 src = fetchurl {
358 src = fetchurl {
346 url = "https://pypi.python.org/packages/1c/dc/2f0231ff3875ded36df8c1ab851451e51a237dc0e5a86d3d96036158da94/Whoosh-2.7.0.zip";
359 url = "https://pypi.python.org/packages/1c/dc/2f0231ff3875ded36df8c1ab851451e51a237dc0e5a86d3d96036158da94/Whoosh-2.7.0.zip";
347 md5 = "7abfd970f16fadc7311960f3fa0bc7a9";
360 md5 = "7abfd970f16fadc7311960f3fa0bc7a9";
348 };
361 };
349 meta = {
362 meta = {
350 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
363 license = [ pkgs.lib.licenses.bsdOriginal pkgs.lib.licenses.bsd2 ];
351 };
364 };
352 };
365 };
353 alembic = super.buildPythonPackage {
366 alembic = super.buildPythonPackage {
354 name = "alembic-0.8.4";
367 name = "alembic-0.8.4";
355 buildInputs = with self; [];
368 buildInputs = with self; [];
356 doCheck = false;
369 doCheck = false;
357 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor];
370 propagatedBuildInputs = with self; [SQLAlchemy Mako python-editor];
358 src = fetchurl {
371 src = fetchurl {
359 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
372 url = "https://pypi.python.org/packages/ca/7e/299b4499b5c75e5a38c5845145ad24755bebfb8eec07a2e1c366b7181eeb/alembic-0.8.4.tar.gz";
360 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
373 md5 = "5f95d8ee62b443f9b37eb5bee76c582d";
361 };
374 };
362 meta = {
375 meta = {
363 license = [ pkgs.lib.licenses.mit ];
376 license = [ pkgs.lib.licenses.mit ];
364 };
377 };
365 };
378 };
366 amqplib = super.buildPythonPackage {
379 amqplib = super.buildPythonPackage {
367 name = "amqplib-1.0.2";
380 name = "amqplib-1.0.2";
368 buildInputs = with self; [];
381 buildInputs = with self; [];
369 doCheck = false;
382 doCheck = false;
370 propagatedBuildInputs = with self; [];
383 propagatedBuildInputs = with self; [];
371 src = fetchurl {
384 src = fetchurl {
372 url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz";
385 url = "https://pypi.python.org/packages/75/b7/8c2429bf8d92354a0118614f9a4d15e53bc69ebedce534284111de5a0102/amqplib-1.0.2.tgz";
373 md5 = "5c92f17fbedd99b2b4a836d4352d1e2f";
386 md5 = "5c92f17fbedd99b2b4a836d4352d1e2f";
374 };
387 };
375 meta = {
388 meta = {
376 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
389 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
377 };
390 };
378 };
391 };
379 anyjson = super.buildPythonPackage {
392 anyjson = super.buildPythonPackage {
380 name = "anyjson-0.3.3";
393 name = "anyjson-0.3.3";
381 buildInputs = with self; [];
394 buildInputs = with self; [];
382 doCheck = false;
395 doCheck = false;
383 propagatedBuildInputs = with self; [];
396 propagatedBuildInputs = with self; [];
384 src = fetchurl {
397 src = fetchurl {
385 url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz";
398 url = "https://pypi.python.org/packages/c3/4d/d4089e1a3dd25b46bebdb55a992b0797cff657b4477bc32ce28038fdecbc/anyjson-0.3.3.tar.gz";
386 md5 = "2ea28d6ec311aeeebaf993cb3008b27c";
399 md5 = "2ea28d6ec311aeeebaf993cb3008b27c";
387 };
400 };
388 meta = {
401 meta = {
389 license = [ pkgs.lib.licenses.bsdOriginal ];
402 license = [ pkgs.lib.licenses.bsdOriginal ];
390 };
403 };
391 };
404 };
392 appenlight-client = super.buildPythonPackage {
405 appenlight-client = super.buildPythonPackage {
393 name = "appenlight-client-0.6.14";
406 name = "appenlight-client-0.6.14";
394 buildInputs = with self; [];
407 buildInputs = with self; [];
395 doCheck = false;
408 doCheck = false;
396 propagatedBuildInputs = with self; [WebOb requests];
409 propagatedBuildInputs = with self; [WebOb requests];
397 src = fetchurl {
410 src = fetchurl {
398 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
411 url = "https://pypi.python.org/packages/4d/e0/23fee3ebada8143f707e65c06bcb82992040ee64ea8355e044ed55ebf0c1/appenlight_client-0.6.14.tar.gz";
399 md5 = "578c69b09f4356d898fff1199b98a95c";
412 md5 = "578c69b09f4356d898fff1199b98a95c";
400 };
413 };
401 meta = {
414 meta = {
402 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "DFSG approved"; } ];
415 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "DFSG approved"; } ];
403 };
416 };
404 };
417 };
405 authomatic = super.buildPythonPackage {
418 authomatic = super.buildPythonPackage {
406 name = "authomatic-0.1.0.post1";
419 name = "authomatic-0.1.0.post1";
407 buildInputs = with self; [];
420 buildInputs = with self; [];
408 doCheck = false;
421 doCheck = false;
409 propagatedBuildInputs = with self; [];
422 propagatedBuildInputs = with self; [];
410 src = fetchurl {
423 src = fetchurl {
411 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
424 url = "https://pypi.python.org/packages/08/1a/8a930461e604c2d5a7a871e1ac59fa82ccf994c32e807230c8d2fb07815a/Authomatic-0.1.0.post1.tar.gz";
412 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
425 md5 = "be3f3ce08747d776aae6d6cc8dcb49a9";
413 };
426 };
414 meta = {
427 meta = {
415 license = [ pkgs.lib.licenses.mit ];
428 license = [ pkgs.lib.licenses.mit ];
416 };
429 };
417 };
430 };
418 backport-ipaddress = super.buildPythonPackage {
431 backport-ipaddress = super.buildPythonPackage {
419 name = "backport-ipaddress-0.1";
432 name = "backport-ipaddress-0.1";
420 buildInputs = with self; [];
433 buildInputs = with self; [];
421 doCheck = false;
434 doCheck = false;
422 propagatedBuildInputs = with self; [];
435 propagatedBuildInputs = with self; [];
423 src = fetchurl {
436 src = fetchurl {
424 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
437 url = "https://pypi.python.org/packages/d3/30/54c6dab05a4dec44db25ff309f1fbb6b7a8bde3f2bade38bb9da67bbab8f/backport_ipaddress-0.1.tar.gz";
425 md5 = "9c1f45f4361f71b124d7293a60006c05";
438 md5 = "9c1f45f4361f71b124d7293a60006c05";
426 };
439 };
427 meta = {
440 meta = {
428 license = [ pkgs.lib.licenses.psfl ];
441 license = [ pkgs.lib.licenses.psfl ];
429 };
442 };
430 };
443 };
431 bottle = super.buildPythonPackage {
444 bottle = super.buildPythonPackage {
432 name = "bottle-0.12.8";
445 name = "bottle-0.12.8";
433 buildInputs = with self; [];
446 buildInputs = with self; [];
434 doCheck = false;
447 doCheck = false;
435 propagatedBuildInputs = with self; [];
448 propagatedBuildInputs = with self; [];
436 src = fetchurl {
449 src = fetchurl {
437 url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz";
450 url = "https://pypi.python.org/packages/52/df/e4a408f3a7af396d186d4ecd3b389dd764f0f943b4fa8d257bfe7b49d343/bottle-0.12.8.tar.gz";
438 md5 = "13132c0a8f607bf860810a6ee9064c5b";
451 md5 = "13132c0a8f607bf860810a6ee9064c5b";
439 };
452 };
440 meta = {
453 meta = {
441 license = [ pkgs.lib.licenses.mit ];
454 license = [ pkgs.lib.licenses.mit ];
442 };
455 };
443 };
456 };
444 bumpversion = super.buildPythonPackage {
457 bumpversion = super.buildPythonPackage {
445 name = "bumpversion-0.5.3";
458 name = "bumpversion-0.5.3";
446 buildInputs = with self; [];
459 buildInputs = with self; [];
447 doCheck = false;
460 doCheck = false;
448 propagatedBuildInputs = with self; [];
461 propagatedBuildInputs = with self; [];
449 src = fetchurl {
462 src = fetchurl {
450 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
463 url = "https://pypi.python.org/packages/14/41/8c9da3549f8e00c84f0432c3a8cf8ed6898374714676aab91501d48760db/bumpversion-0.5.3.tar.gz";
451 md5 = "c66a3492eafcf5ad4b024be9fca29820";
464 md5 = "c66a3492eafcf5ad4b024be9fca29820";
452 };
465 };
453 meta = {
466 meta = {
454 license = [ pkgs.lib.licenses.mit ];
467 license = [ pkgs.lib.licenses.mit ];
455 };
468 };
456 };
469 };
457 celery = super.buildPythonPackage {
470 celery = super.buildPythonPackage {
458 name = "celery-2.2.10";
471 name = "celery-2.2.10";
459 buildInputs = with self; [];
472 buildInputs = with self; [];
460 doCheck = false;
473 doCheck = false;
461 propagatedBuildInputs = with self; [python-dateutil anyjson kombu pyparsing];
474 propagatedBuildInputs = with self; [python-dateutil anyjson kombu pyparsing];
462 src = fetchurl {
475 src = fetchurl {
463 url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz";
476 url = "https://pypi.python.org/packages/b1/64/860fd50e45844c83442e7953effcddeff66b2851d90b2d784f7201c111b8/celery-2.2.10.tar.gz";
464 md5 = "898bc87e54f278055b561316ba73e222";
477 md5 = "898bc87e54f278055b561316ba73e222";
465 };
478 };
466 meta = {
479 meta = {
467 license = [ pkgs.lib.licenses.bsdOriginal ];
480 license = [ pkgs.lib.licenses.bsdOriginal ];
468 };
481 };
469 };
482 };
470 click = super.buildPythonPackage {
483 click = super.buildPythonPackage {
471 name = "click-5.1";
484 name = "click-5.1";
472 buildInputs = with self; [];
485 buildInputs = with self; [];
473 doCheck = false;
486 doCheck = false;
474 propagatedBuildInputs = with self; [];
487 propagatedBuildInputs = with self; [];
475 src = fetchurl {
488 src = fetchurl {
476 url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz";
489 url = "https://pypi.python.org/packages/b7/34/a496632c4fb6c1ee76efedf77bb8d28b29363d839953d95095b12defe791/click-5.1.tar.gz";
477 md5 = "9c5323008cccfe232a8b161fc8196d41";
490 md5 = "9c5323008cccfe232a8b161fc8196d41";
478 };
491 };
479 meta = {
492 meta = {
480 license = [ pkgs.lib.licenses.bsdOriginal ];
493 license = [ pkgs.lib.licenses.bsdOriginal ];
481 };
494 };
482 };
495 };
483 colander = super.buildPythonPackage {
496 colander = super.buildPythonPackage {
484 name = "colander-1.2";
497 name = "colander-1.2";
485 buildInputs = with self; [];
498 buildInputs = with self; [];
486 doCheck = false;
499 doCheck = false;
487 propagatedBuildInputs = with self; [translationstring iso8601];
500 propagatedBuildInputs = with self; [translationstring iso8601];
488 src = fetchurl {
501 src = fetchurl {
489 url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz";
502 url = "https://pypi.python.org/packages/14/23/c9ceba07a6a1dc0eefbb215fc0dc64aabc2b22ee756bc0f0c13278fa0887/colander-1.2.tar.gz";
490 md5 = "83db21b07936a0726e588dae1914b9ed";
503 md5 = "83db21b07936a0726e588dae1914b9ed";
491 };
504 };
492 meta = {
505 meta = {
493 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
506 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
494 };
507 };
495 };
508 };
496 configobj = super.buildPythonPackage {
509 configobj = super.buildPythonPackage {
497 name = "configobj-5.0.6";
510 name = "configobj-5.0.6";
498 buildInputs = with self; [];
511 buildInputs = with self; [];
499 doCheck = false;
512 doCheck = false;
500 propagatedBuildInputs = with self; [six];
513 propagatedBuildInputs = with self; [six];
501 src = fetchurl {
514 src = fetchurl {
502 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
515 url = "https://pypi.python.org/packages/64/61/079eb60459c44929e684fa7d9e2fdca403f67d64dd9dbac27296be2e0fab/configobj-5.0.6.tar.gz";
503 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
516 md5 = "e472a3a1c2a67bb0ec9b5d54c13a47d6";
504 };
517 };
505 meta = {
518 meta = {
506 license = [ pkgs.lib.licenses.bsdOriginal ];
519 license = [ pkgs.lib.licenses.bsdOriginal ];
507 };
520 };
508 };
521 };
509 cov-core = super.buildPythonPackage {
522 cov-core = super.buildPythonPackage {
510 name = "cov-core-1.15.0";
523 name = "cov-core-1.15.0";
511 buildInputs = with self; [];
524 buildInputs = with self; [];
512 doCheck = false;
525 doCheck = false;
513 propagatedBuildInputs = with self; [coverage];
526 propagatedBuildInputs = with self; [coverage];
514 src = fetchurl {
527 src = fetchurl {
515 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
528 url = "https://pypi.python.org/packages/4b/87/13e75a47b4ba1be06f29f6d807ca99638bedc6b57fa491cd3de891ca2923/cov-core-1.15.0.tar.gz";
516 md5 = "f519d4cb4c4e52856afb14af52919fe6";
529 md5 = "f519d4cb4c4e52856afb14af52919fe6";
517 };
530 };
518 meta = {
531 meta = {
519 license = [ pkgs.lib.licenses.mit ];
532 license = [ pkgs.lib.licenses.mit ];
520 };
533 };
521 };
534 };
522 coverage = super.buildPythonPackage {
535 coverage = super.buildPythonPackage {
523 name = "coverage-3.7.1";
536 name = "coverage-3.7.1";
524 buildInputs = with self; [];
537 buildInputs = with self; [];
525 doCheck = false;
538 doCheck = false;
526 propagatedBuildInputs = with self; [];
539 propagatedBuildInputs = with self; [];
527 src = fetchurl {
540 src = fetchurl {
528 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
541 url = "https://pypi.python.org/packages/09/4f/89b06c7fdc09687bca507dc411c342556ef9c5a3b26756137a4878ff19bf/coverage-3.7.1.tar.gz";
529 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
542 md5 = "c47b36ceb17eaff3ecfab3bcd347d0df";
530 };
543 };
531 meta = {
544 meta = {
532 license = [ pkgs.lib.licenses.bsdOriginal ];
545 license = [ pkgs.lib.licenses.bsdOriginal ];
533 };
546 };
534 };
547 };
535 cssselect = super.buildPythonPackage {
548 cssselect = super.buildPythonPackage {
536 name = "cssselect-0.9.1";
549 name = "cssselect-0.9.1";
537 buildInputs = with self; [];
550 buildInputs = with self; [];
538 doCheck = false;
551 doCheck = false;
539 propagatedBuildInputs = with self; [];
552 propagatedBuildInputs = with self; [];
540 src = fetchurl {
553 src = fetchurl {
541 url = "https://pypi.python.org/packages/aa/e5/9ee1460d485b94a6d55732eb7ad5b6c084caf73dd6f9cb0bb7d2a78fafe8/cssselect-0.9.1.tar.gz";
554 url = "https://pypi.python.org/packages/aa/e5/9ee1460d485b94a6d55732eb7ad5b6c084caf73dd6f9cb0bb7d2a78fafe8/cssselect-0.9.1.tar.gz";
542 md5 = "c74f45966277dc7a0f768b9b0f3522ac";
555 md5 = "c74f45966277dc7a0f768b9b0f3522ac";
543 };
556 };
544 meta = {
557 meta = {
545 license = [ pkgs.lib.licenses.bsdOriginal ];
558 license = [ pkgs.lib.licenses.bsdOriginal ];
546 };
559 };
547 };
560 };
548 decorator = super.buildPythonPackage {
561 decorator = super.buildPythonPackage {
549 name = "decorator-3.4.2";
562 name = "decorator-3.4.2";
550 buildInputs = with self; [];
563 buildInputs = with self; [];
551 doCheck = false;
564 doCheck = false;
552 propagatedBuildInputs = with self; [];
565 propagatedBuildInputs = with self; [];
553 src = fetchurl {
566 src = fetchurl {
554 url = "https://pypi.python.org/packages/35/3a/42566eb7a2cbac774399871af04e11d7ae3fc2579e7dae85213b8d1d1c57/decorator-3.4.2.tar.gz";
567 url = "https://pypi.python.org/packages/35/3a/42566eb7a2cbac774399871af04e11d7ae3fc2579e7dae85213b8d1d1c57/decorator-3.4.2.tar.gz";
555 md5 = "9e0536870d2b83ae27d58dbf22582f4d";
568 md5 = "9e0536870d2b83ae27d58dbf22582f4d";
556 };
569 };
557 meta = {
570 meta = {
558 license = [ pkgs.lib.licenses.bsdOriginal ];
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 docutils = super.buildPythonPackage {
588 docutils = super.buildPythonPackage {
562 name = "docutils-0.12";
589 name = "docutils-0.12";
563 buildInputs = with self; [];
590 buildInputs = with self; [];
564 doCheck = false;
591 doCheck = false;
565 propagatedBuildInputs = with self; [];
592 propagatedBuildInputs = with self; [];
566 src = fetchurl {
593 src = fetchurl {
567 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
594 url = "https://pypi.python.org/packages/37/38/ceda70135b9144d84884ae2fc5886c6baac4edea39550f28bcd144c1234d/docutils-0.12.tar.gz";
568 md5 = "4622263b62c5c771c03502afa3157768";
595 md5 = "4622263b62c5c771c03502afa3157768";
569 };
596 };
570 meta = {
597 meta = {
571 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 ];
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 dogpile.cache = super.buildPythonPackage {
601 dogpile.cache = super.buildPythonPackage {
575 name = "dogpile.cache-0.6.1";
602 name = "dogpile.cache-0.6.1";
576 buildInputs = with self; [];
603 buildInputs = with self; [];
577 doCheck = false;
604 doCheck = false;
578 propagatedBuildInputs = with self; [dogpile.core];
605 propagatedBuildInputs = with self; [dogpile.core];
579 src = fetchurl {
606 src = fetchurl {
580 url = "https://pypi.python.org/packages/f6/a0/6f2142c58c6588d17c734265b103ae1cd0741e1681dd9483a63f22033375/dogpile.cache-0.6.1.tar.gz";
607 url = "https://pypi.python.org/packages/f6/a0/6f2142c58c6588d17c734265b103ae1cd0741e1681dd9483a63f22033375/dogpile.cache-0.6.1.tar.gz";
581 md5 = "35d7fb30f22bbd0685763d894dd079a9";
608 md5 = "35d7fb30f22bbd0685763d894dd079a9";
582 };
609 };
583 meta = {
610 meta = {
584 license = [ pkgs.lib.licenses.bsdOriginal ];
611 license = [ pkgs.lib.licenses.bsdOriginal ];
585 };
612 };
586 };
613 };
587 dogpile.core = super.buildPythonPackage {
614 dogpile.core = super.buildPythonPackage {
588 name = "dogpile.core-0.4.1";
615 name = "dogpile.core-0.4.1";
589 buildInputs = with self; [];
616 buildInputs = with self; [];
590 doCheck = false;
617 doCheck = false;
591 propagatedBuildInputs = with self; [];
618 propagatedBuildInputs = with self; [];
592 src = fetchurl {
619 src = fetchurl {
593 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
620 url = "https://pypi.python.org/packages/0e/77/e72abc04c22aedf874301861e5c1e761231c288b5de369c18be8f4b5c9bb/dogpile.core-0.4.1.tar.gz";
594 md5 = "01cb19f52bba3e95c9b560f39341f045";
621 md5 = "01cb19f52bba3e95c9b560f39341f045";
595 };
622 };
596 meta = {
623 meta = {
597 license = [ pkgs.lib.licenses.bsdOriginal ];
624 license = [ pkgs.lib.licenses.bsdOriginal ];
598 };
625 };
599 };
626 };
600 dulwich = super.buildPythonPackage {
627 dulwich = super.buildPythonPackage {
601 name = "dulwich-0.12.0";
628 name = "dulwich-0.12.0";
602 buildInputs = with self; [];
629 buildInputs = with self; [];
603 doCheck = false;
630 doCheck = false;
604 propagatedBuildInputs = with self; [];
631 propagatedBuildInputs = with self; [];
605 src = fetchurl {
632 src = fetchurl {
606 url = "https://pypi.python.org/packages/6f/04/fbe561b6d45c0ec758330d5b7f5ba4b6cb4f1ca1ab49859d2fc16320da75/dulwich-0.12.0.tar.gz";
633 url = "https://pypi.python.org/packages/6f/04/fbe561b6d45c0ec758330d5b7f5ba4b6cb4f1ca1ab49859d2fc16320da75/dulwich-0.12.0.tar.gz";
607 md5 = "f3a8a12bd9f9dd8c233e18f3d49436fa";
634 md5 = "f3a8a12bd9f9dd8c233e18f3d49436fa";
608 };
635 };
609 meta = {
636 meta = {
610 license = [ pkgs.lib.licenses.gpl2Plus ];
637 license = [ pkgs.lib.licenses.gpl2Plus ];
611 };
638 };
612 };
639 };
613 ecdsa = super.buildPythonPackage {
640 ecdsa = super.buildPythonPackage {
614 name = "ecdsa-0.11";
641 name = "ecdsa-0.11";
615 buildInputs = with self; [];
642 buildInputs = with self; [];
616 doCheck = false;
643 doCheck = false;
617 propagatedBuildInputs = with self; [];
644 propagatedBuildInputs = with self; [];
618 src = fetchurl {
645 src = fetchurl {
619 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
646 url = "https://pypi.python.org/packages/6c/3f/92fe5dcdcaa7bd117be21e5520c9a54375112b66ec000d209e9e9519fad1/ecdsa-0.11.tar.gz";
620 md5 = "8ef586fe4dbb156697d756900cb41d7c";
647 md5 = "8ef586fe4dbb156697d756900cb41d7c";
621 };
648 };
622 meta = {
649 meta = {
623 license = [ pkgs.lib.licenses.mit ];
650 license = [ pkgs.lib.licenses.mit ];
624 };
651 };
625 };
652 };
626 elasticsearch = super.buildPythonPackage {
653 elasticsearch = super.buildPythonPackage {
627 name = "elasticsearch-2.3.0";
654 name = "elasticsearch-2.3.0";
628 buildInputs = with self; [];
655 buildInputs = with self; [];
629 doCheck = false;
656 doCheck = false;
630 propagatedBuildInputs = with self; [urllib3];
657 propagatedBuildInputs = with self; [urllib3];
631 src = fetchurl {
658 src = fetchurl {
632 url = "https://pypi.python.org/packages/10/35/5fd52c5f0b0ee405ed4b5195e8bce44c5e041787680dc7b94b8071cac600/elasticsearch-2.3.0.tar.gz";
659 url = "https://pypi.python.org/packages/10/35/5fd52c5f0b0ee405ed4b5195e8bce44c5e041787680dc7b94b8071cac600/elasticsearch-2.3.0.tar.gz";
633 md5 = "2550f3b51629cf1ef9636608af92c340";
660 md5 = "2550f3b51629cf1ef9636608af92c340";
634 };
661 };
635 meta = {
662 meta = {
636 license = [ pkgs.lib.licenses.asl20 ];
663 license = [ pkgs.lib.licenses.asl20 ];
637 };
664 };
638 };
665 };
639 elasticsearch-dsl = super.buildPythonPackage {
666 elasticsearch-dsl = super.buildPythonPackage {
640 name = "elasticsearch-dsl-2.0.0";
667 name = "elasticsearch-dsl-2.0.0";
641 buildInputs = with self; [];
668 buildInputs = with self; [];
642 doCheck = false;
669 doCheck = false;
643 propagatedBuildInputs = with self; [six python-dateutil elasticsearch];
670 propagatedBuildInputs = with self; [six python-dateutil elasticsearch];
644 src = fetchurl {
671 src = fetchurl {
645 url = "https://pypi.python.org/packages/4e/5d/e788ae8dbe2ff4d13426db0a027533386a5c276c77a2654dc0e2007ce04a/elasticsearch-dsl-2.0.0.tar.gz";
672 url = "https://pypi.python.org/packages/4e/5d/e788ae8dbe2ff4d13426db0a027533386a5c276c77a2654dc0e2007ce04a/elasticsearch-dsl-2.0.0.tar.gz";
646 md5 = "4cdfec81bb35383dd3b7d02d7dc5ee68";
673 md5 = "4cdfec81bb35383dd3b7d02d7dc5ee68";
647 };
674 };
648 meta = {
675 meta = {
649 license = [ pkgs.lib.licenses.asl20 ];
676 license = [ pkgs.lib.licenses.asl20 ];
650 };
677 };
651 };
678 };
652 flake8 = super.buildPythonPackage {
679 flake8 = super.buildPythonPackage {
653 name = "flake8-2.4.1";
680 name = "flake8-2.4.1";
654 buildInputs = with self; [];
681 buildInputs = with self; [];
655 doCheck = false;
682 doCheck = false;
656 propagatedBuildInputs = with self; [pyflakes pep8 mccabe];
683 propagatedBuildInputs = with self; [pyflakes pep8 mccabe];
657 src = fetchurl {
684 src = fetchurl {
658 url = "https://pypi.python.org/packages/8f/b5/9a73c66c7dba273bac8758398f060c008a25f3e84531063b42503b5d0a95/flake8-2.4.1.tar.gz";
685 url = "https://pypi.python.org/packages/8f/b5/9a73c66c7dba273bac8758398f060c008a25f3e84531063b42503b5d0a95/flake8-2.4.1.tar.gz";
659 md5 = "ed45d3db81a3b7c88bd63c6e37ca1d65";
686 md5 = "ed45d3db81a3b7c88bd63c6e37ca1d65";
660 };
687 };
661 meta = {
688 meta = {
662 license = [ pkgs.lib.licenses.mit ];
689 license = [ pkgs.lib.licenses.mit ];
663 };
690 };
664 };
691 };
665 future = super.buildPythonPackage {
692 future = super.buildPythonPackage {
666 name = "future-0.14.3";
693 name = "future-0.14.3";
667 buildInputs = with self; [];
694 buildInputs = with self; [];
668 doCheck = false;
695 doCheck = false;
669 propagatedBuildInputs = with self; [];
696 propagatedBuildInputs = with self; [];
670 src = fetchurl {
697 src = fetchurl {
671 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
698 url = "https://pypi.python.org/packages/83/80/8ef3a11a15f8eaafafa0937b20c1b3f73527e69ab6b3fa1cf94a5a96aabb/future-0.14.3.tar.gz";
672 md5 = "e94079b0bd1fc054929e8769fc0f6083";
699 md5 = "e94079b0bd1fc054929e8769fc0f6083";
673 };
700 };
674 meta = {
701 meta = {
675 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
702 license = [ { fullName = "OSI Approved"; } pkgs.lib.licenses.mit ];
676 };
703 };
677 };
704 };
678 futures = super.buildPythonPackage {
705 futures = super.buildPythonPackage {
679 name = "futures-3.0.2";
706 name = "futures-3.0.2";
680 buildInputs = with self; [];
707 buildInputs = with self; [];
681 doCheck = false;
708 doCheck = false;
682 propagatedBuildInputs = with self; [];
709 propagatedBuildInputs = with self; [];
683 src = fetchurl {
710 src = fetchurl {
684 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
711 url = "https://pypi.python.org/packages/f8/e7/fc0fcbeb9193ba2d4de00b065e7fd5aecd0679e93ce95a07322b2b1434f4/futures-3.0.2.tar.gz";
685 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
712 md5 = "42aaf1e4de48d6e871d77dc1f9d96d5a";
686 };
713 };
687 meta = {
714 meta = {
688 license = [ pkgs.lib.licenses.bsdOriginal ];
715 license = [ pkgs.lib.licenses.bsdOriginal ];
689 };
716 };
690 };
717 };
691 gnureadline = super.buildPythonPackage {
718 gnureadline = super.buildPythonPackage {
692 name = "gnureadline-6.3.3";
719 name = "gnureadline-6.3.3";
693 buildInputs = with self; [];
720 buildInputs = with self; [];
694 doCheck = false;
721 doCheck = false;
695 propagatedBuildInputs = with self; [];
722 propagatedBuildInputs = with self; [];
696 src = fetchurl {
723 src = fetchurl {
697 url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz";
724 url = "https://pypi.python.org/packages/3a/ee/2c3f568b0a74974791ac590ec742ef6133e2fbd287a074ba72a53fa5e97c/gnureadline-6.3.3.tar.gz";
698 md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c";
725 md5 = "c4af83c9a3fbeac8f2da9b5a7c60e51c";
699 };
726 };
700 meta = {
727 meta = {
701 license = [ pkgs.lib.licenses.gpl1 ];
728 license = [ pkgs.lib.licenses.gpl1 ];
702 };
729 };
703 };
730 };
704 gprof2dot = super.buildPythonPackage {
731 gprof2dot = super.buildPythonPackage {
705 name = "gprof2dot-2015.12.1";
732 name = "gprof2dot-2015.12.1";
706 buildInputs = with self; [];
733 buildInputs = with self; [];
707 doCheck = false;
734 doCheck = false;
708 propagatedBuildInputs = with self; [];
735 propagatedBuildInputs = with self; [];
709 src = fetchurl {
736 src = fetchurl {
710 url = "https://pypi.python.org/packages/b9/34/7bf93c1952d40fa5c95ad963f4d8344b61ef58558632402eca18e6c14127/gprof2dot-2015.12.1.tar.gz";
737 url = "https://pypi.python.org/packages/b9/34/7bf93c1952d40fa5c95ad963f4d8344b61ef58558632402eca18e6c14127/gprof2dot-2015.12.1.tar.gz";
711 md5 = "e23bf4e2f94db032750c193384b4165b";
738 md5 = "e23bf4e2f94db032750c193384b4165b";
712 };
739 };
713 meta = {
740 meta = {
714 license = [ { fullName = "LGPL"; } ];
741 license = [ { fullName = "LGPL"; } ];
715 };
742 };
716 };
743 };
717 gunicorn = super.buildPythonPackage {
744 gunicorn = super.buildPythonPackage {
718 name = "gunicorn-19.6.0";
745 name = "gunicorn-19.6.0";
719 buildInputs = with self; [];
746 buildInputs = with self; [];
720 doCheck = false;
747 doCheck = false;
721 propagatedBuildInputs = with self; [];
748 propagatedBuildInputs = with self; [];
722 src = fetchurl {
749 src = fetchurl {
723 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
750 url = "https://pypi.python.org/packages/84/ce/7ea5396efad1cef682bbc4068e72a0276341d9d9d0f501da609fab9fcb80/gunicorn-19.6.0.tar.gz";
724 md5 = "338e5e8a83ea0f0625f768dba4597530";
751 md5 = "338e5e8a83ea0f0625f768dba4597530";
725 };
752 };
726 meta = {
753 meta = {
727 license = [ pkgs.lib.licenses.mit ];
754 license = [ pkgs.lib.licenses.mit ];
728 };
755 };
729 };
756 };
730 infrae.cache = super.buildPythonPackage {
757 infrae.cache = super.buildPythonPackage {
731 name = "infrae.cache-1.0.1";
758 name = "infrae.cache-1.0.1";
732 buildInputs = with self; [];
759 buildInputs = with self; [];
733 doCheck = false;
760 doCheck = false;
734 propagatedBuildInputs = with self; [Beaker repoze.lru];
761 propagatedBuildInputs = with self; [Beaker repoze.lru];
735 src = fetchurl {
762 src = fetchurl {
736 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
763 url = "https://pypi.python.org/packages/bb/f0/e7d5e984cf6592fd2807dc7bc44a93f9d18e04e6a61f87fdfb2622422d74/infrae.cache-1.0.1.tar.gz";
737 md5 = "b09076a766747e6ed2a755cc62088e32";
764 md5 = "b09076a766747e6ed2a755cc62088e32";
738 };
765 };
739 meta = {
766 meta = {
740 license = [ pkgs.lib.licenses.zpt21 ];
767 license = [ pkgs.lib.licenses.zpt21 ];
741 };
768 };
742 };
769 };
743 invoke = super.buildPythonPackage {
770 invoke = super.buildPythonPackage {
744 name = "invoke-0.13.0";
771 name = "invoke-0.13.0";
745 buildInputs = with self; [];
772 buildInputs = with self; [];
746 doCheck = false;
773 doCheck = false;
747 propagatedBuildInputs = with self; [];
774 propagatedBuildInputs = with self; [];
748 src = fetchurl {
775 src = fetchurl {
749 url = "https://pypi.python.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
776 url = "https://pypi.python.org/packages/47/bf/d07ef52fa1ac645468858bbac7cb95b246a972a045e821493d17d89c81be/invoke-0.13.0.tar.gz";
750 md5 = "c0d1ed4bfb34eaab551662d8cfee6540";
777 md5 = "c0d1ed4bfb34eaab551662d8cfee6540";
751 };
778 };
752 meta = {
779 meta = {
753 license = [ pkgs.lib.licenses.bsdOriginal ];
780 license = [ pkgs.lib.licenses.bsdOriginal ];
754 };
781 };
755 };
782 };
756 ipdb = super.buildPythonPackage {
783 ipdb = super.buildPythonPackage {
757 name = "ipdb-0.8";
784 name = "ipdb-0.8";
758 buildInputs = with self; [];
785 buildInputs = with self; [];
759 doCheck = false;
786 doCheck = false;
760 propagatedBuildInputs = with self; [ipython];
787 propagatedBuildInputs = with self; [ipython];
761 src = fetchurl {
788 src = fetchurl {
762 url = "https://pypi.python.org/packages/f0/25/d7dd430ced6cd8dc242a933c8682b5dbf32eb4011d82f87e34209e5ec845/ipdb-0.8.zip";
789 url = "https://pypi.python.org/packages/f0/25/d7dd430ced6cd8dc242a933c8682b5dbf32eb4011d82f87e34209e5ec845/ipdb-0.8.zip";
763 md5 = "96dca0712efa01aa5eaf6b22071dd3ed";
790 md5 = "96dca0712efa01aa5eaf6b22071dd3ed";
764 };
791 };
765 meta = {
792 meta = {
766 license = [ pkgs.lib.licenses.gpl1 ];
793 license = [ pkgs.lib.licenses.gpl1 ];
767 };
794 };
768 };
795 };
769 ipython = super.buildPythonPackage {
796 ipython = super.buildPythonPackage {
770 name = "ipython-3.1.0";
797 name = "ipython-3.1.0";
771 buildInputs = with self; [];
798 buildInputs = with self; [];
772 doCheck = false;
799 doCheck = false;
773 propagatedBuildInputs = with self; [];
800 propagatedBuildInputs = with self; [];
774 src = fetchurl {
801 src = fetchurl {
775 url = "https://pypi.python.org/packages/06/91/120c0835254c120af89f066afaabf81289bc2726c1fc3ca0555df6882f58/ipython-3.1.0.tar.gz";
802 url = "https://pypi.python.org/packages/06/91/120c0835254c120af89f066afaabf81289bc2726c1fc3ca0555df6882f58/ipython-3.1.0.tar.gz";
776 md5 = "a749d90c16068687b0ec45a27e72ef8f";
803 md5 = "a749d90c16068687b0ec45a27e72ef8f";
777 };
804 };
778 meta = {
805 meta = {
779 license = [ pkgs.lib.licenses.bsdOriginal ];
806 license = [ pkgs.lib.licenses.bsdOriginal ];
780 };
807 };
781 };
808 };
782 iso8601 = super.buildPythonPackage {
809 iso8601 = super.buildPythonPackage {
783 name = "iso8601-0.1.11";
810 name = "iso8601-0.1.11";
784 buildInputs = with self; [];
811 buildInputs = with self; [];
785 doCheck = false;
812 doCheck = false;
786 propagatedBuildInputs = with self; [];
813 propagatedBuildInputs = with self; [];
787 src = fetchurl {
814 src = fetchurl {
788 url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
815 url = "https://pypi.python.org/packages/c0/75/c9209ee4d1b5975eb8c2cba4428bde6b61bd55664a98290dd015cdb18e98/iso8601-0.1.11.tar.gz";
789 md5 = "b06d11cd14a64096f907086044f0fe38";
816 md5 = "b06d11cd14a64096f907086044f0fe38";
790 };
817 };
791 meta = {
818 meta = {
792 license = [ pkgs.lib.licenses.mit ];
819 license = [ pkgs.lib.licenses.mit ];
793 };
820 };
794 };
821 };
795 itsdangerous = super.buildPythonPackage {
822 itsdangerous = super.buildPythonPackage {
796 name = "itsdangerous-0.24";
823 name = "itsdangerous-0.24";
797 buildInputs = with self; [];
824 buildInputs = with self; [];
798 doCheck = false;
825 doCheck = false;
799 propagatedBuildInputs = with self; [];
826 propagatedBuildInputs = with self; [];
800 src = fetchurl {
827 src = fetchurl {
801 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
828 url = "https://pypi.python.org/packages/dc/b4/a60bcdba945c00f6d608d8975131ab3f25b22f2bcfe1dab221165194b2d4/itsdangerous-0.24.tar.gz";
802 md5 = "a3d55aa79369aef5345c036a8a26307f";
829 md5 = "a3d55aa79369aef5345c036a8a26307f";
803 };
830 };
804 meta = {
831 meta = {
805 license = [ pkgs.lib.licenses.bsdOriginal ];
832 license = [ pkgs.lib.licenses.bsdOriginal ];
806 };
833 };
807 };
834 };
808 kombu = super.buildPythonPackage {
835 kombu = super.buildPythonPackage {
809 name = "kombu-1.5.1";
836 name = "kombu-1.5.1";
810 buildInputs = with self; [];
837 buildInputs = with self; [];
811 doCheck = false;
838 doCheck = false;
812 propagatedBuildInputs = with self; [anyjson amqplib];
839 propagatedBuildInputs = with self; [anyjson amqplib];
813 src = fetchurl {
840 src = fetchurl {
814 url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz";
841 url = "https://pypi.python.org/packages/19/53/74bf2a624644b45f0850a638752514fc10a8e1cbd738f10804951a6df3f5/kombu-1.5.1.tar.gz";
815 md5 = "50662f3c7e9395b3d0721fb75d100b63";
842 md5 = "50662f3c7e9395b3d0721fb75d100b63";
816 };
843 };
817 meta = {
844 meta = {
818 license = [ pkgs.lib.licenses.bsdOriginal ];
845 license = [ pkgs.lib.licenses.bsdOriginal ];
819 };
846 };
820 };
847 };
821 lxml = super.buildPythonPackage {
848 lxml = super.buildPythonPackage {
822 name = "lxml-3.4.4";
849 name = "lxml-3.4.4";
823 buildInputs = with self; [];
850 buildInputs = with self; [];
824 doCheck = false;
851 doCheck = false;
825 propagatedBuildInputs = with self; [];
852 propagatedBuildInputs = with self; [];
826 src = fetchurl {
853 src = fetchurl {
827 url = "https://pypi.python.org/packages/63/c7/4f2a2a4ad6c6fa99b14be6b3c1cece9142e2d915aa7c43c908677afc8fa4/lxml-3.4.4.tar.gz";
854 url = "https://pypi.python.org/packages/63/c7/4f2a2a4ad6c6fa99b14be6b3c1cece9142e2d915aa7c43c908677afc8fa4/lxml-3.4.4.tar.gz";
828 md5 = "a9a65972afc173ec7a39c585f4eea69c";
855 md5 = "a9a65972afc173ec7a39c585f4eea69c";
829 };
856 };
830 meta = {
857 meta = {
831 license = [ pkgs.lib.licenses.bsdOriginal ];
858 license = [ pkgs.lib.licenses.bsdOriginal ];
832 };
859 };
833 };
860 };
834 mccabe = super.buildPythonPackage {
861 mccabe = super.buildPythonPackage {
835 name = "mccabe-0.3";
862 name = "mccabe-0.3";
836 buildInputs = with self; [];
863 buildInputs = with self; [];
837 doCheck = false;
864 doCheck = false;
838 propagatedBuildInputs = with self; [];
865 propagatedBuildInputs = with self; [];
839 src = fetchurl {
866 src = fetchurl {
840 url = "https://pypi.python.org/packages/c9/2e/75231479e11a906b64ac43bad9d0bb534d00080b18bdca8db9da46e1faf7/mccabe-0.3.tar.gz";
867 url = "https://pypi.python.org/packages/c9/2e/75231479e11a906b64ac43bad9d0bb534d00080b18bdca8db9da46e1faf7/mccabe-0.3.tar.gz";
841 md5 = "81640948ff226f8c12b3277059489157";
868 md5 = "81640948ff226f8c12b3277059489157";
842 };
869 };
843 meta = {
870 meta = {
844 license = [ { fullName = "Expat license"; } pkgs.lib.licenses.mit ];
871 license = [ { fullName = "Expat license"; } pkgs.lib.licenses.mit ];
845 };
872 };
846 };
873 };
847 meld3 = super.buildPythonPackage {
874 meld3 = super.buildPythonPackage {
848 name = "meld3-1.0.2";
875 name = "meld3-1.0.2";
849 buildInputs = with self; [];
876 buildInputs = with self; [];
850 doCheck = false;
877 doCheck = false;
851 propagatedBuildInputs = with self; [];
878 propagatedBuildInputs = with self; [];
852 src = fetchurl {
879 src = fetchurl {
853 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
880 url = "https://pypi.python.org/packages/45/a0/317c6422b26c12fe0161e936fc35f36552069ba8e6f7ecbd99bbffe32a5f/meld3-1.0.2.tar.gz";
854 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
881 md5 = "3ccc78cd79cffd63a751ad7684c02c91";
855 };
882 };
856 meta = {
883 meta = {
857 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
884 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
858 };
885 };
859 };
886 };
860 mock = super.buildPythonPackage {
887 mock = super.buildPythonPackage {
861 name = "mock-1.0.1";
888 name = "mock-1.0.1";
862 buildInputs = with self; [];
889 buildInputs = with self; [];
863 doCheck = false;
890 doCheck = false;
864 propagatedBuildInputs = with self; [];
891 propagatedBuildInputs = with self; [];
865 src = fetchurl {
892 src = fetchurl {
866 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
893 url = "https://pypi.python.org/packages/15/45/30273ee91feb60dabb8fbb2da7868520525f02cf910279b3047182feed80/mock-1.0.1.zip";
867 md5 = "869f08d003c289a97c1a6610faf5e913";
894 md5 = "869f08d003c289a97c1a6610faf5e913";
868 };
895 };
869 meta = {
896 meta = {
870 license = [ pkgs.lib.licenses.bsdOriginal ];
897 license = [ pkgs.lib.licenses.bsdOriginal ];
871 };
898 };
872 };
899 };
873 msgpack-python = super.buildPythonPackage {
900 msgpack-python = super.buildPythonPackage {
874 name = "msgpack-python-0.4.6";
901 name = "msgpack-python-0.4.6";
875 buildInputs = with self; [];
902 buildInputs = with self; [];
876 doCheck = false;
903 doCheck = false;
877 propagatedBuildInputs = with self; [];
904 propagatedBuildInputs = with self; [];
878 src = fetchurl {
905 src = fetchurl {
879 url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz";
906 url = "https://pypi.python.org/packages/15/ce/ff2840885789ef8035f66cd506ea05bdb228340307d5e71a7b1e3f82224c/msgpack-python-0.4.6.tar.gz";
880 md5 = "8b317669314cf1bc881716cccdaccb30";
907 md5 = "8b317669314cf1bc881716cccdaccb30";
881 };
908 };
882 meta = {
909 meta = {
883 license = [ pkgs.lib.licenses.asl20 ];
910 license = [ pkgs.lib.licenses.asl20 ];
884 };
911 };
885 };
912 };
886 nose = super.buildPythonPackage {
913 nose = super.buildPythonPackage {
887 name = "nose-1.3.6";
914 name = "nose-1.3.6";
888 buildInputs = with self; [];
915 buildInputs = with self; [];
889 doCheck = false;
916 doCheck = false;
890 propagatedBuildInputs = with self; [];
917 propagatedBuildInputs = with self; [];
891 src = fetchurl {
918 src = fetchurl {
892 url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz";
919 url = "https://pypi.python.org/packages/70/c7/469e68148d17a0d3db5ed49150242fd70a74a8147b8f3f8b87776e028d99/nose-1.3.6.tar.gz";
893 md5 = "0ca546d81ca8309080fc80cb389e7a16";
920 md5 = "0ca546d81ca8309080fc80cb389e7a16";
894 };
921 };
895 meta = {
922 meta = {
896 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "GNU LGPL"; } ];
923 license = [ { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "GNU LGPL"; } ];
897 };
924 };
898 };
925 };
899 objgraph = super.buildPythonPackage {
926 objgraph = super.buildPythonPackage {
900 name = "objgraph-2.0.0";
927 name = "objgraph-2.0.0";
901 buildInputs = with self; [];
928 buildInputs = with self; [];
902 doCheck = false;
929 doCheck = false;
903 propagatedBuildInputs = with self; [];
930 propagatedBuildInputs = with self; [];
904 src = fetchurl {
931 src = fetchurl {
905 url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz";
932 url = "https://pypi.python.org/packages/d7/33/ace750b59247496ed769b170586c5def7202683f3d98e737b75b767ff29e/objgraph-2.0.0.tar.gz";
906 md5 = "25b0d5e5adc74aa63ead15699614159c";
933 md5 = "25b0d5e5adc74aa63ead15699614159c";
907 };
934 };
908 meta = {
935 meta = {
909 license = [ pkgs.lib.licenses.mit ];
936 license = [ pkgs.lib.licenses.mit ];
910 };
937 };
911 };
938 };
912 packaging = super.buildPythonPackage {
939 packaging = super.buildPythonPackage {
913 name = "packaging-15.2";
940 name = "packaging-15.2";
914 buildInputs = with self; [];
941 buildInputs = with self; [];
915 doCheck = false;
942 doCheck = false;
916 propagatedBuildInputs = with self; [];
943 propagatedBuildInputs = with self; [];
917 src = fetchurl {
944 src = fetchurl {
918 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
945 url = "https://pypi.python.org/packages/24/c4/185da1304f07047dc9e0c46c31db75c0351bd73458ac3efad7da3dbcfbe1/packaging-15.2.tar.gz";
919 md5 = "c16093476f6ced42128bf610e5db3784";
946 md5 = "c16093476f6ced42128bf610e5db3784";
920 };
947 };
921 meta = {
948 meta = {
922 license = [ pkgs.lib.licenses.asl20 ];
949 license = [ pkgs.lib.licenses.asl20 ];
923 };
950 };
924 };
951 };
925 paramiko = super.buildPythonPackage {
952 paramiko = super.buildPythonPackage {
926 name = "paramiko-1.15.1";
953 name = "paramiko-1.15.1";
927 buildInputs = with self; [];
954 buildInputs = with self; [];
928 doCheck = false;
955 doCheck = false;
929 propagatedBuildInputs = with self; [pycrypto ecdsa];
956 propagatedBuildInputs = with self; [pycrypto ecdsa];
930 src = fetchurl {
957 src = fetchurl {
931 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
958 url = "https://pypi.python.org/packages/04/2b/a22d2a560c1951abbbf95a0628e245945565f70dc082d9e784666887222c/paramiko-1.15.1.tar.gz";
932 md5 = "48c274c3f9b1282932567b21f6acf3b5";
959 md5 = "48c274c3f9b1282932567b21f6acf3b5";
933 };
960 };
934 meta = {
961 meta = {
935 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
962 license = [ { fullName = "LGPL"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
936 };
963 };
937 };
964 };
938 pep8 = super.buildPythonPackage {
965 pep8 = super.buildPythonPackage {
939 name = "pep8-1.5.7";
966 name = "pep8-1.5.7";
940 buildInputs = with self; [];
967 buildInputs = with self; [];
941 doCheck = false;
968 doCheck = false;
942 propagatedBuildInputs = with self; [];
969 propagatedBuildInputs = with self; [];
943 src = fetchurl {
970 src = fetchurl {
944 url = "https://pypi.python.org/packages/8b/de/259f5e735897ada1683489dd514b2a1c91aaa74e5e6b68f80acf128a6368/pep8-1.5.7.tar.gz";
971 url = "https://pypi.python.org/packages/8b/de/259f5e735897ada1683489dd514b2a1c91aaa74e5e6b68f80acf128a6368/pep8-1.5.7.tar.gz";
945 md5 = "f6adbdd69365ecca20513c709f9b7c93";
972 md5 = "f6adbdd69365ecca20513c709f9b7c93";
946 };
973 };
947 meta = {
974 meta = {
948 license = [ { fullName = "Expat license"; } pkgs.lib.licenses.mit ];
975 license = [ { fullName = "Expat license"; } pkgs.lib.licenses.mit ];
949 };
976 };
950 };
977 };
951 psutil = super.buildPythonPackage {
978 psutil = super.buildPythonPackage {
952 name = "psutil-2.2.1";
979 name = "psutil-2.2.1";
953 buildInputs = with self; [];
980 buildInputs = with self; [];
954 doCheck = false;
981 doCheck = false;
955 propagatedBuildInputs = with self; [];
982 propagatedBuildInputs = with self; [];
956 src = fetchurl {
983 src = fetchurl {
957 url = "https://pypi.python.org/packages/df/47/ee54ef14dd40f8ce831a7581001a5096494dc99fe71586260ca6b531fe86/psutil-2.2.1.tar.gz";
984 url = "https://pypi.python.org/packages/df/47/ee54ef14dd40f8ce831a7581001a5096494dc99fe71586260ca6b531fe86/psutil-2.2.1.tar.gz";
958 md5 = "1a2b58cd9e3a53528bb6148f0c4d5244";
985 md5 = "1a2b58cd9e3a53528bb6148f0c4d5244";
959 };
986 };
960 meta = {
987 meta = {
961 license = [ pkgs.lib.licenses.bsdOriginal ];
988 license = [ pkgs.lib.licenses.bsdOriginal ];
962 };
989 };
963 };
990 };
964 psycopg2 = super.buildPythonPackage {
991 psycopg2 = super.buildPythonPackage {
965 name = "psycopg2-2.6.1";
992 name = "psycopg2-2.6.1";
966 buildInputs = with self; [];
993 buildInputs = with self; [];
967 doCheck = false;
994 doCheck = false;
968 propagatedBuildInputs = with self; [];
995 propagatedBuildInputs = with self; [];
969 src = fetchurl {
996 src = fetchurl {
970 url = "https://pypi.python.org/packages/86/fd/cc8315be63a41fe000cce20482a917e874cdc1151e62cb0141f5e55f711e/psycopg2-2.6.1.tar.gz";
997 url = "https://pypi.python.org/packages/86/fd/cc8315be63a41fe000cce20482a917e874cdc1151e62cb0141f5e55f711e/psycopg2-2.6.1.tar.gz";
971 md5 = "842b44f8c95517ed5b792081a2370da1";
998 md5 = "842b44f8c95517ed5b792081a2370da1";
972 };
999 };
973 meta = {
1000 meta = {
974 license = [ pkgs.lib.licenses.zpt21 { fullName = "GNU Library or Lesser General Public License (LGPL)"; } { fullName = "LGPL with exceptions or ZPL"; } ];
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 py = super.buildPythonPackage {
1004 py = super.buildPythonPackage {
978 name = "py-1.4.29";
1005 name = "py-1.4.29";
979 buildInputs = with self; [];
1006 buildInputs = with self; [];
980 doCheck = false;
1007 doCheck = false;
981 propagatedBuildInputs = with self; [];
1008 propagatedBuildInputs = with self; [];
982 src = fetchurl {
1009 src = fetchurl {
983 url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz";
1010 url = "https://pypi.python.org/packages/2a/bc/a1a4a332ac10069b8e5e25136a35e08a03f01fd6ab03d819889d79a1fd65/py-1.4.29.tar.gz";
984 md5 = "c28e0accba523a29b35a48bb703fb96c";
1011 md5 = "c28e0accba523a29b35a48bb703fb96c";
985 };
1012 };
986 meta = {
1013 meta = {
987 license = [ pkgs.lib.licenses.mit ];
1014 license = [ pkgs.lib.licenses.mit ];
988 };
1015 };
989 };
1016 };
990 py-bcrypt = super.buildPythonPackage {
1017 py-bcrypt = super.buildPythonPackage {
991 name = "py-bcrypt-0.4";
1018 name = "py-bcrypt-0.4";
992 buildInputs = with self; [];
1019 buildInputs = with self; [];
993 doCheck = false;
1020 doCheck = false;
994 propagatedBuildInputs = with self; [];
1021 propagatedBuildInputs = with self; [];
995 src = fetchurl {
1022 src = fetchurl {
996 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
1023 url = "https://pypi.python.org/packages/68/b1/1c3068c5c4d2e35c48b38dcc865301ebfdf45f54507086ac65ced1fd3b3d/py-bcrypt-0.4.tar.gz";
997 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
1024 md5 = "dd8b367d6b716a2ea2e72392525f4e36";
998 };
1025 };
999 meta = {
1026 meta = {
1000 license = [ pkgs.lib.licenses.bsdOriginal ];
1027 license = [ pkgs.lib.licenses.bsdOriginal ];
1001 };
1028 };
1002 };
1029 };
1003 py-gfm = super.buildPythonPackage {
1030 py-gfm = super.buildPythonPackage {
1004 name = "py-gfm-0.1.3";
1031 name = "py-gfm-0.1.3";
1005 buildInputs = with self; [];
1032 buildInputs = with self; [];
1006 doCheck = false;
1033 doCheck = false;
1007 propagatedBuildInputs = with self; [setuptools Markdown];
1034 propagatedBuildInputs = with self; [setuptools Markdown];
1008 src = fetchurl {
1035 src = fetchurl {
1009 url = "https://pypi.python.org/packages/12/e4/6b3d8678da04f97d7490d8264d8de51c2dc9fb91209ccee9c515c95e14c5/py-gfm-0.1.3.tar.gz";
1036 url = "https://pypi.python.org/packages/12/e4/6b3d8678da04f97d7490d8264d8de51c2dc9fb91209ccee9c515c95e14c5/py-gfm-0.1.3.tar.gz";
1010 md5 = "e588d9e69640a241b97e2c59c22527a6";
1037 md5 = "e588d9e69640a241b97e2c59c22527a6";
1011 };
1038 };
1012 meta = {
1039 meta = {
1013 license = [ pkgs.lib.licenses.bsdOriginal ];
1040 license = [ pkgs.lib.licenses.bsdOriginal ];
1014 };
1041 };
1015 };
1042 };
1016 pycrypto = super.buildPythonPackage {
1043 pycrypto = super.buildPythonPackage {
1017 name = "pycrypto-2.6.1";
1044 name = "pycrypto-2.6.1";
1018 buildInputs = with self; [];
1045 buildInputs = with self; [];
1019 doCheck = false;
1046 doCheck = false;
1020 propagatedBuildInputs = with self; [];
1047 propagatedBuildInputs = with self; [];
1021 src = fetchurl {
1048 src = fetchurl {
1022 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1049 url = "https://pypi.python.org/packages/60/db/645aa9af249f059cc3a368b118de33889219e0362141e75d4eaf6f80f163/pycrypto-2.6.1.tar.gz";
1023 md5 = "55a61a054aa66812daf5161a0d5d7eda";
1050 md5 = "55a61a054aa66812daf5161a0d5d7eda";
1024 };
1051 };
1025 meta = {
1052 meta = {
1026 license = [ pkgs.lib.licenses.publicDomain ];
1053 license = [ pkgs.lib.licenses.publicDomain ];
1027 };
1054 };
1028 };
1055 };
1029 pycurl = super.buildPythonPackage {
1056 pycurl = super.buildPythonPackage {
1030 name = "pycurl-7.19.5";
1057 name = "pycurl-7.19.5";
1031 buildInputs = with self; [];
1058 buildInputs = with self; [];
1032 doCheck = false;
1059 doCheck = false;
1033 propagatedBuildInputs = with self; [];
1060 propagatedBuildInputs = with self; [];
1034 src = fetchurl {
1061 src = fetchurl {
1035 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
1062 url = "https://pypi.python.org/packages/6c/48/13bad289ef6f4869b1d8fc11ae54de8cfb3cc4a2eb9f7419c506f763be46/pycurl-7.19.5.tar.gz";
1036 md5 = "47b4eac84118e2606658122104e62072";
1063 md5 = "47b4eac84118e2606658122104e62072";
1037 };
1064 };
1038 meta = {
1065 meta = {
1039 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1066 license = [ pkgs.lib.licenses.mit { fullName = "LGPL/MIT"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1040 };
1067 };
1041 };
1068 };
1042 pyflakes = super.buildPythonPackage {
1069 pyflakes = super.buildPythonPackage {
1043 name = "pyflakes-0.8.1";
1070 name = "pyflakes-0.8.1";
1044 buildInputs = with self; [];
1071 buildInputs = with self; [];
1045 doCheck = false;
1072 doCheck = false;
1046 propagatedBuildInputs = with self; [];
1073 propagatedBuildInputs = with self; [];
1047 src = fetchurl {
1074 src = fetchurl {
1048 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
1075 url = "https://pypi.python.org/packages/75/22/a90ec0252f4f87f3ffb6336504de71fe16a49d69c4538dae2f12b9360a38/pyflakes-0.8.1.tar.gz";
1049 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
1076 md5 = "905fe91ad14b912807e8fdc2ac2e2c23";
1050 };
1077 };
1051 meta = {
1078 meta = {
1052 license = [ pkgs.lib.licenses.mit ];
1079 license = [ pkgs.lib.licenses.mit ];
1053 };
1080 };
1054 };
1081 };
1055 pyparsing = super.buildPythonPackage {
1082 pyparsing = super.buildPythonPackage {
1056 name = "pyparsing-1.5.7";
1083 name = "pyparsing-1.5.7";
1057 buildInputs = with self; [];
1084 buildInputs = with self; [];
1058 doCheck = false;
1085 doCheck = false;
1059 propagatedBuildInputs = with self; [];
1086 propagatedBuildInputs = with self; [];
1060 src = fetchurl {
1087 src = fetchurl {
1061 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
1088 url = "https://pypi.python.org/packages/2e/26/e8fb5b4256a5f5036be7ce115ef8db8d06bc537becfbdc46c6af008314ee/pyparsing-1.5.7.zip";
1062 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
1089 md5 = "b86854857a368d6ccb4d5b6e76d0637f";
1063 };
1090 };
1064 meta = {
1091 meta = {
1065 license = [ pkgs.lib.licenses.mit ];
1092 license = [ pkgs.lib.licenses.mit ];
1066 };
1093 };
1067 };
1094 };
1068 pyramid = super.buildPythonPackage {
1095 pyramid = super.buildPythonPackage {
1069 name = "pyramid-1.6.1";
1096 name = "pyramid-1.6.1";
1070 buildInputs = with self; [];
1097 buildInputs = with self; [];
1071 doCheck = false;
1098 doCheck = false;
1072 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
1099 propagatedBuildInputs = with self; [setuptools WebOb repoze.lru zope.interface zope.deprecation venusian translationstring PasteDeploy];
1073 src = fetchurl {
1100 src = fetchurl {
1074 url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz";
1101 url = "https://pypi.python.org/packages/30/b3/fcc4a2a4800cbf21989e00454b5828cf1f7fe35c63e0810b350e56d4c475/pyramid-1.6.1.tar.gz";
1075 md5 = "b18688ff3cc33efdbb098a35b45dd122";
1102 md5 = "b18688ff3cc33efdbb098a35b45dd122";
1076 };
1103 };
1077 meta = {
1104 meta = {
1078 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1105 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1079 };
1106 };
1080 };
1107 };
1081 pyramid-beaker = super.buildPythonPackage {
1108 pyramid-beaker = super.buildPythonPackage {
1082 name = "pyramid-beaker-0.8";
1109 name = "pyramid-beaker-0.8";
1083 buildInputs = with self; [];
1110 buildInputs = with self; [];
1084 doCheck = false;
1111 doCheck = false;
1085 propagatedBuildInputs = with self; [pyramid Beaker];
1112 propagatedBuildInputs = with self; [pyramid Beaker];
1086 src = fetchurl {
1113 src = fetchurl {
1087 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
1114 url = "https://pypi.python.org/packages/d9/6e/b85426e00fd3d57f4545f74e1c3828552d8700f13ededeef9233f7bca8be/pyramid_beaker-0.8.tar.gz";
1088 md5 = "22f14be31b06549f80890e2c63a93834";
1115 md5 = "22f14be31b06549f80890e2c63a93834";
1089 };
1116 };
1090 meta = {
1117 meta = {
1091 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1118 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1092 };
1119 };
1093 };
1120 };
1094 pyramid-debugtoolbar = super.buildPythonPackage {
1121 pyramid-debugtoolbar = super.buildPythonPackage {
1095 name = "pyramid-debugtoolbar-2.4.2";
1122 name = "pyramid-debugtoolbar-2.4.2";
1096 buildInputs = with self; [];
1123 buildInputs = with self; [];
1097 doCheck = false;
1124 doCheck = false;
1098 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments];
1125 propagatedBuildInputs = with self; [pyramid pyramid-mako repoze.lru Pygments];
1099 src = fetchurl {
1126 src = fetchurl {
1100 url = "https://pypi.python.org/packages/89/00/ed5426ee41ed747ba3ffd30e8230841a6878286ea67d480b1444d24f06a2/pyramid_debugtoolbar-2.4.2.tar.gz";
1127 url = "https://pypi.python.org/packages/89/00/ed5426ee41ed747ba3ffd30e8230841a6878286ea67d480b1444d24f06a2/pyramid_debugtoolbar-2.4.2.tar.gz";
1101 md5 = "073ea67086cc4bd5decc3a000853642d";
1128 md5 = "073ea67086cc4bd5decc3a000853642d";
1102 };
1129 };
1103 meta = {
1130 meta = {
1104 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1131 license = [ { fullName = "Repoze Public License"; } pkgs.lib.licenses.bsdOriginal ];
1105 };
1132 };
1106 };
1133 };
1107 pyramid-jinja2 = super.buildPythonPackage {
1134 pyramid-jinja2 = super.buildPythonPackage {
1108 name = "pyramid-jinja2-2.5";
1135 name = "pyramid-jinja2-2.5";
1109 buildInputs = with self; [];
1136 buildInputs = with self; [];
1110 doCheck = false;
1137 doCheck = false;
1111 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
1138 propagatedBuildInputs = with self; [pyramid zope.deprecation Jinja2 MarkupSafe];
1112 src = fetchurl {
1139 src = fetchurl {
1113 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
1140 url = "https://pypi.python.org/packages/a1/80/595e26ffab7deba7208676b6936b7e5a721875710f982e59899013cae1ed/pyramid_jinja2-2.5.tar.gz";
1114 md5 = "07cb6547204ac5e6f0b22a954ccee928";
1141 md5 = "07cb6547204ac5e6f0b22a954ccee928";
1115 };
1142 };
1116 meta = {
1143 meta = {
1117 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1144 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1118 };
1145 };
1119 };
1146 };
1120 pyramid-mako = super.buildPythonPackage {
1147 pyramid-mako = super.buildPythonPackage {
1121 name = "pyramid-mako-1.0.2";
1148 name = "pyramid-mako-1.0.2";
1122 buildInputs = with self; [];
1149 buildInputs = with self; [];
1123 doCheck = false;
1150 doCheck = false;
1124 propagatedBuildInputs = with self; [pyramid Mako];
1151 propagatedBuildInputs = with self; [pyramid Mako];
1125 src = fetchurl {
1152 src = fetchurl {
1126 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
1153 url = "https://pypi.python.org/packages/f1/92/7e69bcf09676d286a71cb3bbb887b16595b96f9ba7adbdc239ffdd4b1eb9/pyramid_mako-1.0.2.tar.gz";
1127 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
1154 md5 = "ee25343a97eb76bd90abdc2a774eb48a";
1128 };
1155 };
1129 meta = {
1156 meta = {
1130 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1157 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1131 };
1158 };
1132 };
1159 };
1133 pysqlite = super.buildPythonPackage {
1160 pysqlite = super.buildPythonPackage {
1134 name = "pysqlite-2.6.3";
1161 name = "pysqlite-2.6.3";
1135 buildInputs = with self; [];
1162 buildInputs = with self; [];
1136 doCheck = false;
1163 doCheck = false;
1137 propagatedBuildInputs = with self; [];
1164 propagatedBuildInputs = with self; [];
1138 src = fetchurl {
1165 src = fetchurl {
1139 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
1166 url = "https://pypi.python.org/packages/5c/a6/1c429cd4c8069cf4bfbd0eb4d592b3f4042155a8202df83d7e9b93aa3dc2/pysqlite-2.6.3.tar.gz";
1140 md5 = "7ff1cedee74646b50117acff87aa1cfa";
1167 md5 = "7ff1cedee74646b50117acff87aa1cfa";
1141 };
1168 };
1142 meta = {
1169 meta = {
1143 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1170 license = [ { fullName = "zlib/libpng License"; } { fullName = "zlib/libpng license"; } ];
1144 };
1171 };
1145 };
1172 };
1146 pytest = super.buildPythonPackage {
1173 pytest = super.buildPythonPackage {
1147 name = "pytest-2.8.5";
1174 name = "pytest-2.8.5";
1148 buildInputs = with self; [];
1175 buildInputs = with self; [];
1149 doCheck = false;
1176 doCheck = false;
1150 propagatedBuildInputs = with self; [py];
1177 propagatedBuildInputs = with self; [py];
1151 src = fetchurl {
1178 src = fetchurl {
1152 url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip";
1179 url = "https://pypi.python.org/packages/b1/3d/d7ea9b0c51e0cacded856e49859f0a13452747491e842c236bbab3714afe/pytest-2.8.5.zip";
1153 md5 = "8493b06f700862f1294298d6c1b715a9";
1180 md5 = "8493b06f700862f1294298d6c1b715a9";
1154 };
1181 };
1155 meta = {
1182 meta = {
1156 license = [ pkgs.lib.licenses.mit ];
1183 license = [ pkgs.lib.licenses.mit ];
1157 };
1184 };
1158 };
1185 };
1159 pytest-catchlog = super.buildPythonPackage {
1186 pytest-catchlog = super.buildPythonPackage {
1160 name = "pytest-catchlog-1.2.2";
1187 name = "pytest-catchlog-1.2.2";
1161 buildInputs = with self; [];
1188 buildInputs = with self; [];
1162 doCheck = false;
1189 doCheck = false;
1163 propagatedBuildInputs = with self; [py pytest];
1190 propagatedBuildInputs = with self; [py pytest];
1164 src = fetchurl {
1191 src = fetchurl {
1165 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
1192 url = "https://pypi.python.org/packages/f2/2b/2faccdb1a978fab9dd0bf31cca9f6847fbe9184a0bdcc3011ac41dd44191/pytest-catchlog-1.2.2.zip";
1166 md5 = "09d890c54c7456c818102b7ff8c182c8";
1193 md5 = "09d890c54c7456c818102b7ff8c182c8";
1167 };
1194 };
1168 meta = {
1195 meta = {
1169 license = [ pkgs.lib.licenses.mit ];
1196 license = [ pkgs.lib.licenses.mit ];
1170 };
1197 };
1171 };
1198 };
1172 pytest-cov = super.buildPythonPackage {
1199 pytest-cov = super.buildPythonPackage {
1173 name = "pytest-cov-1.8.1";
1200 name = "pytest-cov-1.8.1";
1174 buildInputs = with self; [];
1201 buildInputs = with self; [];
1175 doCheck = false;
1202 doCheck = false;
1176 propagatedBuildInputs = with self; [py pytest coverage cov-core];
1203 propagatedBuildInputs = with self; [py pytest coverage cov-core];
1177 src = fetchurl {
1204 src = fetchurl {
1178 url = "https://pypi.python.org/packages/11/4b/b04646e97f1721878eb21e9f779102d84dd044d324382263b1770a3e4838/pytest-cov-1.8.1.tar.gz";
1205 url = "https://pypi.python.org/packages/11/4b/b04646e97f1721878eb21e9f779102d84dd044d324382263b1770a3e4838/pytest-cov-1.8.1.tar.gz";
1179 md5 = "76c778afa2494088270348be42d759fc";
1206 md5 = "76c778afa2494088270348be42d759fc";
1180 };
1207 };
1181 meta = {
1208 meta = {
1182 license = [ pkgs.lib.licenses.mit ];
1209 license = [ pkgs.lib.licenses.mit ];
1183 };
1210 };
1184 };
1211 };
1185 pytest-profiling = super.buildPythonPackage {
1212 pytest-profiling = super.buildPythonPackage {
1186 name = "pytest-profiling-1.0.1";
1213 name = "pytest-profiling-1.0.1";
1187 buildInputs = with self; [];
1214 buildInputs = with self; [];
1188 doCheck = false;
1215 doCheck = false;
1189 propagatedBuildInputs = with self; [six pytest gprof2dot];
1216 propagatedBuildInputs = with self; [six pytest gprof2dot];
1190 src = fetchurl {
1217 src = fetchurl {
1191 url = "https://pypi.python.org/packages/d8/67/8ffab73406e22870e07fa4dc8dce1d7689b26dba8efd00161c9b6fc01ec0/pytest-profiling-1.0.1.tar.gz";
1218 url = "https://pypi.python.org/packages/d8/67/8ffab73406e22870e07fa4dc8dce1d7689b26dba8efd00161c9b6fc01ec0/pytest-profiling-1.0.1.tar.gz";
1192 md5 = "354404eb5b3fd4dc5eb7fffbb3d9b68b";
1219 md5 = "354404eb5b3fd4dc5eb7fffbb3d9b68b";
1193 };
1220 };
1194 meta = {
1221 meta = {
1195 license = [ pkgs.lib.licenses.mit ];
1222 license = [ pkgs.lib.licenses.mit ];
1196 };
1223 };
1197 };
1224 };
1198 pytest-runner = super.buildPythonPackage {
1225 pytest-runner = super.buildPythonPackage {
1199 name = "pytest-runner-2.7.1";
1226 name = "pytest-runner-2.7.1";
1200 buildInputs = with self; [];
1227 buildInputs = with self; [];
1201 doCheck = false;
1228 doCheck = false;
1202 propagatedBuildInputs = with self; [];
1229 propagatedBuildInputs = with self; [];
1203 src = fetchurl {
1230 src = fetchurl {
1204 url = "https://pypi.python.org/packages/99/6b/c4ff4418d3424d4475b7af60724fd4a5cdd91ed8e489dc9443281f0052bc/pytest-runner-2.7.1.tar.gz";
1231 url = "https://pypi.python.org/packages/99/6b/c4ff4418d3424d4475b7af60724fd4a5cdd91ed8e489dc9443281f0052bc/pytest-runner-2.7.1.tar.gz";
1205 md5 = "e56f0bc8d79a6bd91772b44ef4215c7e";
1232 md5 = "e56f0bc8d79a6bd91772b44ef4215c7e";
1206 };
1233 };
1207 meta = {
1234 meta = {
1208 license = [ pkgs.lib.licenses.mit ];
1235 license = [ pkgs.lib.licenses.mit ];
1209 };
1236 };
1210 };
1237 };
1211 pytest-timeout = super.buildPythonPackage {
1238 pytest-timeout = super.buildPythonPackage {
1212 name = "pytest-timeout-0.4";
1239 name = "pytest-timeout-0.4";
1213 buildInputs = with self; [];
1240 buildInputs = with self; [];
1214 doCheck = false;
1241 doCheck = false;
1215 propagatedBuildInputs = with self; [pytest];
1242 propagatedBuildInputs = with self; [pytest];
1216 src = fetchurl {
1243 src = fetchurl {
1217 url = "https://pypi.python.org/packages/24/48/5f6bd4b8026a26e1dd427243d560a29a0f1b24a5c7cffca4bf049a7bb65b/pytest-timeout-0.4.tar.gz";
1244 url = "https://pypi.python.org/packages/24/48/5f6bd4b8026a26e1dd427243d560a29a0f1b24a5c7cffca4bf049a7bb65b/pytest-timeout-0.4.tar.gz";
1218 md5 = "03b28aff69cbbfb959ed35ade5fde262";
1245 md5 = "03b28aff69cbbfb959ed35ade5fde262";
1219 };
1246 };
1220 meta = {
1247 meta = {
1221 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1248 license = [ pkgs.lib.licenses.mit { fullName = "DFSG approved"; } ];
1222 };
1249 };
1223 };
1250 };
1224 python-dateutil = super.buildPythonPackage {
1251 python-dateutil = super.buildPythonPackage {
1225 name = "python-dateutil-1.5";
1252 name = "python-dateutil-1.5";
1226 buildInputs = with self; [];
1253 buildInputs = with self; [];
1227 doCheck = false;
1254 doCheck = false;
1228 propagatedBuildInputs = with self; [];
1255 propagatedBuildInputs = with self; [];
1229 src = fetchurl {
1256 src = fetchurl {
1230 url = "https://pypi.python.org/packages/b4/7c/df59c89a753eb33c7c44e1dd42de0e9bc2ccdd5a4d576e0bfad97cc280cb/python-dateutil-1.5.tar.gz";
1257 url = "https://pypi.python.org/packages/b4/7c/df59c89a753eb33c7c44e1dd42de0e9bc2ccdd5a4d576e0bfad97cc280cb/python-dateutil-1.5.tar.gz";
1231 md5 = "0dcb1de5e5cad69490a3b6ab63f0cfa5";
1258 md5 = "0dcb1de5e5cad69490a3b6ab63f0cfa5";
1232 };
1259 };
1233 meta = {
1260 meta = {
1234 license = [ pkgs.lib.licenses.psfl ];
1261 license = [ pkgs.lib.licenses.psfl ];
1235 };
1262 };
1236 };
1263 };
1237 python-editor = super.buildPythonPackage {
1264 python-editor = super.buildPythonPackage {
1238 name = "python-editor-1.0.1";
1265 name = "python-editor-1.0.1";
1239 buildInputs = with self; [];
1266 buildInputs = with self; [];
1240 doCheck = false;
1267 doCheck = false;
1241 propagatedBuildInputs = with self; [];
1268 propagatedBuildInputs = with self; [];
1242 src = fetchurl {
1269 src = fetchurl {
1243 url = "https://pypi.python.org/packages/2b/c0/df7b87d5cf016f82eab3b05cd35f53287c1178ad8c42bfb6fa61b89b22f6/python-editor-1.0.1.tar.gz";
1270 url = "https://pypi.python.org/packages/2b/c0/df7b87d5cf016f82eab3b05cd35f53287c1178ad8c42bfb6fa61b89b22f6/python-editor-1.0.1.tar.gz";
1244 md5 = "e1fa63535b40e022fa4fd646fd8b511a";
1271 md5 = "e1fa63535b40e022fa4fd646fd8b511a";
1245 };
1272 };
1246 meta = {
1273 meta = {
1247 license = [ pkgs.lib.licenses.asl20 ];
1274 license = [ pkgs.lib.licenses.asl20 ];
1248 };
1275 };
1249 };
1276 };
1250 python-ldap = super.buildPythonPackage {
1277 python-ldap = super.buildPythonPackage {
1251 name = "python-ldap-2.4.19";
1278 name = "python-ldap-2.4.19";
1252 buildInputs = with self; [];
1279 buildInputs = with self; [];
1253 doCheck = false;
1280 doCheck = false;
1254 propagatedBuildInputs = with self; [setuptools];
1281 propagatedBuildInputs = with self; [setuptools];
1255 src = fetchurl {
1282 src = fetchurl {
1256 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
1283 url = "https://pypi.python.org/packages/42/81/1b64838c82e64f14d4e246ff00b52e650a35c012551b891ada2b85d40737/python-ldap-2.4.19.tar.gz";
1257 md5 = "b941bf31d09739492aa19ef679e94ae3";
1284 md5 = "b941bf31d09739492aa19ef679e94ae3";
1258 };
1285 };
1259 meta = {
1286 meta = {
1260 license = [ pkgs.lib.licenses.psfl ];
1287 license = [ pkgs.lib.licenses.psfl ];
1261 };
1288 };
1262 };
1289 };
1263 python-memcached = super.buildPythonPackage {
1290 python-memcached = super.buildPythonPackage {
1264 name = "python-memcached-1.57";
1291 name = "python-memcached-1.57";
1265 buildInputs = with self; [];
1292 buildInputs = with self; [];
1266 doCheck = false;
1293 doCheck = false;
1267 propagatedBuildInputs = with self; [six];
1294 propagatedBuildInputs = with self; [six];
1268 src = fetchurl {
1295 src = fetchurl {
1269 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
1296 url = "https://pypi.python.org/packages/52/9d/eebc0dcbc5c7c66840ad207dfc1baa376dadb74912484bff73819cce01e6/python-memcached-1.57.tar.gz";
1270 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
1297 md5 = "de21f64b42b2d961f3d4ad7beb5468a1";
1271 };
1298 };
1272 meta = {
1299 meta = {
1273 license = [ pkgs.lib.licenses.psfl ];
1300 license = [ pkgs.lib.licenses.psfl ];
1274 };
1301 };
1275 };
1302 };
1276 python-pam = super.buildPythonPackage {
1303 python-pam = super.buildPythonPackage {
1277 name = "python-pam-1.8.2";
1304 name = "python-pam-1.8.2";
1278 buildInputs = with self; [];
1305 buildInputs = with self; [];
1279 doCheck = false;
1306 doCheck = false;
1280 propagatedBuildInputs = with self; [];
1307 propagatedBuildInputs = with self; [];
1281 src = fetchurl {
1308 src = fetchurl {
1282 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
1309 url = "https://pypi.python.org/packages/de/8c/f8f5d38b4f26893af267ea0b39023d4951705ab0413a39e0cf7cf4900505/python-pam-1.8.2.tar.gz";
1283 md5 = "db71b6b999246fb05d78ecfbe166629d";
1310 md5 = "db71b6b999246fb05d78ecfbe166629d";
1284 };
1311 };
1285 meta = {
1312 meta = {
1286 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1313 license = [ { fullName = "License :: OSI Approved :: MIT License"; } pkgs.lib.licenses.mit ];
1287 };
1314 };
1288 };
1315 };
1289 pytz = super.buildPythonPackage {
1316 pytz = super.buildPythonPackage {
1290 name = "pytz-2015.4";
1317 name = "pytz-2015.4";
1291 buildInputs = with self; [];
1318 buildInputs = with self; [];
1292 doCheck = false;
1319 doCheck = false;
1293 propagatedBuildInputs = with self; [];
1320 propagatedBuildInputs = with self; [];
1294 src = fetchurl {
1321 src = fetchurl {
1295 url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip";
1322 url = "https://pypi.python.org/packages/7e/1a/f43b5c92df7b156822030fed151327ea096bcf417e45acc23bd1df43472f/pytz-2015.4.zip";
1296 md5 = "233f2a2b370d03f9b5911700cc9ebf3c";
1323 md5 = "233f2a2b370d03f9b5911700cc9ebf3c";
1297 };
1324 };
1298 meta = {
1325 meta = {
1299 license = [ pkgs.lib.licenses.mit ];
1326 license = [ pkgs.lib.licenses.mit ];
1300 };
1327 };
1301 };
1328 };
1302 pyzmq = super.buildPythonPackage {
1329 pyzmq = super.buildPythonPackage {
1303 name = "pyzmq-14.6.0";
1330 name = "pyzmq-14.6.0";
1304 buildInputs = with self; [];
1331 buildInputs = with self; [];
1305 doCheck = false;
1332 doCheck = false;
1306 propagatedBuildInputs = with self; [];
1333 propagatedBuildInputs = with self; [];
1307 src = fetchurl {
1334 src = fetchurl {
1308 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1335 url = "https://pypi.python.org/packages/8a/3b/5463d5a9d712cd8bbdac335daece0d69f6a6792da4e3dd89956c0db4e4e6/pyzmq-14.6.0.tar.gz";
1309 md5 = "395b5de95a931afa5b14c9349a5b8024";
1336 md5 = "395b5de95a931afa5b14c9349a5b8024";
1310 };
1337 };
1311 meta = {
1338 meta = {
1312 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1339 license = [ pkgs.lib.licenses.bsdOriginal { fullName = "LGPL+BSD"; } { fullName = "GNU Library or Lesser General Public License (LGPL)"; } ];
1313 };
1340 };
1314 };
1341 };
1315 recaptcha-client = super.buildPythonPackage {
1342 recaptcha-client = super.buildPythonPackage {
1316 name = "recaptcha-client-1.0.6";
1343 name = "recaptcha-client-1.0.6";
1317 buildInputs = with self; [];
1344 buildInputs = with self; [];
1318 doCheck = false;
1345 doCheck = false;
1319 propagatedBuildInputs = with self; [];
1346 propagatedBuildInputs = with self; [];
1320 src = fetchurl {
1347 src = fetchurl {
1321 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1348 url = "https://pypi.python.org/packages/0a/ea/5f2fbbfd894bdac1c68ef8d92019066cfcf9fbff5fe3d728d2b5c25c8db4/recaptcha-client-1.0.6.tar.gz";
1322 md5 = "74228180f7e1fb76c4d7089160b0d919";
1349 md5 = "74228180f7e1fb76c4d7089160b0d919";
1323 };
1350 };
1324 meta = {
1351 meta = {
1325 license = [ { fullName = "MIT/X11"; } ];
1352 license = [ { fullName = "MIT/X11"; } ];
1326 };
1353 };
1327 };
1354 };
1328 repoze.lru = super.buildPythonPackage {
1355 repoze.lru = super.buildPythonPackage {
1329 name = "repoze.lru-0.6";
1356 name = "repoze.lru-0.6";
1330 buildInputs = with self; [];
1357 buildInputs = with self; [];
1331 doCheck = false;
1358 doCheck = false;
1332 propagatedBuildInputs = with self; [];
1359 propagatedBuildInputs = with self; [];
1333 src = fetchurl {
1360 src = fetchurl {
1334 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
1361 url = "https://pypi.python.org/packages/6e/1e/aa15cc90217e086dc8769872c8778b409812ff036bf021b15795638939e4/repoze.lru-0.6.tar.gz";
1335 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
1362 md5 = "2c3b64b17a8e18b405f55d46173e14dd";
1336 };
1363 };
1337 meta = {
1364 meta = {
1338 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1365 license = [ { fullName = "Repoze Public License"; } { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1339 };
1366 };
1340 };
1367 };
1341 requests = super.buildPythonPackage {
1368 requests = super.buildPythonPackage {
1342 name = "requests-2.9.1";
1369 name = "requests-2.9.1";
1343 buildInputs = with self; [];
1370 buildInputs = with self; [];
1344 doCheck = false;
1371 doCheck = false;
1345 propagatedBuildInputs = with self; [];
1372 propagatedBuildInputs = with self; [];
1346 src = fetchurl {
1373 src = fetchurl {
1347 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1374 url = "https://pypi.python.org/packages/f9/6d/07c44fb1ebe04d069459a189e7dab9e4abfe9432adcd4477367c25332748/requests-2.9.1.tar.gz";
1348 md5 = "0b7f480d19012ec52bab78292efd976d";
1375 md5 = "0b7f480d19012ec52bab78292efd976d";
1349 };
1376 };
1350 meta = {
1377 meta = {
1351 license = [ pkgs.lib.licenses.asl20 ];
1378 license = [ pkgs.lib.licenses.asl20 ];
1352 };
1379 };
1353 };
1380 };
1354 rhodecode-enterprise-ce = super.buildPythonPackage {
1381 rhodecode-enterprise-ce = super.buildPythonPackage {
1355 name = "rhodecode-enterprise-ce-4.3.0";
1382 name = "rhodecode-enterprise-ce-4.3.0";
1356 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
1383 buildInputs = with self; [WebTest configobj cssselect flake8 lxml mock pytest pytest-cov pytest-runner];
1357 doCheck = true;
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 src = ./.;
1386 src = ./.;
1360 meta = {
1387 meta = {
1361 license = [ { fullName = "AGPLv3, and Commercial License"; } ];
1388 license = [ { fullName = "AGPLv3, and Commercial License"; } ];
1362 };
1389 };
1363 };
1390 };
1364 rhodecode-tools = super.buildPythonPackage {
1391 rhodecode-tools = super.buildPythonPackage {
1365 name = "rhodecode-tools-0.8.3";
1392 name = "rhodecode-tools-0.8.3";
1366 buildInputs = with self; [];
1393 buildInputs = with self; [];
1367 doCheck = false;
1394 doCheck = false;
1368 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests Whoosh elasticsearch elasticsearch-dsl];
1395 propagatedBuildInputs = with self; [click future six Mako MarkupSafe requests Whoosh elasticsearch elasticsearch-dsl];
1369 src = fetchurl {
1396 src = fetchurl {
1370 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.8.3.zip";
1397 url = "https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.8.3.zip";
1371 md5 = "9acdfd71b8ddf4056057065f37ab9ccb";
1398 md5 = "9acdfd71b8ddf4056057065f37ab9ccb";
1372 };
1399 };
1373 meta = {
1400 meta = {
1374 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1401 license = [ { fullName = "AGPLv3 and Proprietary"; } ];
1375 };
1402 };
1376 };
1403 };
1377 serpent = super.buildPythonPackage {
1404 serpent = super.buildPythonPackage {
1378 name = "serpent-1.12";
1405 name = "serpent-1.12";
1379 buildInputs = with self; [];
1406 buildInputs = with self; [];
1380 doCheck = false;
1407 doCheck = false;
1381 propagatedBuildInputs = with self; [];
1408 propagatedBuildInputs = with self; [];
1382 src = fetchurl {
1409 src = fetchurl {
1383 url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz";
1410 url = "https://pypi.python.org/packages/3b/19/1e0e83b47c09edaef8398655088036e7e67386b5c48770218ebb339fbbd5/serpent-1.12.tar.gz";
1384 md5 = "05869ac7b062828b34f8f927f0457b65";
1411 md5 = "05869ac7b062828b34f8f927f0457b65";
1385 };
1412 };
1386 meta = {
1413 meta = {
1387 license = [ pkgs.lib.licenses.mit ];
1414 license = [ pkgs.lib.licenses.mit ];
1388 };
1415 };
1389 };
1416 };
1390 setproctitle = super.buildPythonPackage {
1417 setproctitle = super.buildPythonPackage {
1391 name = "setproctitle-1.1.8";
1418 name = "setproctitle-1.1.8";
1392 buildInputs = with self; [];
1419 buildInputs = with self; [];
1393 doCheck = false;
1420 doCheck = false;
1394 propagatedBuildInputs = with self; [];
1421 propagatedBuildInputs = with self; [];
1395 src = fetchurl {
1422 src = fetchurl {
1396 url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz";
1423 url = "https://pypi.python.org/packages/33/c3/ad367a4f4f1ca90468863ae727ac62f6edb558fc09a003d344a02cfc6ea6/setproctitle-1.1.8.tar.gz";
1397 md5 = "728f4c8c6031bbe56083a48594027edd";
1424 md5 = "728f4c8c6031bbe56083a48594027edd";
1398 };
1425 };
1399 meta = {
1426 meta = {
1400 license = [ pkgs.lib.licenses.bsdOriginal ];
1427 license = [ pkgs.lib.licenses.bsdOriginal ];
1401 };
1428 };
1402 };
1429 };
1403 setuptools = super.buildPythonPackage {
1430 setuptools = super.buildPythonPackage {
1404 name = "setuptools-20.8.1";
1431 name = "setuptools-20.8.1";
1405 buildInputs = with self; [];
1432 buildInputs = with self; [];
1406 doCheck = false;
1433 doCheck = false;
1407 propagatedBuildInputs = with self; [];
1434 propagatedBuildInputs = with self; [];
1408 src = fetchurl {
1435 src = fetchurl {
1409 url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip";
1436 url = "https://pypi.python.org/packages/c4/19/c1bdc88b53da654df43770f941079dbab4e4788c2dcb5658fb86259894c7/setuptools-20.8.1.zip";
1410 md5 = "fe58a5cac0df20bb83942b252a4b0543";
1437 md5 = "fe58a5cac0df20bb83942b252a4b0543";
1411 };
1438 };
1412 meta = {
1439 meta = {
1413 license = [ pkgs.lib.licenses.mit ];
1440 license = [ pkgs.lib.licenses.mit ];
1414 };
1441 };
1415 };
1442 };
1416 setuptools-scm = super.buildPythonPackage {
1443 setuptools-scm = super.buildPythonPackage {
1417 name = "setuptools-scm-1.11.0";
1444 name = "setuptools-scm-1.11.0";
1418 buildInputs = with self; [];
1445 buildInputs = with self; [];
1419 doCheck = false;
1446 doCheck = false;
1420 propagatedBuildInputs = with self; [];
1447 propagatedBuildInputs = with self; [];
1421 src = fetchurl {
1448 src = fetchurl {
1422 url = "https://pypi.python.org/packages/cd/5f/e3a038292358058d83d764a47d09114aa5a8003ed4529518f9e580f1a94f/setuptools_scm-1.11.0.tar.gz";
1449 url = "https://pypi.python.org/packages/cd/5f/e3a038292358058d83d764a47d09114aa5a8003ed4529518f9e580f1a94f/setuptools_scm-1.11.0.tar.gz";
1423 md5 = "4c5c896ba52e134bbc3507bac6400087";
1450 md5 = "4c5c896ba52e134bbc3507bac6400087";
1424 };
1451 };
1425 meta = {
1452 meta = {
1426 license = [ pkgs.lib.licenses.mit ];
1453 license = [ pkgs.lib.licenses.mit ];
1427 };
1454 };
1428 };
1455 };
1429 simplejson = super.buildPythonPackage {
1456 simplejson = super.buildPythonPackage {
1430 name = "simplejson-3.7.2";
1457 name = "simplejson-3.7.2";
1431 buildInputs = with self; [];
1458 buildInputs = with self; [];
1432 doCheck = false;
1459 doCheck = false;
1433 propagatedBuildInputs = with self; [];
1460 propagatedBuildInputs = with self; [];
1434 src = fetchurl {
1461 src = fetchurl {
1435 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1462 url = "https://pypi.python.org/packages/6d/89/7f13f099344eea9d6722779a1f165087cb559598107844b1ac5dbd831fb1/simplejson-3.7.2.tar.gz";
1436 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1463 md5 = "a5fc7d05d4cb38492285553def5d4b46";
1437 };
1464 };
1438 meta = {
1465 meta = {
1439 license = [ pkgs.lib.licenses.mit pkgs.lib.licenses.afl21 ];
1466 license = [ pkgs.lib.licenses.mit pkgs.lib.licenses.afl21 ];
1440 };
1467 };
1441 };
1468 };
1442 six = super.buildPythonPackage {
1469 six = super.buildPythonPackage {
1443 name = "six-1.9.0";
1470 name = "six-1.9.0";
1444 buildInputs = with self; [];
1471 buildInputs = with self; [];
1445 doCheck = false;
1472 doCheck = false;
1446 propagatedBuildInputs = with self; [];
1473 propagatedBuildInputs = with self; [];
1447 src = fetchurl {
1474 src = fetchurl {
1448 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
1475 url = "https://pypi.python.org/packages/16/64/1dc5e5976b17466fd7d712e59cbe9fb1e18bec153109e5ba3ed6c9102f1a/six-1.9.0.tar.gz";
1449 md5 = "476881ef4012262dfc8adc645ee786c4";
1476 md5 = "476881ef4012262dfc8adc645ee786c4";
1450 };
1477 };
1451 meta = {
1478 meta = {
1452 license = [ pkgs.lib.licenses.mit ];
1479 license = [ pkgs.lib.licenses.mit ];
1453 };
1480 };
1454 };
1481 };
1455 subprocess32 = super.buildPythonPackage {
1482 subprocess32 = super.buildPythonPackage {
1456 name = "subprocess32-3.2.6";
1483 name = "subprocess32-3.2.6";
1457 buildInputs = with self; [];
1484 buildInputs = with self; [];
1458 doCheck = false;
1485 doCheck = false;
1459 propagatedBuildInputs = with self; [];
1486 propagatedBuildInputs = with self; [];
1460 src = fetchurl {
1487 src = fetchurl {
1461 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1488 url = "https://pypi.python.org/packages/28/8d/33ccbff51053f59ae6c357310cac0e79246bbed1d345ecc6188b176d72c3/subprocess32-3.2.6.tar.gz";
1462 md5 = "754c5ab9f533e764f931136974b618f1";
1489 md5 = "754c5ab9f533e764f931136974b618f1";
1463 };
1490 };
1464 meta = {
1491 meta = {
1465 license = [ pkgs.lib.licenses.psfl ];
1492 license = [ pkgs.lib.licenses.psfl ];
1466 };
1493 };
1467 };
1494 };
1468 supervisor = super.buildPythonPackage {
1495 supervisor = super.buildPythonPackage {
1469 name = "supervisor-3.3.0";
1496 name = "supervisor-3.3.0";
1470 buildInputs = with self; [];
1497 buildInputs = with self; [];
1471 doCheck = false;
1498 doCheck = false;
1472 propagatedBuildInputs = with self; [meld3];
1499 propagatedBuildInputs = with self; [meld3];
1473 src = fetchurl {
1500 src = fetchurl {
1474 url = "https://pypi.python.org/packages/44/80/d28047d120bfcc8158b4e41127706731ee6a3419c661e0a858fb0e7c4b2d/supervisor-3.3.0.tar.gz";
1501 url = "https://pypi.python.org/packages/44/80/d28047d120bfcc8158b4e41127706731ee6a3419c661e0a858fb0e7c4b2d/supervisor-3.3.0.tar.gz";
1475 md5 = "46bac00378d1eddb616752b990c67416";
1502 md5 = "46bac00378d1eddb616752b990c67416";
1476 };
1503 };
1477 meta = {
1504 meta = {
1478 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1505 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1479 };
1506 };
1480 };
1507 };
1481 transifex-client = super.buildPythonPackage {
1508 transifex-client = super.buildPythonPackage {
1482 name = "transifex-client-0.10";
1509 name = "transifex-client-0.10";
1483 buildInputs = with self; [];
1510 buildInputs = with self; [];
1484 doCheck = false;
1511 doCheck = false;
1485 propagatedBuildInputs = with self; [];
1512 propagatedBuildInputs = with self; [];
1486 src = fetchurl {
1513 src = fetchurl {
1487 url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz";
1514 url = "https://pypi.python.org/packages/f3/4e/7b925192aee656fb3e04fa6381c8b3dc40198047c3b4a356f6cfd642c809/transifex-client-0.10.tar.gz";
1488 md5 = "5549538d84b8eede6b254cd81ae024fa";
1515 md5 = "5549538d84b8eede6b254cd81ae024fa";
1489 };
1516 };
1490 meta = {
1517 meta = {
1491 license = [ pkgs.lib.licenses.gpl2 ];
1518 license = [ pkgs.lib.licenses.gpl2 ];
1492 };
1519 };
1493 };
1520 };
1494 translationstring = super.buildPythonPackage {
1521 translationstring = super.buildPythonPackage {
1495 name = "translationstring-1.3";
1522 name = "translationstring-1.3";
1496 buildInputs = with self; [];
1523 buildInputs = with self; [];
1497 doCheck = false;
1524 doCheck = false;
1498 propagatedBuildInputs = with self; [];
1525 propagatedBuildInputs = with self; [];
1499 src = fetchurl {
1526 src = fetchurl {
1500 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1527 url = "https://pypi.python.org/packages/5e/eb/bee578cc150b44c653b63f5ebe258b5d0d812ddac12497e5f80fcad5d0b4/translationstring-1.3.tar.gz";
1501 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1528 md5 = "a4b62e0f3c189c783a1685b3027f7c90";
1502 };
1529 };
1503 meta = {
1530 meta = {
1504 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
1531 license = [ { fullName = "BSD-like (http://repoze.org/license.html)"; } ];
1505 };
1532 };
1506 };
1533 };
1507 trollius = super.buildPythonPackage {
1534 trollius = super.buildPythonPackage {
1508 name = "trollius-1.0.4";
1535 name = "trollius-1.0.4";
1509 buildInputs = with self; [];
1536 buildInputs = with self; [];
1510 doCheck = false;
1537 doCheck = false;
1511 propagatedBuildInputs = with self; [futures];
1538 propagatedBuildInputs = with self; [futures];
1512 src = fetchurl {
1539 src = fetchurl {
1513 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1540 url = "https://pypi.python.org/packages/aa/e6/4141db437f55e6ee7a3fb69663239e3fde7841a811b4bef293145ad6c836/trollius-1.0.4.tar.gz";
1514 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1541 md5 = "3631a464d49d0cbfd30ab2918ef2b783";
1515 };
1542 };
1516 meta = {
1543 meta = {
1517 license = [ pkgs.lib.licenses.asl20 ];
1544 license = [ pkgs.lib.licenses.asl20 ];
1518 };
1545 };
1519 };
1546 };
1520 uWSGI = super.buildPythonPackage {
1547 uWSGI = super.buildPythonPackage {
1521 name = "uWSGI-2.0.11.2";
1548 name = "uWSGI-2.0.11.2";
1522 buildInputs = with self; [];
1549 buildInputs = with self; [];
1523 doCheck = false;
1550 doCheck = false;
1524 propagatedBuildInputs = with self; [];
1551 propagatedBuildInputs = with self; [];
1525 src = fetchurl {
1552 src = fetchurl {
1526 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1553 url = "https://pypi.python.org/packages/9b/78/918db0cfab0546afa580c1e565209c49aaf1476bbfe491314eadbe47c556/uwsgi-2.0.11.2.tar.gz";
1527 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1554 md5 = "1f02dcbee7f6f61de4b1fd68350cf16f";
1528 };
1555 };
1529 meta = {
1556 meta = {
1530 license = [ pkgs.lib.licenses.gpl2 ];
1557 license = [ pkgs.lib.licenses.gpl2 ];
1531 };
1558 };
1532 };
1559 };
1533 urllib3 = super.buildPythonPackage {
1560 urllib3 = super.buildPythonPackage {
1534 name = "urllib3-1.16";
1561 name = "urllib3-1.16";
1535 buildInputs = with self; [];
1562 buildInputs = with self; [];
1536 doCheck = false;
1563 doCheck = false;
1537 propagatedBuildInputs = with self; [];
1564 propagatedBuildInputs = with self; [];
1538 src = fetchurl {
1565 src = fetchurl {
1539 url = "https://pypi.python.org/packages/3b/f0/e763169124e3f5db0926bc3dbfcd580a105f9ca44cf5d8e6c7a803c9f6b5/urllib3-1.16.tar.gz";
1566 url = "https://pypi.python.org/packages/3b/f0/e763169124e3f5db0926bc3dbfcd580a105f9ca44cf5d8e6c7a803c9f6b5/urllib3-1.16.tar.gz";
1540 md5 = "fcaab1c5385c57deeb7053d3d7d81d59";
1567 md5 = "fcaab1c5385c57deeb7053d3d7d81d59";
1541 };
1568 };
1542 meta = {
1569 meta = {
1543 license = [ pkgs.lib.licenses.mit ];
1570 license = [ pkgs.lib.licenses.mit ];
1544 };
1571 };
1545 };
1572 };
1546 venusian = super.buildPythonPackage {
1573 venusian = super.buildPythonPackage {
1547 name = "venusian-1.0";
1574 name = "venusian-1.0";
1548 buildInputs = with self; [];
1575 buildInputs = with self; [];
1549 doCheck = false;
1576 doCheck = false;
1550 propagatedBuildInputs = with self; [];
1577 propagatedBuildInputs = with self; [];
1551 src = fetchurl {
1578 src = fetchurl {
1552 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1579 url = "https://pypi.python.org/packages/86/20/1948e0dfc4930ddde3da8c33612f6a5717c0b4bc28f591a5c5cf014dd390/venusian-1.0.tar.gz";
1553 md5 = "dccf2eafb7113759d60c86faf5538756";
1580 md5 = "dccf2eafb7113759d60c86faf5538756";
1554 };
1581 };
1555 meta = {
1582 meta = {
1556 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1583 license = [ { fullName = "BSD-derived (http://www.repoze.org/LICENSE.txt)"; } ];
1557 };
1584 };
1558 };
1585 };
1559 waitress = super.buildPythonPackage {
1586 waitress = super.buildPythonPackage {
1560 name = "waitress-0.8.9";
1587 name = "waitress-0.8.9";
1561 buildInputs = with self; [];
1588 buildInputs = with self; [];
1562 doCheck = false;
1589 doCheck = false;
1563 propagatedBuildInputs = with self; [setuptools];
1590 propagatedBuildInputs = with self; [setuptools];
1564 src = fetchurl {
1591 src = fetchurl {
1565 url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz";
1592 url = "https://pypi.python.org/packages/ee/65/fc9dee74a909a1187ca51e4f15ad9c4d35476e4ab5813f73421505c48053/waitress-0.8.9.tar.gz";
1566 md5 = "da3f2e62b3676be5dd630703a68e2a04";
1593 md5 = "da3f2e62b3676be5dd630703a68e2a04";
1567 };
1594 };
1568 meta = {
1595 meta = {
1569 license = [ pkgs.lib.licenses.zpt21 ];
1596 license = [ pkgs.lib.licenses.zpt21 ];
1570 };
1597 };
1571 };
1598 };
1572 wsgiref = super.buildPythonPackage {
1599 wsgiref = super.buildPythonPackage {
1573 name = "wsgiref-0.1.2";
1600 name = "wsgiref-0.1.2";
1574 buildInputs = with self; [];
1601 buildInputs = with self; [];
1575 doCheck = false;
1602 doCheck = false;
1576 propagatedBuildInputs = with self; [];
1603 propagatedBuildInputs = with self; [];
1577 src = fetchurl {
1604 src = fetchurl {
1578 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
1605 url = "https://pypi.python.org/packages/41/9e/309259ce8dff8c596e8c26df86dbc4e848b9249fd36797fd60be456f03fc/wsgiref-0.1.2.zip";
1579 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
1606 md5 = "29b146e6ebd0f9fb119fe321f7bcf6cb";
1580 };
1607 };
1581 meta = {
1608 meta = {
1582 license = [ { fullName = "PSF or ZPL"; } ];
1609 license = [ { fullName = "PSF or ZPL"; } ];
1583 };
1610 };
1584 };
1611 };
1585 zope.cachedescriptors = super.buildPythonPackage {
1612 zope.cachedescriptors = super.buildPythonPackage {
1586 name = "zope.cachedescriptors-4.0.0";
1613 name = "zope.cachedescriptors-4.0.0";
1587 buildInputs = with self; [];
1614 buildInputs = with self; [];
1588 doCheck = false;
1615 doCheck = false;
1589 propagatedBuildInputs = with self; [setuptools];
1616 propagatedBuildInputs = with self; [setuptools];
1590 src = fetchurl {
1617 src = fetchurl {
1591 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
1618 url = "https://pypi.python.org/packages/40/33/694b6644c37f28553f4b9f20b3c3a20fb709a22574dff20b5bdffb09ecd5/zope.cachedescriptors-4.0.0.tar.gz";
1592 md5 = "8d308de8c936792c8e758058fcb7d0f0";
1619 md5 = "8d308de8c936792c8e758058fcb7d0f0";
1593 };
1620 };
1594 meta = {
1621 meta = {
1595 license = [ pkgs.lib.licenses.zpt21 ];
1622 license = [ pkgs.lib.licenses.zpt21 ];
1596 };
1623 };
1597 };
1624 };
1598 zope.deprecation = super.buildPythonPackage {
1625 zope.deprecation = super.buildPythonPackage {
1599 name = "zope.deprecation-4.1.2";
1626 name = "zope.deprecation-4.1.2";
1600 buildInputs = with self; [];
1627 buildInputs = with self; [];
1601 doCheck = false;
1628 doCheck = false;
1602 propagatedBuildInputs = with self; [setuptools];
1629 propagatedBuildInputs = with self; [setuptools];
1603 src = fetchurl {
1630 src = fetchurl {
1604 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
1631 url = "https://pypi.python.org/packages/c1/d3/3919492d5e57d8dd01b36f30b34fc8404a30577392b1eb817c303499ad20/zope.deprecation-4.1.2.tar.gz";
1605 md5 = "e9a663ded58f4f9f7881beb56cae2782";
1632 md5 = "e9a663ded58f4f9f7881beb56cae2782";
1606 };
1633 };
1607 meta = {
1634 meta = {
1608 license = [ pkgs.lib.licenses.zpt21 ];
1635 license = [ pkgs.lib.licenses.zpt21 ];
1609 };
1636 };
1610 };
1637 };
1611 zope.event = super.buildPythonPackage {
1638 zope.event = super.buildPythonPackage {
1612 name = "zope.event-4.0.3";
1639 name = "zope.event-4.0.3";
1613 buildInputs = with self; [];
1640 buildInputs = with self; [];
1614 doCheck = false;
1641 doCheck = false;
1615 propagatedBuildInputs = with self; [setuptools];
1642 propagatedBuildInputs = with self; [setuptools];
1616 src = fetchurl {
1643 src = fetchurl {
1617 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
1644 url = "https://pypi.python.org/packages/c1/29/91ba884d7d6d96691df592e9e9c2bfa57a47040ec1ff47eff18c85137152/zope.event-4.0.3.tar.gz";
1618 md5 = "9a3780916332b18b8b85f522bcc3e249";
1645 md5 = "9a3780916332b18b8b85f522bcc3e249";
1619 };
1646 };
1620 meta = {
1647 meta = {
1621 license = [ pkgs.lib.licenses.zpt21 ];
1648 license = [ pkgs.lib.licenses.zpt21 ];
1622 };
1649 };
1623 };
1650 };
1624 zope.interface = super.buildPythonPackage {
1651 zope.interface = super.buildPythonPackage {
1625 name = "zope.interface-4.1.3";
1652 name = "zope.interface-4.1.3";
1626 buildInputs = with self; [];
1653 buildInputs = with self; [];
1627 doCheck = false;
1654 doCheck = false;
1628 propagatedBuildInputs = with self; [setuptools];
1655 propagatedBuildInputs = with self; [setuptools];
1629 src = fetchurl {
1656 src = fetchurl {
1630 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
1657 url = "https://pypi.python.org/packages/9d/81/2509ca3c6f59080123c1a8a97125eb48414022618cec0e64eb1313727bfe/zope.interface-4.1.3.tar.gz";
1631 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
1658 md5 = "9ae3d24c0c7415deb249dd1a132f0f79";
1632 };
1659 };
1633 meta = {
1660 meta = {
1634 license = [ pkgs.lib.licenses.zpt21 ];
1661 license = [ pkgs.lib.licenses.zpt21 ];
1635 };
1662 };
1636 };
1663 };
1637
1664
1638 ### Test requirements
1665 ### Test requirements
1639
1666
1640
1667
1641 }
1668 }
@@ -1,151 +1,152 b''
1 Babel==1.3
1 Babel==1.3
2 Beaker==1.7.0
2 Beaker==1.7.0
3 CProfileV==1.0.6
3 CProfileV==1.0.6
4 Fabric==1.10.0
4 Fabric==1.10.0
5 FormEncode==1.2.4
5 FormEncode==1.2.4
6 Jinja2==2.7.3
6 Jinja2==2.7.3
7 Mako==1.0.1
7 Mako==1.0.1
8 Markdown==2.6.2
8 Markdown==2.6.2
9 MarkupSafe==0.23
9 MarkupSafe==0.23
10 MySQL-python==1.2.5
10 MySQL-python==1.2.5
11 Paste==2.0.2
11 Paste==2.0.2
12 PasteDeploy==1.5.2
12 PasteDeploy==1.5.2
13 PasteScript==1.7.5
13 PasteScript==1.7.5
14 Pygments==2.1.3
14 Pygments==2.1.3
15
15
16 # TODO: This version is not available on PyPI
16 # TODO: This version is not available on PyPI
17 # Pylons==1.0.2.dev20160108
17 # Pylons==1.0.2.dev20160108
18 Pylons==1.0.1
18 Pylons==1.0.1
19
19
20 # TODO: This version is not available, but newer ones are
20 # TODO: This version is not available, but newer ones are
21 # Pyro4==4.35
21 # Pyro4==4.35
22 Pyro4==4.41
22 Pyro4==4.41
23
23
24 # TODO: This should probably not be in here
24 # TODO: This should probably not be in here
25 # -e hg+https://johbo@code.rhodecode.com/johbo/rhodecode-fork@3a454bd1f17c0b2b2a951cf2b111e0320d7942a9#egg=RhodeCodeEnterprise-dev
25 # -e hg+https://johbo@code.rhodecode.com/johbo/rhodecode-fork@3a454bd1f17c0b2b2a951cf2b111e0320d7942a9#egg=RhodeCodeEnterprise-dev
26
26
27 # TODO: This is not really a dependency, we should add it only
27 # TODO: This is not really a dependency, we should add it only
28 # into the development environment, since there it is useful.
28 # into the development environment, since there it is useful.
29 # RhodeCodeVCSServer==3.9.0
29 # RhodeCodeVCSServer==3.9.0
30
30
31 Routes==1.13
31 Routes==1.13
32 SQLAlchemy==0.9.9
32 SQLAlchemy==0.9.9
33 Sphinx==1.2.2
33 Sphinx==1.2.2
34 Tempita==0.5.2
34 Tempita==0.5.2
35 URLObject==2.4.0
35 URLObject==2.4.0
36 WebError==0.10.3
36 WebError==0.10.3
37
37
38 # TODO: This is modified by us, needs a better integration. For now
38 # TODO: This is modified by us, needs a better integration. For now
39 # using the latest version before.
39 # using the latest version before.
40 # WebHelpers==1.3.dev20150807
40 # WebHelpers==1.3.dev20150807
41 WebHelpers==1.3
41 WebHelpers==1.3
42
42
43 WebHelpers2==2.0
43 WebHelpers2==2.0
44 WebOb==1.3.1
44 WebOb==1.3.1
45 WebTest==1.4.3
45 WebTest==1.4.3
46 Whoosh==2.7.0
46 Whoosh==2.7.0
47 alembic==0.8.4
47 alembic==0.8.4
48 amqplib==1.0.2
48 amqplib==1.0.2
49 anyjson==0.3.3
49 anyjson==0.3.3
50 appenlight-client==0.6.14
50 appenlight-client==0.6.14
51 authomatic==0.1.0.post1;
51 authomatic==0.1.0.post1;
52 backport-ipaddress==0.1
52 backport-ipaddress==0.1
53 bottle==0.12.8
53 bottle==0.12.8
54 bumpversion==0.5.3
54 bumpversion==0.5.3
55 celery==2.2.10
55 celery==2.2.10
56 click==5.1
56 click==5.1
57 colander==1.2
57 colander==1.2
58 configobj==5.0.6
58 configobj==5.0.6
59 cov-core==1.15.0
59 cov-core==1.15.0
60 coverage==3.7.1
60 coverage==3.7.1
61 cssselect==0.9.1
61 cssselect==0.9.1
62 decorator==3.4.2
62 decorator==3.4.2
63 git+https://github.com/Pylons/deform@08fb9de077c76951f6e70e28d4bf060a209d3d39#egg=deform
63 docutils==0.12
64 docutils==0.12
64 dogpile.cache==0.6.1
65 dogpile.cache==0.6.1
65 dogpile.core==0.4.1
66 dogpile.core==0.4.1
66 dulwich==0.12.0
67 dulwich==0.12.0
67 ecdsa==0.11
68 ecdsa==0.11
68 flake8==2.4.1
69 flake8==2.4.1
69 future==0.14.3
70 future==0.14.3
70 futures==3.0.2
71 futures==3.0.2
71 gprof2dot==2015.12.1
72 gprof2dot==2015.12.1
72 gunicorn==19.6.0
73 gunicorn==19.6.0
73
74
74 # TODO: Needs subvertpy and blows up without Subversion headers,
75 # TODO: Needs subvertpy and blows up without Subversion headers,
75 # actually we should not need this for Enterprise at all.
76 # actually we should not need this for Enterprise at all.
76 # hgsubversion==1.8.2
77 # hgsubversion==1.8.2
77
78
78 gnureadline==6.3.3
79 gnureadline==6.3.3
79 infrae.cache==1.0.1
80 infrae.cache==1.0.1
80 invoke==0.13.0
81 invoke==0.13.0
81 ipdb==0.8
82 ipdb==0.8
82 ipython==3.1.0
83 ipython==3.1.0
83 iso8601==0.1.11
84 iso8601==0.1.11
84 itsdangerous==0.24
85 itsdangerous==0.24
85 kombu==1.5.1
86 kombu==1.5.1
86 lxml==3.4.4
87 lxml==3.4.4
87 mccabe==0.3
88 mccabe==0.3
88 meld3==1.0.2
89 meld3==1.0.2
89 mock==1.0.1
90 mock==1.0.1
90 msgpack-python==0.4.6
91 msgpack-python==0.4.6
91 nose==1.3.6
92 nose==1.3.6
92 objgraph==2.0.0
93 objgraph==2.0.0
93 packaging==15.2
94 packaging==15.2
94 paramiko==1.15.1
95 paramiko==1.15.1
95 pep8==1.5.7
96 pep8==1.5.7
96 psutil==2.2.1
97 psutil==2.2.1
97 psycopg2==2.6.1
98 psycopg2==2.6.1
98 py==1.4.29
99 py==1.4.29
99 py-bcrypt==0.4
100 py-bcrypt==0.4
100 py-gfm==0.1.3
101 py-gfm==0.1.3
101 pycrypto==2.6.1
102 pycrypto==2.6.1
102 pycurl==7.19.5
103 pycurl==7.19.5
103 pyflakes==0.8.1
104 pyflakes==0.8.1
104 pyparsing==1.5.7
105 pyparsing==1.5.7
105 pyramid==1.6.1
106 pyramid==1.6.1
106 pyramid-beaker==0.8
107 pyramid-beaker==0.8
107 pyramid-debugtoolbar==2.4.2
108 pyramid-debugtoolbar==2.4.2
108 pyramid-jinja2==2.5
109 pyramid-jinja2==2.5
109 pyramid-mako==1.0.2
110 pyramid-mako==1.0.2
110 pysqlite==2.6.3
111 pysqlite==2.6.3
111 pytest==2.8.5
112 pytest==2.8.5
112 pytest-runner==2.7.1
113 pytest-runner==2.7.1
113 pytest-catchlog==1.2.2
114 pytest-catchlog==1.2.2
114 pytest-cov==1.8.1
115 pytest-cov==1.8.1
115 pytest-profiling==1.0.1
116 pytest-profiling==1.0.1
116 pytest-timeout==0.4
117 pytest-timeout==0.4
117 python-dateutil==1.5
118 python-dateutil==1.5
118 python-ldap==2.4.19
119 python-ldap==2.4.19
119 python-memcached==1.57
120 python-memcached==1.57
120 python-pam==1.8.2
121 python-pam==1.8.2
121 pytz==2015.4
122 pytz==2015.4
122 pyzmq==14.6.0
123 pyzmq==14.6.0
123
124
124 # TODO: This is not available in public
125 # TODO: This is not available in public
125 # rc-testdata==0.2.0
126 # rc-testdata==0.2.0
126
127
127 https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.8.3.zip#md5=9acdfd71b8ddf4056057065f37ab9ccb
128 https://code.rhodecode.com/rhodecode-tools-ce/archive/v0.8.3.zip#md5=9acdfd71b8ddf4056057065f37ab9ccb
128
129
129
130
130 recaptcha-client==1.0.6
131 recaptcha-client==1.0.6
131 repoze.lru==0.6
132 repoze.lru==0.6
132 requests==2.9.1
133 requests==2.9.1
133 serpent==1.12
134 serpent==1.12
134 setproctitle==1.1.8
135 setproctitle==1.1.8
135 setuptools==20.8.1
136 setuptools==20.8.1
136 setuptools-scm==1.11.0
137 setuptools-scm==1.11.0
137 simplejson==3.7.2
138 simplejson==3.7.2
138 six==1.9.0
139 six==1.9.0
139 subprocess32==3.2.6
140 subprocess32==3.2.6
140 supervisor==3.3.0
141 supervisor==3.3.0
141 transifex-client==0.10
142 transifex-client==0.10
142 translationstring==1.3
143 translationstring==1.3
143 trollius==1.0.4
144 trollius==1.0.4
144 uWSGI==2.0.11.2
145 uWSGI==2.0.11.2
145 venusian==1.0
146 venusian==1.0
146 waitress==0.8.9
147 waitress==0.8.9
147 wsgiref==0.1.2
148 wsgiref==0.1.2
148 zope.cachedescriptors==4.0.0
149 zope.cachedescriptors==4.0.0
149 zope.deprecation==4.1.2
150 zope.deprecation==4.1.2
150 zope.event==4.0.3
151 zope.event==4.0.3
151 zope.interface==4.1.3
152 zope.interface==4.1.3
@@ -1,394 +1,395 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Pylons middleware initialization
22 Pylons middleware initialization
23 """
23 """
24 import logging
24 import logging
25
25
26 from paste.registry import RegistryManager
26 from paste.registry import RegistryManager
27 from paste.gzipper import make_gzip_middleware
27 from paste.gzipper import make_gzip_middleware
28 from pylons.wsgiapp import PylonsApp
28 from pylons.wsgiapp import PylonsApp
29 from pyramid.authorization import ACLAuthorizationPolicy
29 from pyramid.authorization import ACLAuthorizationPolicy
30 from pyramid.config import Configurator
30 from pyramid.config import Configurator
31 from pyramid.static import static_view
31 from pyramid.static import static_view
32 from pyramid.settings import asbool, aslist
32 from pyramid.settings import asbool, aslist
33 from pyramid.wsgi import wsgiapp
33 from pyramid.wsgi import wsgiapp
34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
34 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 import pyramid.httpexceptions as httpexceptions
36 import pyramid.httpexceptions as httpexceptions
37 from pyramid.renderers import render_to_response, render
37 from pyramid.renderers import render_to_response, render
38 from routes.middleware import RoutesMiddleware
38 from routes.middleware import RoutesMiddleware
39 import routes.util
39 import routes.util
40
40
41 import rhodecode
41 import rhodecode
42 import rhodecode.integrations # do not remove this as it registers celery tasks
42 import rhodecode.integrations # do not remove this as it registers celery tasks
43 from rhodecode.config import patches
43 from rhodecode.config import patches
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
44 from rhodecode.config.routing import STATIC_FILE_PREFIX
45 from rhodecode.config.environment import (
45 from rhodecode.config.environment import (
46 load_environment, load_pyramid_environment)
46 load_environment, load_pyramid_environment)
47 from rhodecode.lib.middleware import csrf
47 from rhodecode.lib.middleware import csrf
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
48 from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled
49 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
49 from rhodecode.lib.middleware.disable_vcs import DisableVCSPagesWrapper
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
50 from rhodecode.lib.middleware.https_fixup import HttpsFixup
51 from rhodecode.lib.middleware.vcs import VCSMiddleware
51 from rhodecode.lib.middleware.vcs import VCSMiddleware
52 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
52 from rhodecode.lib.plugins.utils import register_rhodecode_plugin
53
53
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 # this is used to avoid avoid the route lookup overhead in routesmiddleware
58 # this is used to avoid avoid the route lookup overhead in routesmiddleware
59 # for certain routes which won't go to pylons to - eg. static files, debugger
59 # for certain routes which won't go to pylons to - eg. static files, debugger
60 # it is only needed for the pylons migration and can be removed once complete
60 # it is only needed for the pylons migration and can be removed once complete
61 class SkippableRoutesMiddleware(RoutesMiddleware):
61 class SkippableRoutesMiddleware(RoutesMiddleware):
62 """ Routes middleware that allows you to skip prefixes """
62 """ Routes middleware that allows you to skip prefixes """
63
63
64 def __init__(self, *args, **kw):
64 def __init__(self, *args, **kw):
65 self.skip_prefixes = kw.pop('skip_prefixes', [])
65 self.skip_prefixes = kw.pop('skip_prefixes', [])
66 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
66 super(SkippableRoutesMiddleware, self).__init__(*args, **kw)
67
67
68 def __call__(self, environ, start_response):
68 def __call__(self, environ, start_response):
69 for prefix in self.skip_prefixes:
69 for prefix in self.skip_prefixes:
70 if environ['PATH_INFO'].startswith(prefix):
70 if environ['PATH_INFO'].startswith(prefix):
71 return self.app(environ, start_response)
71 return self.app(environ, start_response)
72
72
73 return super(SkippableRoutesMiddleware, self).__call__(
73 return super(SkippableRoutesMiddleware, self).__call__(
74 environ, start_response)
74 environ, start_response)
75
75
76
76
77 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
77 def make_app(global_conf, full_stack=True, static_files=True, **app_conf):
78 """Create a Pylons WSGI application and return it
78 """Create a Pylons WSGI application and return it
79
79
80 ``global_conf``
80 ``global_conf``
81 The inherited configuration for this application. Normally from
81 The inherited configuration for this application. Normally from
82 the [DEFAULT] section of the Paste ini file.
82 the [DEFAULT] section of the Paste ini file.
83
83
84 ``full_stack``
84 ``full_stack``
85 Whether or not this application provides a full WSGI stack (by
85 Whether or not this application provides a full WSGI stack (by
86 default, meaning it handles its own exceptions and errors).
86 default, meaning it handles its own exceptions and errors).
87 Disable full_stack when this application is "managed" by
87 Disable full_stack when this application is "managed" by
88 another WSGI middleware.
88 another WSGI middleware.
89
89
90 ``app_conf``
90 ``app_conf``
91 The application's local configuration. Normally specified in
91 The application's local configuration. Normally specified in
92 the [app:<name>] section of the Paste ini file (where <name>
92 the [app:<name>] section of the Paste ini file (where <name>
93 defaults to main).
93 defaults to main).
94
94
95 """
95 """
96 # Apply compatibility patches
96 # Apply compatibility patches
97 patches.kombu_1_5_1_python_2_7_11()
97 patches.kombu_1_5_1_python_2_7_11()
98 patches.inspect_getargspec()
98 patches.inspect_getargspec()
99
99
100 # Configure the Pylons environment
100 # Configure the Pylons environment
101 config = load_environment(global_conf, app_conf)
101 config = load_environment(global_conf, app_conf)
102
102
103 # The Pylons WSGI app
103 # The Pylons WSGI app
104 app = PylonsApp(config=config)
104 app = PylonsApp(config=config)
105 if rhodecode.is_test:
105 if rhodecode.is_test:
106 app = csrf.CSRFDetector(app)
106 app = csrf.CSRFDetector(app)
107
107
108 expected_origin = config.get('expected_origin')
108 expected_origin = config.get('expected_origin')
109 if expected_origin:
109 if expected_origin:
110 # The API can be accessed from other Origins.
110 # The API can be accessed from other Origins.
111 app = csrf.OriginChecker(app, expected_origin,
111 app = csrf.OriginChecker(app, expected_origin,
112 skip_urls=[routes.util.url_for('api')])
112 skip_urls=[routes.util.url_for('api')])
113
113
114
114
115 if asbool(full_stack):
115 if asbool(full_stack):
116
116
117 # Appenlight monitoring and error handler
117 # Appenlight monitoring and error handler
118 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
118 app, appenlight_client = wrap_in_appenlight_if_enabled(app, config)
119
119
120 # we want our low level middleware to get to the request ASAP. We don't
120 # we want our low level middleware to get to the request ASAP. We don't
121 # need any pylons stack middleware in them
121 # need any pylons stack middleware in them
122 app = VCSMiddleware(app, config, appenlight_client)
122 app = VCSMiddleware(app, config, appenlight_client)
123
123
124 # Establish the Registry for this application
124 # Establish the Registry for this application
125 app = RegistryManager(app)
125 app = RegistryManager(app)
126
126
127 app.config = config
127 app.config = config
128
128
129 return app
129 return app
130
130
131
131
132 def make_pyramid_app(global_config, **settings):
132 def make_pyramid_app(global_config, **settings):
133 """
133 """
134 Constructs the WSGI application based on Pyramid and wraps the Pylons based
134 Constructs the WSGI application based on Pyramid and wraps the Pylons based
135 application.
135 application.
136
136
137 Specials:
137 Specials:
138
138
139 * We migrate from Pylons to Pyramid. While doing this, we keep both
139 * We migrate from Pylons to Pyramid. While doing this, we keep both
140 frameworks functional. This involves moving some WSGI middlewares around
140 frameworks functional. This involves moving some WSGI middlewares around
141 and providing access to some data internals, so that the old code is
141 and providing access to some data internals, so that the old code is
142 still functional.
142 still functional.
143
143
144 * The application can also be integrated like a plugin via the call to
144 * The application can also be integrated like a plugin via the call to
145 `includeme`. This is accompanied with the other utility functions which
145 `includeme`. This is accompanied with the other utility functions which
146 are called. Changing this should be done with great care to not break
146 are called. Changing this should be done with great care to not break
147 cases when these fragments are assembled from another place.
147 cases when these fragments are assembled from another place.
148
148
149 """
149 """
150 # The edition string should be available in pylons too, so we add it here
150 # The edition string should be available in pylons too, so we add it here
151 # before copying the settings.
151 # before copying the settings.
152 settings.setdefault('rhodecode.edition', 'Community Edition')
152 settings.setdefault('rhodecode.edition', 'Community Edition')
153
153
154 # As long as our Pylons application does expect "unprepared" settings, make
154 # As long as our Pylons application does expect "unprepared" settings, make
155 # sure that we keep an unmodified copy. This avoids unintentional change of
155 # sure that we keep an unmodified copy. This avoids unintentional change of
156 # behavior in the old application.
156 # behavior in the old application.
157 settings_pylons = settings.copy()
157 settings_pylons = settings.copy()
158
158
159 sanitize_settings_and_apply_defaults(settings)
159 sanitize_settings_and_apply_defaults(settings)
160 config = Configurator(settings=settings)
160 config = Configurator(settings=settings)
161 add_pylons_compat_data(config.registry, global_config, settings_pylons)
161 add_pylons_compat_data(config.registry, global_config, settings_pylons)
162
162
163 load_pyramid_environment(global_config, settings)
163 load_pyramid_environment(global_config, settings)
164
164
165 includeme_first(config)
165 includeme_first(config)
166 includeme(config)
166 includeme(config)
167 pyramid_app = config.make_wsgi_app()
167 pyramid_app = config.make_wsgi_app()
168 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
168 pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config)
169 return pyramid_app
169 return pyramid_app
170
170
171
171
172 def add_pylons_compat_data(registry, global_config, settings):
172 def add_pylons_compat_data(registry, global_config, settings):
173 """
173 """
174 Attach data to the registry to support the Pylons integration.
174 Attach data to the registry to support the Pylons integration.
175 """
175 """
176 registry._pylons_compat_global_config = global_config
176 registry._pylons_compat_global_config = global_config
177 registry._pylons_compat_settings = settings
177 registry._pylons_compat_settings = settings
178
178
179
179
180 def webob_to_pyramid_http_response(webob_response):
180 def webob_to_pyramid_http_response(webob_response):
181 ResponseClass = httpexceptions.status_map[webob_response.status_int]
181 ResponseClass = httpexceptions.status_map[webob_response.status_int]
182 pyramid_response = ResponseClass(webob_response.status)
182 pyramid_response = ResponseClass(webob_response.status)
183 pyramid_response.status = webob_response.status
183 pyramid_response.status = webob_response.status
184 pyramid_response.headers.update(webob_response.headers)
184 pyramid_response.headers.update(webob_response.headers)
185 if pyramid_response.headers['content-type'] == 'text/html':
185 if pyramid_response.headers['content-type'] == 'text/html':
186 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
186 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
187 return pyramid_response
187 return pyramid_response
188
188
189
189
190 def error_handler(exception, request):
190 def error_handler(exception, request):
191 # TODO: dan: replace the old pylons error controller with this
191 # TODO: dan: replace the old pylons error controller with this
192 from rhodecode.model.settings import SettingsModel
192 from rhodecode.model.settings import SettingsModel
193 from rhodecode.lib.utils2 import AttributeDict
193 from rhodecode.lib.utils2 import AttributeDict
194
194
195 try:
195 try:
196 rc_config = SettingsModel().get_all_settings()
196 rc_config = SettingsModel().get_all_settings()
197 except Exception:
197 except Exception:
198 log.exception('failed to fetch settings')
198 log.exception('failed to fetch settings')
199 rc_config = {}
199 rc_config = {}
200
200
201 base_response = HTTPInternalServerError()
201 base_response = HTTPInternalServerError()
202 # prefer original exception for the response since it may have headers set
202 # prefer original exception for the response since it may have headers set
203 if isinstance(exception, HTTPError):
203 if isinstance(exception, HTTPError):
204 base_response = exception
204 base_response = exception
205
205
206 c = AttributeDict()
206 c = AttributeDict()
207 c.error_message = base_response.status
207 c.error_message = base_response.status
208 c.error_explanation = base_response.explanation or str(base_response)
208 c.error_explanation = base_response.explanation or str(base_response)
209 c.visual = AttributeDict()
209 c.visual = AttributeDict()
210
210
211 c.visual.rhodecode_support_url = (
211 c.visual.rhodecode_support_url = (
212 request.registry.settings.get('rhodecode_support_url') or
212 request.registry.settings.get('rhodecode_support_url') or
213 request.route_url('rhodecode_support')
213 request.route_url('rhodecode_support')
214 )
214 )
215 c.redirect_time = 0
215 c.redirect_time = 0
216 c.rhodecode_name = rc_config.get('rhodecode_title', '')
216 c.rhodecode_name = rc_config.get('rhodecode_title', '')
217 if not c.rhodecode_name:
217 if not c.rhodecode_name:
218 c.rhodecode_name = 'Rhodecode'
218 c.rhodecode_name = 'Rhodecode'
219
219
220 response = render_to_response(
220 response = render_to_response(
221 '/errors/error_document.html', {'c': c}, request=request,
221 '/errors/error_document.html', {'c': c}, request=request,
222 response=base_response)
222 response=base_response)
223
223
224 return response
224 return response
225
225
226
226
227 def includeme(config):
227 def includeme(config):
228 settings = config.registry.settings
228 settings = config.registry.settings
229
229
230 # plugin information
230 # plugin information
231 config.registry.rhodecode_plugins = {}
231 config.registry.rhodecode_plugins = {}
232
232
233 config.add_directive(
233 config.add_directive(
234 'register_rhodecode_plugin', register_rhodecode_plugin)
234 'register_rhodecode_plugin', register_rhodecode_plugin)
235
235
236 if asbool(settings.get('appenlight', 'false')):
236 if asbool(settings.get('appenlight', 'false')):
237 config.include('appenlight_client.ext.pyramid_tween')
237 config.include('appenlight_client.ext.pyramid_tween')
238
238
239 # Includes which are required. The application would fail without them.
239 # Includes which are required. The application would fail without them.
240 config.include('pyramid_mako')
240 config.include('pyramid_mako')
241 config.include('pyramid_beaker')
241 config.include('pyramid_beaker')
242 config.include('rhodecode.admin')
242 config.include('rhodecode.admin')
243 config.include('rhodecode.authentication')
243 config.include('rhodecode.authentication')
244 config.include('rhodecode.integrations')
244 config.include('rhodecode.integrations')
245 config.include('rhodecode.login')
245 config.include('rhodecode.login')
246 config.include('rhodecode.tweens')
246 config.include('rhodecode.tweens')
247 config.include('rhodecode.api')
247 config.include('rhodecode.api')
248 config.add_route(
248 config.add_route(
249 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
249 'rhodecode_support', 'https://rhodecode.com/help/', static=True)
250
250
251 # Set the authorization policy.
251 # Set the authorization policy.
252 authz_policy = ACLAuthorizationPolicy()
252 authz_policy = ACLAuthorizationPolicy()
253 config.set_authorization_policy(authz_policy)
253 config.set_authorization_policy(authz_policy)
254
254
255 # Set the default renderer for HTML templates to mako.
255 # Set the default renderer for HTML templates to mako.
256 config.add_mako_renderer('.html')
256 config.add_mako_renderer('.html')
257
257
258 # include RhodeCode plugins
258 # include RhodeCode plugins
259 includes = aslist(settings.get('rhodecode.includes', []))
259 includes = aslist(settings.get('rhodecode.includes', []))
260 for inc in includes:
260 for inc in includes:
261 config.include(inc)
261 config.include(inc)
262
262
263 pylons_app = make_app(
263 pylons_app = make_app(
264 config.registry._pylons_compat_global_config,
264 config.registry._pylons_compat_global_config,
265 **config.registry._pylons_compat_settings)
265 **config.registry._pylons_compat_settings)
266 config.registry._pylons_compat_config = pylons_app.config
266 config.registry._pylons_compat_config = pylons_app.config
267
267
268 pylons_app_as_view = wsgiapp(pylons_app)
268 pylons_app_as_view = wsgiapp(pylons_app)
269
269
270 # Protect from VCS Server error related pages when server is not available
270 # Protect from VCS Server error related pages when server is not available
271 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
271 vcs_server_enabled = asbool(settings.get('vcs.server.enable', 'true'))
272 if not vcs_server_enabled:
272 if not vcs_server_enabled:
273 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
273 pylons_app_as_view = DisableVCSPagesWrapper(pylons_app_as_view)
274
274
275
275
276 def pylons_app_with_error_handler(context, request):
276 def pylons_app_with_error_handler(context, request):
277 """
277 """
278 Handle exceptions from rc pylons app:
278 Handle exceptions from rc pylons app:
279
279
280 - old webob type exceptions get converted to pyramid exceptions
280 - old webob type exceptions get converted to pyramid exceptions
281 - pyramid exceptions are passed to the error handler view
281 - pyramid exceptions are passed to the error handler view
282 """
282 """
283 try:
283 try:
284 response = pylons_app_as_view(context, request)
284 response = pylons_app_as_view(context, request)
285 if 400 <= response.status_int <= 599: # webob type error responses
285 if 400 <= response.status_int <= 599: # webob type error responses
286 return error_handler(
286 return error_handler(
287 webob_to_pyramid_http_response(response), request)
287 webob_to_pyramid_http_response(response), request)
288 except HTTPError as e: # pyramid type exceptions
288 except HTTPError as e: # pyramid type exceptions
289 return error_handler(e, request)
289 return error_handler(e, request)
290 except Exception:
290 except Exception:
291 if settings.get('debugtoolbar.enabled', False):
291 if settings.get('debugtoolbar.enabled', False):
292 raise
292 raise
293 return error_handler(HTTPInternalServerError(), request)
293 return error_handler(HTTPInternalServerError(), request)
294 return response
294 return response
295
295
296 # This is the glue which allows us to migrate in chunks. By registering the
296 # This is the glue which allows us to migrate in chunks. By registering the
297 # pylons based application as the "Not Found" view in Pyramid, we will
297 # pylons based application as the "Not Found" view in Pyramid, we will
298 # fallback to the old application each time the new one does not yet know
298 # fallback to the old application each time the new one does not yet know
299 # how to handle a request.
299 # how to handle a request.
300 config.add_notfound_view(pylons_app_with_error_handler)
300 config.add_notfound_view(pylons_app_with_error_handler)
301
301
302 if not settings.get('debugtoolbar.enabled', False):
302 if not settings.get('debugtoolbar.enabled', False):
303 # if no toolbar, then any exception gets caught and rendered
303 # if no toolbar, then any exception gets caught and rendered
304 config.add_view(error_handler, context=Exception)
304 config.add_view(error_handler, context=Exception)
305
305
306 config.add_view(error_handler, context=HTTPError)
306 config.add_view(error_handler, context=HTTPError)
307
307
308
308
309 def includeme_first(config):
309 def includeme_first(config):
310 # redirect automatic browser favicon.ico requests to correct place
310 # redirect automatic browser favicon.ico requests to correct place
311 def favicon_redirect(context, request):
311 def favicon_redirect(context, request):
312 return redirect(
312 return redirect(
313 request.static_url('rhodecode:public/images/favicon.ico'))
313 request.static_url('rhodecode:public/images/favicon.ico'))
314
314
315 config.add_view(favicon_redirect, route_name='favicon')
315 config.add_view(favicon_redirect, route_name='favicon')
316 config.add_route('favicon', '/favicon.ico')
316 config.add_route('favicon', '/favicon.ico')
317
317
318 config.add_static_view(
318 config.add_static_view(
319 '_static/deform', 'deform:static')
320 config.add_static_view(
319 '_static', path='rhodecode:public', cache_max_age=3600 * 24)
321 '_static', path='rhodecode:public', cache_max_age=3600 * 24)
320
322
321
322 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
323 def wrap_app_in_wsgi_middlewares(pyramid_app, config):
323 """
324 """
324 Apply outer WSGI middlewares around the application.
325 Apply outer WSGI middlewares around the application.
325
326
326 Part of this has been moved up from the Pylons layer, so that the
327 Part of this has been moved up from the Pylons layer, so that the
327 data is also available if old Pylons code is hit through an already ported
328 data is also available if old Pylons code is hit through an already ported
328 view.
329 view.
329 """
330 """
330 settings = config.registry.settings
331 settings = config.registry.settings
331
332
332 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
333 # enable https redirects based on HTTP_X_URL_SCHEME set by proxy
333 pyramid_app = HttpsFixup(pyramid_app, settings)
334 pyramid_app = HttpsFixup(pyramid_app, settings)
334
335
335 # Add RoutesMiddleware to support the pylons compatibility tween during
336 # Add RoutesMiddleware to support the pylons compatibility tween during
336 # migration to pyramid.
337 # migration to pyramid.
337 pyramid_app = SkippableRoutesMiddleware(
338 pyramid_app = SkippableRoutesMiddleware(
338 pyramid_app, config.registry._pylons_compat_config['routes.map'],
339 pyramid_app, config.registry._pylons_compat_config['routes.map'],
339 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
340 skip_prefixes=(STATIC_FILE_PREFIX, '/_debug_toolbar'))
340
341
341 if asbool(settings.get('appenlight', 'false')):
342 if asbool(settings.get('appenlight', 'false')):
342 pyramid_app, _ = wrap_in_appenlight_if_enabled(
343 pyramid_app, _ = wrap_in_appenlight_if_enabled(
343 pyramid_app, config.registry._pylons_compat_config)
344 pyramid_app, config.registry._pylons_compat_config)
344
345
345 if asbool(settings.get('gzip_responses', 'true')):
346 if asbool(settings.get('gzip_responses', 'true')):
346 pyramid_app = make_gzip_middleware(
347 pyramid_app = make_gzip_middleware(
347 pyramid_app, settings, compress_level=1)
348 pyramid_app, settings, compress_level=1)
348
349
349 return pyramid_app
350 return pyramid_app
350
351
351
352
352 def sanitize_settings_and_apply_defaults(settings):
353 def sanitize_settings_and_apply_defaults(settings):
353 """
354 """
354 Applies settings defaults and does all type conversion.
355 Applies settings defaults and does all type conversion.
355
356
356 We would move all settings parsing and preparation into this place, so that
357 We would move all settings parsing and preparation into this place, so that
357 we have only one place left which deals with this part. The remaining parts
358 we have only one place left which deals with this part. The remaining parts
358 of the application would start to rely fully on well prepared settings.
359 of the application would start to rely fully on well prepared settings.
359
360
360 This piece would later be split up per topic to avoid a big fat monster
361 This piece would later be split up per topic to avoid a big fat monster
361 function.
362 function.
362 """
363 """
363
364
364 # Pyramid's mako renderer has to search in the templates folder so that the
365 # Pyramid's mako renderer has to search in the templates folder so that the
365 # old templates still work. Ported and new templates are expected to use
366 # old templates still work. Ported and new templates are expected to use
366 # real asset specifications for the includes.
367 # real asset specifications for the includes.
367 mako_directories = settings.setdefault('mako.directories', [
368 mako_directories = settings.setdefault('mako.directories', [
368 # Base templates of the original Pylons application
369 # Base templates of the original Pylons application
369 'rhodecode:templates',
370 'rhodecode:templates',
370 ])
371 ])
371 log.debug(
372 log.debug(
372 "Using the following Mako template directories: %s",
373 "Using the following Mako template directories: %s",
373 mako_directories)
374 mako_directories)
374
375
375 # Default includes, possible to change as a user
376 # Default includes, possible to change as a user
376 pyramid_includes = settings.setdefault('pyramid.includes', [
377 pyramid_includes = settings.setdefault('pyramid.includes', [
377 'rhodecode.lib.middleware.request_wrapper',
378 'rhodecode.lib.middleware.request_wrapper',
378 ])
379 ])
379 log.debug(
380 log.debug(
380 "Using the following pyramid.includes: %s",
381 "Using the following pyramid.includes: %s",
381 pyramid_includes)
382 pyramid_includes)
382
383
383 # TODO: johbo: Re-think this, usually the call to config.include
384 # TODO: johbo: Re-think this, usually the call to config.include
384 # should allow to pass in a prefix.
385 # should allow to pass in a prefix.
385 settings.setdefault('rhodecode.api.url', '/_admin/api')
386 settings.setdefault('rhodecode.api.url', '/_admin/api')
386
387
387 _bool_setting(settings, 'vcs.server.enable', 'true')
388 _bool_setting(settings, 'vcs.server.enable', 'true')
388 _bool_setting(settings, 'is_test', 'false')
389 _bool_setting(settings, 'is_test', 'false')
389
390
390 return settings
391 return settings
391
392
392
393
393 def _bool_setting(settings, name, default):
394 def _bool_setting(settings, name, default):
394 settings[name] = asbool(settings.get(name, default))
395 settings[name] = asbool(settings.get(name, default))
@@ -1,57 +1,58 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22 from rhodecode.integrations.registry import IntegrationTypeRegistry
23 from rhodecode.integrations.registry import IntegrationTypeRegistry
23 from rhodecode.integrations.types import webhook, slack
24 from rhodecode.integrations.types import webhook, slack
24
25
25 log = logging.getLogger(__name__)
26 log = logging.getLogger(__name__)
26
27
27
28
28 # TODO: dan: This is currently global until we figure out what to do about
29 # TODO: dan: This is currently global until we figure out what to do about
29 # VCS's not having a pyramid context - move it to pyramid app configuration
30 # VCS's not having a pyramid context - move it to pyramid app configuration
30 # includeme level later to allow per instance integration setup
31 # includeme level later to allow per instance integration setup
31 integration_type_registry = IntegrationTypeRegistry()
32 integration_type_registry = IntegrationTypeRegistry()
32
33
33 integration_type_registry.register_integration_type(
34 integration_type_registry.register_integration_type(
34 webhook.WebhookIntegrationType)
35 webhook.WebhookIntegrationType)
35 integration_type_registry.register_integration_type(
36 integration_type_registry.register_integration_type(
36 slack.SlackIntegrationType)
37 slack.SlackIntegrationType)
37
38
38
39
39 def integrations_event_handler(event):
40 def integrations_event_handler(event):
40 """
41 """
41 Takes an event and passes it to all enabled integrations
42 Takes an event and passes it to all enabled integrations
42 """
43 """
43 from rhodecode.model.integration import IntegrationModel
44 from rhodecode.model.integration import IntegrationModel
44
45
45 integration_model = IntegrationModel()
46 integration_model = IntegrationModel()
46 integrations = integration_model.get_for_event(event)
47 integrations = integration_model.get_for_event(event)
47 for integration in integrations:
48 for integration in integrations:
48 try:
49 try:
49 integration_model.send_event(integration, event)
50 integration_model.send_event(integration, event)
50 except Exception:
51 except Exception:
51 log.exception(
52 log.exception(
52 'failure occured when sending event %s to integration %s' % (
53 'failure occured when sending event %s to integration %s' % (
53 event, integration))
54 event, integration))
54
55
55
56
56 def includeme(config):
57 def includeme(config):
57 config.include('rhodecode.integrations.routes')
58 config.include('rhodecode.integrations.routes')
@@ -1,48 +1,45 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import colander
21 import colander
22
22
23 from rhodecode.translation import lazy_ugettext
23 from rhodecode.translation import lazy_ugettext
24
24
25
25
26 class IntegrationSettingsSchemaBase(colander.MappingSchema):
26 class IntegrationSettingsSchemaBase(colander.MappingSchema):
27 """
27 """
28 This base schema is intended for use in integrations.
28 This base schema is intended for use in integrations.
29 It adds a few default settings (e.g., "enabled"), so that integration
29 It adds a few default settings (e.g., "enabled"), so that integration
30 authors don't have to maintain a bunch of boilerplate.
30 authors don't have to maintain a bunch of boilerplate.
31 """
31 """
32 enabled = colander.SchemaNode(
32 enabled = colander.SchemaNode(
33 colander.Bool(),
33 colander.Bool(),
34 default=True,
34 default=True,
35 description=lazy_ugettext('Enable or disable this integration.'),
35 description=lazy_ugettext('Enable or disable this integration.'),
36 missing=False,
36 missing=False,
37 title=lazy_ugettext('Enabled'),
37 title=lazy_ugettext('Enabled'),
38 widget='bool',
39 )
38 )
40
39
41 name = colander.SchemaNode(
40 name = colander.SchemaNode(
42 colander.String(),
41 colander.String(),
43 description=lazy_ugettext('Short name for this integration.'),
42 description=lazy_ugettext('Short name for this integration.'),
44 missing=colander.required,
43 missing=colander.required,
45 title=lazy_ugettext('Integration name'),
44 title=lazy_ugettext('Integration name'),
46 widget='string',
47 )
45 )
48
@@ -1,43 +1,42 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
21 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
22
22
23
23
24 class IntegrationTypeBase(object):
24 class IntegrationTypeBase(object):
25 """ Base class for IntegrationType plugins """
25 """ Base class for IntegrationType plugins """
26
26
27 def __init__(self, settings):
27 def __init__(self, settings):
28 """
28 """
29 :param settings: dict of settings to be used for the integration
29 :param settings: dict of settings to be used for the integration
30 """
30 """
31 self.settings = settings
31 self.settings = settings
32
32
33
33
34 @classmethod
34 def settings_schema(self):
35 def settings_schema(cls):
36 """
35 """
37 A colander schema of settings for the integration type
36 A colander schema of settings for the integration type
38
37
39 Subclasses can return their own schema but should always
38 Subclasses can return their own schema but should always
40 inherit from IntegrationSettingsSchemaBase
39 inherit from IntegrationSettingsSchemaBase
41 """
40 """
42 return IntegrationSettingsSchemaBase()
41 return IntegrationSettingsSchemaBase()
43
42
@@ -1,246 +1,253 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 from __future__ import unicode_literals
21 from __future__ import unicode_literals
22
22 import deform
23 import re
23 import re
24 import logging
24 import logging
25 import requests
25 import requests
26 import colander
26 import colander
27 import textwrap
27 import textwrap
28 from celery.task import task
28 from celery.task import task
29 from mako.template import Template
29 from mako.template import Template
30
30
31 from rhodecode import events
31 from rhodecode import events
32 from rhodecode.translation import lazy_ugettext
32 from rhodecode.translation import lazy_ugettext
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib.celerylib import run_task
34 from rhodecode.lib.celerylib import run_task
35 from rhodecode.lib.colander_utils import strip_whitespace
35 from rhodecode.lib.colander_utils import strip_whitespace
36 from rhodecode.integrations.types.base import IntegrationTypeBase
36 from rhodecode.integrations.types.base import IntegrationTypeBase
37 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
37 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
38
38
39 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
40
40
41
41
42 class SlackSettingsSchema(IntegrationSettingsSchemaBase):
42 class SlackSettingsSchema(IntegrationSettingsSchemaBase):
43 service = colander.SchemaNode(
43 service = colander.SchemaNode(
44 colander.String(),
44 colander.String(),
45 title=lazy_ugettext('Slack service URL'),
45 title=lazy_ugettext('Slack service URL'),
46 description=h.literal(lazy_ugettext(
46 description=h.literal(lazy_ugettext(
47 'This can be setup at the '
47 'This can be setup at the '
48 '<a href="https://my.slack.com/services/new/incoming-webhook/">'
48 '<a href="https://my.slack.com/services/new/incoming-webhook/">'
49 'slack app manager</a>')),
49 'slack app manager</a>')),
50 default='',
50 default='',
51 placeholder='https://hooks.slack.com/services/...',
52 preparer=strip_whitespace,
51 preparer=strip_whitespace,
53 validator=colander.url,
52 validator=colander.url,
54 widget='string'
53 widget=deform.widget.TextInputWidget(
54 placeholder='https://hooks.slack.com/services/...',
55 ),
55 )
56 )
56 username = colander.SchemaNode(
57 username = colander.SchemaNode(
57 colander.String(),
58 colander.String(),
58 title=lazy_ugettext('Username'),
59 title=lazy_ugettext('Username'),
59 description=lazy_ugettext('Username to show notifications coming from.'),
60 description=lazy_ugettext('Username to show notifications coming from.'),
60 missing='Rhodecode',
61 missing='Rhodecode',
61 preparer=strip_whitespace,
62 preparer=strip_whitespace,
62 widget='string',
63 widget=deform.widget.TextInputWidget(
63 placeholder='Rhodecode'
64 placeholder='Rhodecode'
65 ),
64 )
66 )
65 channel = colander.SchemaNode(
67 channel = colander.SchemaNode(
66 colander.String(),
68 colander.String(),
67 title=lazy_ugettext('Channel'),
69 title=lazy_ugettext('Channel'),
68 description=lazy_ugettext('Channel to send notifications to.'),
70 description=lazy_ugettext('Channel to send notifications to.'),
69 missing='',
71 missing='',
70 preparer=strip_whitespace,
72 preparer=strip_whitespace,
71 widget='string',
73 widget=deform.widget.TextInputWidget(
72 placeholder='#general'
74 placeholder='#general'
75 ),
73 )
76 )
74 icon_emoji = colander.SchemaNode(
77 icon_emoji = colander.SchemaNode(
75 colander.String(),
78 colander.String(),
76 title=lazy_ugettext('Emoji'),
79 title=lazy_ugettext('Emoji'),
77 description=lazy_ugettext('Emoji to use eg. :studio_microphone:'),
80 description=lazy_ugettext('Emoji to use eg. :studio_microphone:'),
78 missing='',
81 missing='',
79 preparer=strip_whitespace,
82 preparer=strip_whitespace,
80 widget='string',
83 widget=deform.widget.TextInputWidget(
81 placeholder=':studio_microphone:'
84 placeholder=':studio_microphone:'
85 ),
82 )
86 )
83
87
84
88
85 repo_push_template = Template(r'''
89 repo_push_template = Template(r'''
86 *${data['actor']['username']}* pushed to \
90 *${data['actor']['username']}* pushed to \
87 %if data['push']['branches']:
91 %if data['push']['branches']:
88 ${len(data['push']['branches']) > 1 and 'branches' or 'branch'} \
92 ${len(data['push']['branches']) > 1 and 'branches' or 'branch'} \
89 ${', '.join('<%s|%s>' % (branch['url'], branch['name']) for branch in data['push']['branches'])} \
93 ${', '.join('<%s|%s>' % (branch['url'], branch['name']) for branch in data['push']['branches'])} \
90 %else:
94 %else:
91 unknown branch \
95 unknown branch \
92 %endif
96 %endif
93 in <${data['repo']['url']}|${data['repo']['repo_name']}>
97 in <${data['repo']['url']}|${data['repo']['repo_name']}>
94 >>>
98 >>>
95 %for commit in data['push']['commits']:
99 %for commit in data['push']['commits']:
96 <${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
100 <${commit['url']}|${commit['short_id']}> - ${commit['message_html']|html_to_slack_links}
97 %endfor
101 %endfor
98 ''')
102 ''')
99
103
100
104
101 class SlackIntegrationType(IntegrationTypeBase):
105 class SlackIntegrationType(IntegrationTypeBase):
102 key = 'slack'
106 key = 'slack'
103 display_name = lazy_ugettext('Slack')
107 display_name = lazy_ugettext('Slack')
104 SettingsSchema = SlackSettingsSchema
108 SettingsSchema = SlackSettingsSchema
105 valid_events = [
109 valid_events = [
106 events.PullRequestCloseEvent,
110 events.PullRequestCloseEvent,
107 events.PullRequestMergeEvent,
111 events.PullRequestMergeEvent,
108 events.PullRequestUpdateEvent,
112 events.PullRequestUpdateEvent,
109 events.PullRequestCommentEvent,
113 events.PullRequestCommentEvent,
110 events.PullRequestReviewEvent,
114 events.PullRequestReviewEvent,
111 events.PullRequestCreateEvent,
115 events.PullRequestCreateEvent,
112 events.RepoPushEvent,
116 events.RepoPushEvent,
113 events.RepoCreateEvent,
117 events.RepoCreateEvent,
114 ]
118 ]
115
119
116 def send_event(self, event):
120 def send_event(self, event):
117 if event.__class__ not in self.valid_events:
121 if event.__class__ not in self.valid_events:
118 log.debug('event not valid: %r' % event)
122 log.debug('event not valid: %r' % event)
119 return
123 return
120
124
121 if event.name not in self.settings['events']:
125 if event.name not in self.settings['events']:
122 log.debug('event ignored: %r' % event)
126 log.debug('event ignored: %r' % event)
123 return
127 return
124
128
125 data = event.as_dict()
129 data = event.as_dict()
126
130
127 text = '*%s* caused a *%s* event' % (
131 text = '*%s* caused a *%s* event' % (
128 data['actor']['username'], event.name)
132 data['actor']['username'], event.name)
129
133
130 log.debug('handling slack event for %s' % event.name)
134 log.debug('handling slack event for %s' % event.name)
131
135
132 if isinstance(event, events.PullRequestCommentEvent):
136 if isinstance(event, events.PullRequestCommentEvent):
133 text = self.format_pull_request_comment_event(event, data)
137 text = self.format_pull_request_comment_event(event, data)
134 elif isinstance(event, events.PullRequestReviewEvent):
138 elif isinstance(event, events.PullRequestReviewEvent):
135 text = self.format_pull_request_review_event(event, data)
139 text = self.format_pull_request_review_event(event, data)
136 elif isinstance(event, events.PullRequestEvent):
140 elif isinstance(event, events.PullRequestEvent):
137 text = self.format_pull_request_event(event, data)
141 text = self.format_pull_request_event(event, data)
138 elif isinstance(event, events.RepoPushEvent):
142 elif isinstance(event, events.RepoPushEvent):
139 text = self.format_repo_push_event(data)
143 text = self.format_repo_push_event(data)
140 elif isinstance(event, events.RepoCreateEvent):
144 elif isinstance(event, events.RepoCreateEvent):
141 text = self.format_repo_create_event(data)
145 text = self.format_repo_create_event(data)
142 else:
146 else:
143 log.error('unhandled event type: %r' % event)
147 log.error('unhandled event type: %r' % event)
144
148
145 run_task(post_text_to_slack, self.settings, text)
149 run_task(post_text_to_slack, self.settings, text)
146
150
147 @classmethod
151 def settings_schema(self):
148 def settings_schema(cls):
149 schema = SlackSettingsSchema()
152 schema = SlackSettingsSchema()
150 schema.add(colander.SchemaNode(
153 schema.add(colander.SchemaNode(
151 colander.Set(),
154 colander.Set(),
152 widget='checkbox_list',
155 widget=deform.widget.CheckboxChoiceWidget(
153 choices=sorted([e.name for e in cls.valid_events]),
156 values=sorted(
157 [(e.name, e.display_name) for e in self.valid_events]
158 )
159 ),
154 description="Events activated for this integration",
160 description="Events activated for this integration",
155 name='events'
161 name='events'
156 ))
162 ))
163
157 return schema
164 return schema
158
165
159 def format_pull_request_comment_event(self, event, data):
166 def format_pull_request_comment_event(self, event, data):
160 comment_text = data['comment']['text']
167 comment_text = data['comment']['text']
161 if len(comment_text) > 200:
168 if len(comment_text) > 200:
162 comment_text = '<{comment_url}|{comment_text}...>'.format(
169 comment_text = '<{comment_url}|{comment_text}...>'.format(
163 comment_text=comment_text[:200],
170 comment_text=comment_text[:200],
164 comment_url=data['comment']['url'],
171 comment_url=data['comment']['url'],
165 )
172 )
166
173
167 comment_status = ''
174 comment_status = ''
168 if data['comment']['status']:
175 if data['comment']['status']:
169 comment_status = '[{}]: '.format(data['comment']['status'])
176 comment_status = '[{}]: '.format(data['comment']['status'])
170
177
171 return (textwrap.dedent(
178 return (textwrap.dedent(
172 '''
179 '''
173 {user} commented on pull request <{pr_url}|#{number}> - {pr_title}:
180 {user} commented on pull request <{pr_url}|#{number}> - {pr_title}:
174 >>> {comment_status}{comment_text}
181 >>> {comment_status}{comment_text}
175 ''').format(
182 ''').format(
176 comment_status=comment_status,
183 comment_status=comment_status,
177 user=data['actor']['username'],
184 user=data['actor']['username'],
178 number=data['pullrequest']['pull_request_id'],
185 number=data['pullrequest']['pull_request_id'],
179 pr_url=data['pullrequest']['url'],
186 pr_url=data['pullrequest']['url'],
180 pr_status=data['pullrequest']['status'],
187 pr_status=data['pullrequest']['status'],
181 pr_title=data['pullrequest']['title'],
188 pr_title=data['pullrequest']['title'],
182 comment_text=comment_text
189 comment_text=comment_text
183 )
190 )
184 )
191 )
185
192
186 def format_pull_request_review_event(self, event, data):
193 def format_pull_request_review_event(self, event, data):
187 return (textwrap.dedent(
194 return (textwrap.dedent(
188 '''
195 '''
189 Status changed to {pr_status} for pull request <{pr_url}|#{number}> - {pr_title}
196 Status changed to {pr_status} for pull request <{pr_url}|#{number}> - {pr_title}
190 ''').format(
197 ''').format(
191 user=data['actor']['username'],
198 user=data['actor']['username'],
192 number=data['pullrequest']['pull_request_id'],
199 number=data['pullrequest']['pull_request_id'],
193 pr_url=data['pullrequest']['url'],
200 pr_url=data['pullrequest']['url'],
194 pr_status=data['pullrequest']['status'],
201 pr_status=data['pullrequest']['status'],
195 pr_title=data['pullrequest']['title'],
202 pr_title=data['pullrequest']['title'],
196 )
203 )
197 )
204 )
198
205
199 def format_pull_request_event(self, event, data):
206 def format_pull_request_event(self, event, data):
200 action = {
207 action = {
201 events.PullRequestCloseEvent: 'closed',
208 events.PullRequestCloseEvent: 'closed',
202 events.PullRequestMergeEvent: 'merged',
209 events.PullRequestMergeEvent: 'merged',
203 events.PullRequestUpdateEvent: 'updated',
210 events.PullRequestUpdateEvent: 'updated',
204 events.PullRequestCreateEvent: 'created',
211 events.PullRequestCreateEvent: 'created',
205 }.get(event.__class__, str(event.__class__))
212 }.get(event.__class__, str(event.__class__))
206
213
207 return ('Pull request <{url}|#{number}> - {title} '
214 return ('Pull request <{url}|#{number}> - {title} '
208 '{action} by {user}').format(
215 '{action} by {user}').format(
209 user=data['actor']['username'],
216 user=data['actor']['username'],
210 number=data['pullrequest']['pull_request_id'],
217 number=data['pullrequest']['pull_request_id'],
211 url=data['pullrequest']['url'],
218 url=data['pullrequest']['url'],
212 title=data['pullrequest']['title'],
219 title=data['pullrequest']['title'],
213 action=action
220 action=action
214 )
221 )
215
222
216 def format_repo_push_event(self, data):
223 def format_repo_push_event(self, data):
217 result = repo_push_template.render(
224 result = repo_push_template.render(
218 data=data,
225 data=data,
219 html_to_slack_links=html_to_slack_links,
226 html_to_slack_links=html_to_slack_links,
220 )
227 )
221 return result
228 return result
222
229
223 def format_repo_create_event(self, data):
230 def format_repo_create_event(self, data):
224 return '<{}|{}> ({}) repository created by *{}*'.format(
231 return '<{}|{}> ({}) repository created by *{}*'.format(
225 data['repo']['url'],
232 data['repo']['url'],
226 data['repo']['repo_name'],
233 data['repo']['repo_name'],
227 data['repo']['repo_type'],
234 data['repo']['repo_type'],
228 data['actor']['username'],
235 data['actor']['username'],
229 )
236 )
230
237
231
238
232 def html_to_slack_links(message):
239 def html_to_slack_links(message):
233 return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(
240 return re.compile(r'<a .*?href=["\'](.+?)".*?>(.+?)</a>').sub(
234 r'<\1|\2>', message)
241 r'<\1|\2>', message)
235
242
236
243
237 @task(ignore_result=True)
244 @task(ignore_result=True)
238 def post_text_to_slack(settings, text):
245 def post_text_to_slack(settings, text):
239 log.debug('sending %s to slack %s' % (text, settings['service']))
246 log.debug('sending %s to slack %s' % (text, settings['service']))
240 resp = requests.post(settings['service'], json={
247 resp = requests.post(settings['service'], json={
241 "channel": settings.get('channel', ''),
248 "channel": settings.get('channel', ''),
242 "username": settings.get('username', 'Rhodecode'),
249 "username": settings.get('username', 'Rhodecode'),
243 "text": text,
250 "text": text,
244 "icon_emoji": settings.get('icon_emoji', ':studio_microphone:')
251 "icon_emoji": settings.get('icon_emoji', ':studio_microphone:')
245 })
252 })
246 resp.raise_for_status() # raise exception on a failed request
253 resp.raise_for_status() # raise exception on a failed request
@@ -1,106 +1,111 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 from __future__ import unicode_literals
21 from __future__ import unicode_literals
22
22
23 import deform
23 import logging
24 import logging
24 import requests
25 import requests
25 import colander
26 import colander
26 from celery.task import task
27 from celery.task import task
27 from mako.template import Template
28 from mako.template import Template
28
29
29 from rhodecode import events
30 from rhodecode import events
30 from rhodecode.translation import lazy_ugettext
31 from rhodecode.translation import lazy_ugettext
31 from rhodecode.integrations.types.base import IntegrationTypeBase
32 from rhodecode.integrations.types.base import IntegrationTypeBase
32 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
33 from rhodecode.integrations.schema import IntegrationSettingsSchemaBase
33
34
34 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
35
36
36
37
37 class WebhookSettingsSchema(IntegrationSettingsSchemaBase):
38 class WebhookSettingsSchema(IntegrationSettingsSchemaBase):
38 url = colander.SchemaNode(
39 url = colander.SchemaNode(
39 colander.String(),
40 colander.String(),
40 title=lazy_ugettext('Webhook URL'),
41 title=lazy_ugettext('Webhook URL'),
41 description=lazy_ugettext('URL of the webhook to receive POST event.'),
42 description=lazy_ugettext('URL of the webhook to receive POST event.'),
42 default='',
43 default='',
43 validator=colander.url,
44 validator=colander.url,
44 placeholder='https://www.example.com/webhook',
45 widget=deform.widget.TextInputWidget(
45 widget='string'
46 placeholder='https://www.example.com/webhook'
47 ),
46 )
48 )
47 secret_token = colander.SchemaNode(
49 secret_token = colander.SchemaNode(
48 colander.String(),
50 colander.String(),
49 title=lazy_ugettext('Secret Token'),
51 title=lazy_ugettext('Secret Token'),
50 description=lazy_ugettext('String used to validate received payloads.'),
52 description=lazy_ugettext('String used to validate received payloads.'),
51 default='',
53 default='',
52 placeholder='secret_token',
54 widget=deform.widget.TextInputWidget(
53 widget='string'
55 placeholder='secret_token'
56 ),
54 )
57 )
55
58
56
59
57 class WebhookIntegrationType(IntegrationTypeBase):
60 class WebhookIntegrationType(IntegrationTypeBase):
58 key = 'webhook'
61 key = 'webhook'
59 display_name = lazy_ugettext('Webhook')
62 display_name = lazy_ugettext('Webhook')
60 valid_events = [
63 valid_events = [
61 events.PullRequestCloseEvent,
64 events.PullRequestCloseEvent,
62 events.PullRequestMergeEvent,
65 events.PullRequestMergeEvent,
63 events.PullRequestUpdateEvent,
66 events.PullRequestUpdateEvent,
64 events.PullRequestCommentEvent,
67 events.PullRequestCommentEvent,
65 events.PullRequestReviewEvent,
68 events.PullRequestReviewEvent,
66 events.PullRequestCreateEvent,
69 events.PullRequestCreateEvent,
67 events.RepoPushEvent,
70 events.RepoPushEvent,
68 events.RepoCreateEvent,
71 events.RepoCreateEvent,
69 ]
72 ]
70
73
71 @classmethod
74 def settings_schema(self):
72 def settings_schema(cls):
73 schema = WebhookSettingsSchema()
75 schema = WebhookSettingsSchema()
74 schema.add(colander.SchemaNode(
76 schema.add(colander.SchemaNode(
75 colander.Set(),
77 colander.Set(),
76 widget='checkbox_list',
78 widget=deform.widget.CheckboxChoiceWidget(
77 choices=sorted([e.name for e in cls.valid_events]),
79 values=sorted(
80 [(e.name, e.display_name) for e in self.valid_events]
81 )
82 ),
78 description="Events activated for this integration",
83 description="Events activated for this integration",
79 name='events'
84 name='events'
80 ))
85 ))
81 return schema
86 return schema
82
87
83 def send_event(self, event):
88 def send_event(self, event):
84 log.debug('handling event %s with webhook integration %s',
89 log.debug('handling event %s with webhook integration %s',
85 event.name, self)
90 event.name, self)
86
91
87 if event.__class__ not in self.valid_events:
92 if event.__class__ not in self.valid_events:
88 log.debug('event not valid: %r' % event)
93 log.debug('event not valid: %r' % event)
89 return
94 return
90
95
91 if event.name not in self.settings['events']:
96 if event.name not in self.settings['events']:
92 log.debug('event ignored: %r' % event)
97 log.debug('event ignored: %r' % event)
93 return
98 return
94
99
95 data = event.as_dict()
100 data = event.as_dict()
96 post_to_webhook(data, self.settings)
101 post_to_webhook(data, self.settings)
97
102
98
103
99 @task(ignore_result=True)
104 @task(ignore_result=True)
100 def post_to_webhook(data, settings):
105 def post_to_webhook(data, settings):
101 log.debug('sending event:%s to webhook %s', data['name'], settings['url'])
106 log.debug('sending event:%s to webhook %s', data['name'], settings['url'])
102 resp = requests.post(settings['url'], json={
107 resp = requests.post(settings['url'], json={
103 'token': settings['secret_token'],
108 'token': settings['secret_token'],
104 'event': data
109 'event': data
105 })
110 })
106 resp.raise_for_status() # raise exception on a failed request
111 resp.raise_for_status() # raise exception on a failed request
@@ -1,258 +1,272 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import colander
21 import colander
22 import logging
22 import logging
23 import pylons
23 import pylons
24 import deform
24
25
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.renderers import render
27 from pyramid.renderers import render
27 from pyramid.response import Response
28 from pyramid.response import Response
28
29
29 from rhodecode.lib import auth
30 from rhodecode.lib import auth
30 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
31 from rhodecode.model.db import Repository, Session, Integration
32 from rhodecode.model.db import Repository, Session, Integration
32 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.scm import ScmModel
33 from rhodecode.model.integration import IntegrationModel
34 from rhodecode.model.integration import IntegrationModel
34 from rhodecode.admin.navigation import navigation_list
35 from rhodecode.admin.navigation import navigation_list
35 from rhodecode.translation import _
36 from rhodecode.translation import _
36 from rhodecode.integrations import integration_type_registry
37 from rhodecode.integrations import integration_type_registry
37
38
38 log = logging.getLogger(__name__)
39 log = logging.getLogger(__name__)
39
40
40
41
41 class IntegrationSettingsViewBase(object):
42 class IntegrationSettingsViewBase(object):
42 """ Base Integration settings view used by both repo / global settings """
43 """ Base Integration settings view used by both repo / global settings """
43
44
44 def __init__(self, context, request):
45 def __init__(self, context, request):
45 self.context = context
46 self.context = context
46 self.request = request
47 self.request = request
47 self._load_general_context()
48 self._load_general_context()
48
49
49 if not self.perm_check(request.user):
50 if not self.perm_check(request.user):
50 raise HTTPForbidden()
51 raise HTTPForbidden()
51
52
52 def _load_general_context(self):
53 def _load_general_context(self):
53 """
54 """
54 This avoids boilerplate for repo/global+list/edit+views/templates
55 This avoids boilerplate for repo/global+list/edit+views/templates
55 by doing all possible contexts at the same time however it should
56 by doing all possible contexts at the same time however it should
56 be split up into separate functions once more "contexts" exist
57 be split up into separate functions once more "contexts" exist
57 """
58 """
58
59
59 self.IntegrationType = None
60 self.IntegrationType = None
60 self.repo = None
61 self.repo = None
61 self.integration = None
62 self.integration = None
62 self.integrations = {}
63 self.integrations = {}
63
64
64 request = self.request
65 request = self.request
65
66
66 if 'repo_name' in request.matchdict: # we're in a repo context
67 if 'repo_name' in request.matchdict: # we're in a repo context
67 repo_name = request.matchdict['repo_name']
68 repo_name = request.matchdict['repo_name']
68 self.repo = Repository.get_by_repo_name(repo_name)
69 self.repo = Repository.get_by_repo_name(repo_name)
69
70
70 if 'integration' in request.matchdict: # we're in integration context
71 if 'integration' in request.matchdict: # we're in integration context
71 integration_type = request.matchdict['integration']
72 integration_type = request.matchdict['integration']
72 self.IntegrationType = integration_type_registry[integration_type]
73 self.IntegrationType = integration_type_registry[integration_type]
73
74
74 if 'integration_id' in request.matchdict: # single integration context
75 if 'integration_id' in request.matchdict: # single integration context
75 integration_id = request.matchdict['integration_id']
76 integration_id = request.matchdict['integration_id']
76 self.integration = Integration.get(integration_id)
77 self.integration = Integration.get(integration_id)
77 else: # list integrations context
78 else: # list integrations context
78 for integration in IntegrationModel().get_integrations(self.repo):
79 for integration in IntegrationModel().get_integrations(self.repo):
79 self.integrations.setdefault(integration.integration_type, []
80 self.integrations.setdefault(integration.integration_type, []
80 ).append(integration)
81 ).append(integration)
81
82
82 self.settings = self.integration and self.integration.settings or {}
83 self.settings = self.integration and self.integration.settings or {}
83
84
84 def _template_c_context(self):
85 def _template_c_context(self):
85 # TODO: dan: this is a stopgap in order to inherit from current pylons
86 # TODO: dan: this is a stopgap in order to inherit from current pylons
86 # based admin/repo settings templates - this should be removed entirely
87 # based admin/repo settings templates - this should be removed entirely
87 # after port to pyramid
88 # after port to pyramid
88
89
89 c = pylons.tmpl_context
90 c = pylons.tmpl_context
90 c.active = 'integrations'
91 c.active = 'integrations'
91 c.rhodecode_user = self.request.user
92 c.rhodecode_user = self.request.user
92 c.repo = self.repo
93 c.repo = self.repo
93 c.repo_name = self.repo and self.repo.repo_name or None
94 c.repo_name = self.repo and self.repo.repo_name or None
94 if self.repo:
95 if self.repo:
95 c.repo_info = self.repo
96 c.repo_info = self.repo
96 c.rhodecode_db_repo = self.repo
97 c.rhodecode_db_repo = self.repo
97 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
98 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
98 else:
99 else:
99 c.navlist = navigation_list(self.request)
100 c.navlist = navigation_list(self.request)
100
101
101 return c
102 return c
102
103
103 def _form_schema(self):
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 View that displays the plugin settings as a form.
113 View that displays the plugin settings as a form.
109 """
114 """
110 defaults = defaults or {}
115 defaults = defaults or {}
111 errors = errors or {}
116 errors = errors or {}
112
117
113 schema = self._form_schema()
118 if self.integration:
114
119 defaults = self.integration.settings or {}
115 if not defaults:
120 defaults['name'] = self.integration.name
116 if self.integration:
121 defaults['enabled'] = self.integration.enabled
117 defaults['enabled'] = self.integration.enabled
122 else:
118 defaults['name'] = self.integration.name
123 if self.repo:
124 scope = self.repo.repo_name
119 else:
125 else:
120 if self.repo:
126 scope = _('Global')
121 scope = self.repo.repo_name
127
122 else:
128 defaults['name'] = '{} {} integration'.format(scope,
123 scope = _('Global')
129 self.IntegrationType.display_name)
130 defaults['enabled'] = True
124
131
125 defaults['name'] = '{} {} integration'.format(scope,
132 schema = self._form_schema().bind(request=self.request)
126 self.IntegrationType.display_name)
133
127 defaults['enabled'] = True
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 for node in schema:
141 for node in schema:
130 setting = self.settings.get(node.name)
142 setting = self.settings.get(node.name)
131 if setting is not None:
143 if setting is not None:
132 defaults.setdefault(node.name, setting)
144 defaults.setdefault(node.name, setting)
133 else:
145 else:
134 if node.default:
146 if node.default:
135 defaults.setdefault(node.name, node.default)
147 defaults.setdefault(node.name, node.default)
136
148
137 template_context = {
149 template_context = {
150 'form': form,
138 'defaults': defaults,
151 'defaults': defaults,
139 'errors': errors,
152 'errors': errors,
140 'schema': schema,
153 'schema': schema,
141 'current_IntegrationType': self.IntegrationType,
154 'current_IntegrationType': self.IntegrationType,
142 'integration': self.integration,
155 'integration': self.integration,
143 'settings': self.settings,
156 'settings': self.settings,
144 'resource': self.context,
157 'resource': self.context,
145 'c': self._template_c_context(),
158 'c': self._template_c_context(),
146 }
159 }
147
160
148 return template_context
161 return template_context
149
162
150 @auth.CSRFRequired()
163 @auth.CSRFRequired()
151 def settings_post(self):
164 def settings_post(self):
152 """
165 """
153 View that validates and stores the plugin settings.
166 View that validates and stores the plugin settings.
154 """
167 """
155 if self.request.params.get('delete'):
168 if self.request.params.get('delete'):
156 Session().delete(self.integration)
169 Session().delete(self.integration)
157 Session().commit()
170 Session().commit()
158 self.request.session.flash(
171 self.request.session.flash(
159 _('Integration {integration_name} deleted successfully.').format(
172 _('Integration {integration_name} deleted successfully.').format(
160 integration_name=self.integration.name),
173 integration_name=self.integration.name),
161 queue='success')
174 queue='success')
162 if self.repo:
175 if self.repo:
163 redirect_to = self.request.route_url(
176 redirect_to = self.request.route_url(
164 'repo_integrations_home', repo_name=self.repo.repo_name)
177 'repo_integrations_home', repo_name=self.repo.repo_name)
165 else:
178 else:
166 redirect_to = self.request.route_url('global_integrations_home')
179 redirect_to = self.request.route_url('global_integrations_home')
167 raise HTTPFound(redirect_to)
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 params = {}
186 params = {}
172 for node in schema.children:
187 for node in schema.children:
173 if type(node.typ) in (colander.Set, colander.List):
188 if type(node.typ) in (colander.Set, colander.List):
174 val = self.request.params.getall(node.name)
189 val = self.request.params.getall(node.name)
175 else:
190 else:
176 val = self.request.params.get(node.name)
191 val = self.request.params.get(node.name)
177 if val:
192 if val:
178 params[node.name] = val
193 params[node.name] = val
179
194
195 controls = self.request.POST.items()
180 try:
196 try:
181 valid_data = schema.deserialize(params)
197 valid_data = form.validate(controls)
182 except colander.Invalid as e:
198 except deform.ValidationFailure as e:
183 # Display error message and display form again.
184 self.request.session.flash(
199 self.request.session.flash(
185 _('Errors exist when saving plugin settings. '
200 _('Errors exist when saving integration settings. '
186 'Please check the form inputs.'),
201 'Please check the form inputs.'),
187 queue='error')
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 if not self.integration:
205 if not self.integration:
191 self.integration = Integration()
206 self.integration = Integration()
192 self.integration.integration_type = self.IntegrationType.key
207 self.integration.integration_type = self.IntegrationType.key
193 if self.repo:
208 if self.repo:
194 self.integration.repo = self.repo
209 self.integration.repo = self.repo
195 Session().add(self.integration)
210 Session().add(self.integration)
196
211
197 self.integration.enabled = valid_data.pop('enabled', False)
212 self.integration.enabled = valid_data.pop('enabled', False)
198 self.integration.name = valid_data.pop('name')
213 self.integration.name = valid_data.pop('name')
199 self.integration.settings = valid_data
214 self.integration.settings = valid_data
200
215
201 Session().commit()
216 Session().commit()
202
217
203 # Display success message and redirect.
218 # Display success message and redirect.
204 self.request.session.flash(
219 self.request.session.flash(
205 _('Integration {integration_name} updated successfully.').format(
220 _('Integration {integration_name} updated successfully.').format(
206 integration_name=self.IntegrationType.display_name),
221 integration_name=self.IntegrationType.display_name),
207 queue='success')
222 queue='success')
208
223
209 if self.repo:
224 if self.repo:
210 redirect_to = self.request.route_url(
225 redirect_to = self.request.route_url(
211 'repo_integrations_edit', repo_name=self.repo.repo_name,
226 'repo_integrations_edit', repo_name=self.repo.repo_name,
212 integration=self.integration.integration_type,
227 integration=self.integration.integration_type,
213 integration_id=self.integration.integration_id)
228 integration_id=self.integration.integration_id)
214 else:
229 else:
215 redirect_to = self.request.route_url(
230 redirect_to = self.request.route_url(
216 'global_integrations_edit',
231 'global_integrations_edit',
217 integration=self.integration.integration_type,
232 integration=self.integration.integration_type,
218 integration_id=self.integration.integration_id)
233 integration_id=self.integration.integration_id)
219
234
220 return HTTPFound(redirect_to)
235 return HTTPFound(redirect_to)
221
236
222 def index(self):
237 def index(self):
223 current_integrations = self.integrations
238 current_integrations = self.integrations
224 if self.IntegrationType:
239 if self.IntegrationType:
225 current_integrations = {
240 current_integrations = {
226 self.IntegrationType.key: self.integrations.get(
241 self.IntegrationType.key: self.integrations.get(
227 self.IntegrationType.key, [])
242 self.IntegrationType.key, [])
228 }
243 }
229
244
230 template_context = {
245 template_context = {
231 'current_IntegrationType': self.IntegrationType,
246 'current_IntegrationType': self.IntegrationType,
232 'current_integrations': current_integrations,
247 'current_integrations': current_integrations,
233 'current_integration': 'none',
234 'available_integrations': integration_type_registry,
248 'available_integrations': integration_type_registry,
235 'c': self._template_c_context()
249 'c': self._template_c_context()
236 }
250 }
237
251
238 if self.repo:
252 if self.repo:
239 html = render('rhodecode:templates/admin/integrations/list.html',
253 html = render('rhodecode:templates/admin/integrations/list.html',
240 template_context,
254 template_context,
241 request=self.request)
255 request=self.request)
242 else:
256 else:
243 html = render('rhodecode:templates/admin/integrations/list.html',
257 html = render('rhodecode:templates/admin/integrations/list.html',
244 template_context,
258 template_context,
245 request=self.request)
259 request=self.request)
246
260
247 return Response(html)
261 return Response(html)
248
262
249
263
250 class GlobalIntegrationsView(IntegrationSettingsViewBase):
264 class GlobalIntegrationsView(IntegrationSettingsViewBase):
251 def perm_check(self, user):
265 def perm_check(self, user):
252 return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
266 return auth.HasPermissionAll('hg.admin').check_permissions(user=user)
253
267
254
268
255 class RepoIntegrationsView(IntegrationSettingsViewBase):
269 class RepoIntegrationsView(IntegrationSettingsViewBase):
256 def perm_check(self, user):
270 def perm_check(self, user):
257 return auth.HasRepoPermissionAll('repository.admin'
271 return auth.HasRepoPermissionAll('repository.admin'
258 )(repo_name=self.repo.repo_name, user=user)
272 )(repo_name=self.repo.repo_name, user=user)
@@ -1,1947 +1,1962 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 Helper functions
22 Helper functions
23
23
24 Consists of functions to typically be used within templates, but also
24 Consists of functions to typically be used within templates, but also
25 available to Controllers. This module is available to both as 'h'.
25 available to Controllers. This module is available to both as 'h'.
26 """
26 """
27
27
28 import random
28 import random
29 import hashlib
29 import hashlib
30 import StringIO
30 import StringIO
31 import urllib
31 import urllib
32 import math
32 import math
33 import logging
33 import logging
34 import re
34 import re
35 import urlparse
35 import urlparse
36 import time
36 import time
37 import string
37 import string
38 import hashlib
38 import hashlib
39 import pygments
39 import pygments
40
40
41 from datetime import datetime
41 from datetime import datetime
42 from functools import partial
42 from functools import partial
43 from pygments.formatters.html import HtmlFormatter
43 from pygments.formatters.html import HtmlFormatter
44 from pygments import highlight as code_highlight
44 from pygments import highlight as code_highlight
45 from pygments.lexers import (
45 from pygments.lexers import (
46 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
46 get_lexer_by_name, get_lexer_for_filename, get_lexer_for_mimetype)
47 from pylons import url as pylons_url
47 from pylons import url as pylons_url
48 from pylons.i18n.translation import _, ungettext
48 from pylons.i18n.translation import _, ungettext
49 from pyramid.threadlocal import get_current_request
49 from pyramid.threadlocal import get_current_request
50
50
51 from webhelpers.html import literal, HTML, escape
51 from webhelpers.html import literal, HTML, escape
52 from webhelpers.html.tools import *
52 from webhelpers.html.tools import *
53 from webhelpers.html.builder import make_tag
53 from webhelpers.html.builder import make_tag
54 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
54 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
55 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
55 end_form, file, form as wh_form, hidden, image, javascript_link, link_to, \
56 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
56 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
57 submit, text, password, textarea, title, ul, xml_declaration, radio
57 submit, text, password, textarea, title, ul, xml_declaration, radio
58 from webhelpers.html.tools import auto_link, button_to, highlight, \
58 from webhelpers.html.tools import auto_link, button_to, highlight, \
59 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
59 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
60 from webhelpers.pylonslib import Flash as _Flash
60 from webhelpers.pylonslib import Flash as _Flash
61 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
61 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
62 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
62 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
63 replace_whitespace, urlify, truncate, wrap_paragraphs
63 replace_whitespace, urlify, truncate, wrap_paragraphs
64 from webhelpers.date import time_ago_in_words
64 from webhelpers.date import time_ago_in_words
65 from webhelpers.paginate import Page as _Page
65 from webhelpers.paginate import Page as _Page
66 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
66 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
67 convert_boolean_attrs, NotGiven, _make_safe_id_component
67 convert_boolean_attrs, NotGiven, _make_safe_id_component
68 from webhelpers2.number import format_byte_size
68 from webhelpers2.number import format_byte_size
69
69
70 from rhodecode.lib.annotate import annotate_highlight
70 from rhodecode.lib.annotate import annotate_highlight
71 from rhodecode.lib.action_parser import action_parser
71 from rhodecode.lib.action_parser import action_parser
72 from rhodecode.lib.ext_json import json
72 from rhodecode.lib.ext_json import json
73 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
73 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
74 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
74 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
75 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
75 get_commit_safe, datetime_to_time, time_to_datetime, time_to_utcdatetime, \
76 AttributeDict, safe_int, md5, md5_safe
76 AttributeDict, safe_int, md5, md5_safe
77 from rhodecode.lib.markup_renderer import MarkupRenderer
77 from rhodecode.lib.markup_renderer import MarkupRenderer
78 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
78 from rhodecode.lib.vcs.exceptions import CommitDoesNotExistError
79 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
79 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyCommit
80 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
80 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
81 from rhodecode.model.changeset_status import ChangesetStatusModel
81 from rhodecode.model.changeset_status import ChangesetStatusModel
82 from rhodecode.model.db import Permission, User, Repository
82 from rhodecode.model.db import Permission, User, Repository
83 from rhodecode.model.repo_group import RepoGroupModel
83 from rhodecode.model.repo_group import RepoGroupModel
84 from rhodecode.model.settings import IssueTrackerSettingsModel
84 from rhodecode.model.settings import IssueTrackerSettingsModel
85
85
86 log = logging.getLogger(__name__)
86 log = logging.getLogger(__name__)
87
87
88
88
89 DEFAULT_USER = User.DEFAULT_USER
89 DEFAULT_USER = User.DEFAULT_USER
90 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
90 DEFAULT_USER_EMAIL = User.DEFAULT_USER_EMAIL
91
91
92
92
93 def url(*args, **kw):
93 def url(*args, **kw):
94 return pylons_url(*args, **kw)
94 return pylons_url(*args, **kw)
95
95
96
96
97 def pylons_url_current(*args, **kw):
97 def pylons_url_current(*args, **kw):
98 """
98 """
99 This function overrides pylons.url.current() which returns the current
99 This function overrides pylons.url.current() which returns the current
100 path so that it will also work from a pyramid only context. This
100 path so that it will also work from a pyramid only context. This
101 should be removed once port to pyramid is complete.
101 should be removed once port to pyramid is complete.
102 """
102 """
103 if not args and not kw:
103 if not args and not kw:
104 request = get_current_request()
104 request = get_current_request()
105 return request.path
105 return request.path
106 return pylons_url.current(*args, **kw)
106 return pylons_url.current(*args, **kw)
107
107
108 url.current = pylons_url_current
108 url.current = pylons_url_current
109
109
110
110
111 def asset(path, ver=None):
111 def asset(path, ver=None):
112 """
112 """
113 Helper to generate a static asset file path for rhodecode assets
113 Helper to generate a static asset file path for rhodecode assets
114
114
115 eg. h.asset('images/image.png', ver='3923')
115 eg. h.asset('images/image.png', ver='3923')
116
116
117 :param path: path of asset
117 :param path: path of asset
118 :param ver: optional version query param to append as ?ver=
118 :param ver: optional version query param to append as ?ver=
119 """
119 """
120 request = get_current_request()
120 request = get_current_request()
121 return request.static_url('rhodecode:public/{}'.format(path), ver=ver)
121 return request.static_url('rhodecode:public/{}'.format(path), ver=ver)
122
122
123
123
124 def html_escape(text, html_escape_table=None):
124 def html_escape(text, html_escape_table=None):
125 """Produce entities within text."""
125 """Produce entities within text."""
126 if not html_escape_table:
126 if not html_escape_table:
127 html_escape_table = {
127 html_escape_table = {
128 "&": "&amp;",
128 "&": "&amp;",
129 '"': "&quot;",
129 '"': "&quot;",
130 "'": "&apos;",
130 "'": "&apos;",
131 ">": "&gt;",
131 ">": "&gt;",
132 "<": "&lt;",
132 "<": "&lt;",
133 }
133 }
134 return "".join(html_escape_table.get(c, c) for c in text)
134 return "".join(html_escape_table.get(c, c) for c in text)
135
135
136
136
137 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
137 def chop_at_smart(s, sub, inclusive=False, suffix_if_chopped=None):
138 """
138 """
139 Truncate string ``s`` at the first occurrence of ``sub``.
139 Truncate string ``s`` at the first occurrence of ``sub``.
140
140
141 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
141 If ``inclusive`` is true, truncate just after ``sub`` rather than at it.
142 """
142 """
143 suffix_if_chopped = suffix_if_chopped or ''
143 suffix_if_chopped = suffix_if_chopped or ''
144 pos = s.find(sub)
144 pos = s.find(sub)
145 if pos == -1:
145 if pos == -1:
146 return s
146 return s
147
147
148 if inclusive:
148 if inclusive:
149 pos += len(sub)
149 pos += len(sub)
150
150
151 chopped = s[:pos]
151 chopped = s[:pos]
152 left = s[pos:].strip()
152 left = s[pos:].strip()
153
153
154 if left and suffix_if_chopped:
154 if left and suffix_if_chopped:
155 chopped += suffix_if_chopped
155 chopped += suffix_if_chopped
156
156
157 return chopped
157 return chopped
158
158
159
159
160 def shorter(text, size=20):
160 def shorter(text, size=20):
161 postfix = '...'
161 postfix = '...'
162 if len(text) > size:
162 if len(text) > size:
163 return text[:size - len(postfix)] + postfix
163 return text[:size - len(postfix)] + postfix
164 return text
164 return text
165
165
166
166
167 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
167 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
168 """
168 """
169 Reset button
169 Reset button
170 """
170 """
171 _set_input_attrs(attrs, type, name, value)
171 _set_input_attrs(attrs, type, name, value)
172 _set_id_attr(attrs, id, name)
172 _set_id_attr(attrs, id, name)
173 convert_boolean_attrs(attrs, ["disabled"])
173 convert_boolean_attrs(attrs, ["disabled"])
174 return HTML.input(**attrs)
174 return HTML.input(**attrs)
175
175
176 reset = _reset
176 reset = _reset
177 safeid = _make_safe_id_component
177 safeid = _make_safe_id_component
178
178
179
179
180 def branding(name, length=40):
180 def branding(name, length=40):
181 return truncate(name, length, indicator="")
181 return truncate(name, length, indicator="")
182
182
183
183
184 def FID(raw_id, path):
184 def FID(raw_id, path):
185 """
185 """
186 Creates a unique ID for filenode based on it's hash of path and commit
186 Creates a unique ID for filenode based on it's hash of path and commit
187 it's safe to use in urls
187 it's safe to use in urls
188
188
189 :param raw_id:
189 :param raw_id:
190 :param path:
190 :param path:
191 """
191 """
192
192
193 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
193 return 'c-%s-%s' % (short_id(raw_id), md5_safe(path)[:12])
194
194
195
195
196 class _GetError(object):
196 class _GetError(object):
197 """Get error from form_errors, and represent it as span wrapped error
197 """Get error from form_errors, and represent it as span wrapped error
198 message
198 message
199
199
200 :param field_name: field to fetch errors for
200 :param field_name: field to fetch errors for
201 :param form_errors: form errors dict
201 :param form_errors: form errors dict
202 """
202 """
203
203
204 def __call__(self, field_name, form_errors):
204 def __call__(self, field_name, form_errors):
205 tmpl = """<span class="error_msg">%s</span>"""
205 tmpl = """<span class="error_msg">%s</span>"""
206 if form_errors and field_name in form_errors:
206 if form_errors and field_name in form_errors:
207 return literal(tmpl % form_errors.get(field_name))
207 return literal(tmpl % form_errors.get(field_name))
208
208
209 get_error = _GetError()
209 get_error = _GetError()
210
210
211
211
212 class _ToolTip(object):
212 class _ToolTip(object):
213
213
214 def __call__(self, tooltip_title, trim_at=50):
214 def __call__(self, tooltip_title, trim_at=50):
215 """
215 """
216 Special function just to wrap our text into nice formatted
216 Special function just to wrap our text into nice formatted
217 autowrapped text
217 autowrapped text
218
218
219 :param tooltip_title:
219 :param tooltip_title:
220 """
220 """
221 tooltip_title = escape(tooltip_title)
221 tooltip_title = escape(tooltip_title)
222 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
222 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
223 return tooltip_title
223 return tooltip_title
224 tooltip = _ToolTip()
224 tooltip = _ToolTip()
225
225
226
226
227 def files_breadcrumbs(repo_name, commit_id, file_path):
227 def files_breadcrumbs(repo_name, commit_id, file_path):
228 if isinstance(file_path, str):
228 if isinstance(file_path, str):
229 file_path = safe_unicode(file_path)
229 file_path = safe_unicode(file_path)
230
230
231 # TODO: johbo: Is this always a url like path, or is this operating
231 # TODO: johbo: Is this always a url like path, or is this operating
232 # system dependent?
232 # system dependent?
233 path_segments = file_path.split('/')
233 path_segments = file_path.split('/')
234
234
235 repo_name_html = escape(repo_name)
235 repo_name_html = escape(repo_name)
236 if len(path_segments) == 1 and path_segments[0] == '':
236 if len(path_segments) == 1 and path_segments[0] == '':
237 url_segments = [repo_name_html]
237 url_segments = [repo_name_html]
238 else:
238 else:
239 url_segments = [
239 url_segments = [
240 link_to(
240 link_to(
241 repo_name_html,
241 repo_name_html,
242 url('files_home',
242 url('files_home',
243 repo_name=repo_name,
243 repo_name=repo_name,
244 revision=commit_id,
244 revision=commit_id,
245 f_path=''),
245 f_path=''),
246 class_='pjax-link')]
246 class_='pjax-link')]
247
247
248 last_cnt = len(path_segments) - 1
248 last_cnt = len(path_segments) - 1
249 for cnt, segment in enumerate(path_segments):
249 for cnt, segment in enumerate(path_segments):
250 if not segment:
250 if not segment:
251 continue
251 continue
252 segment_html = escape(segment)
252 segment_html = escape(segment)
253
253
254 if cnt != last_cnt:
254 if cnt != last_cnt:
255 url_segments.append(
255 url_segments.append(
256 link_to(
256 link_to(
257 segment_html,
257 segment_html,
258 url('files_home',
258 url('files_home',
259 repo_name=repo_name,
259 repo_name=repo_name,
260 revision=commit_id,
260 revision=commit_id,
261 f_path='/'.join(path_segments[:cnt + 1])),
261 f_path='/'.join(path_segments[:cnt + 1])),
262 class_='pjax-link'))
262 class_='pjax-link'))
263 else:
263 else:
264 url_segments.append(segment_html)
264 url_segments.append(segment_html)
265
265
266 return literal('/'.join(url_segments))
266 return literal('/'.join(url_segments))
267
267
268
268
269 class CodeHtmlFormatter(HtmlFormatter):
269 class CodeHtmlFormatter(HtmlFormatter):
270 """
270 """
271 My code Html Formatter for source codes
271 My code Html Formatter for source codes
272 """
272 """
273
273
274 def wrap(self, source, outfile):
274 def wrap(self, source, outfile):
275 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
275 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
276
276
277 def _wrap_code(self, source):
277 def _wrap_code(self, source):
278 for cnt, it in enumerate(source):
278 for cnt, it in enumerate(source):
279 i, t = it
279 i, t = it
280 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
280 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
281 yield i, t
281 yield i, t
282
282
283 def _wrap_tablelinenos(self, inner):
283 def _wrap_tablelinenos(self, inner):
284 dummyoutfile = StringIO.StringIO()
284 dummyoutfile = StringIO.StringIO()
285 lncount = 0
285 lncount = 0
286 for t, line in inner:
286 for t, line in inner:
287 if t:
287 if t:
288 lncount += 1
288 lncount += 1
289 dummyoutfile.write(line)
289 dummyoutfile.write(line)
290
290
291 fl = self.linenostart
291 fl = self.linenostart
292 mw = len(str(lncount + fl - 1))
292 mw = len(str(lncount + fl - 1))
293 sp = self.linenospecial
293 sp = self.linenospecial
294 st = self.linenostep
294 st = self.linenostep
295 la = self.lineanchors
295 la = self.lineanchors
296 aln = self.anchorlinenos
296 aln = self.anchorlinenos
297 nocls = self.noclasses
297 nocls = self.noclasses
298 if sp:
298 if sp:
299 lines = []
299 lines = []
300
300
301 for i in range(fl, fl + lncount):
301 for i in range(fl, fl + lncount):
302 if i % st == 0:
302 if i % st == 0:
303 if i % sp == 0:
303 if i % sp == 0:
304 if aln:
304 if aln:
305 lines.append('<a href="#%s%d" class="special">%*d</a>' %
305 lines.append('<a href="#%s%d" class="special">%*d</a>' %
306 (la, i, mw, i))
306 (la, i, mw, i))
307 else:
307 else:
308 lines.append('<span class="special">%*d</span>' % (mw, i))
308 lines.append('<span class="special">%*d</span>' % (mw, i))
309 else:
309 else:
310 if aln:
310 if aln:
311 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
311 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
312 else:
312 else:
313 lines.append('%*d' % (mw, i))
313 lines.append('%*d' % (mw, i))
314 else:
314 else:
315 lines.append('')
315 lines.append('')
316 ls = '\n'.join(lines)
316 ls = '\n'.join(lines)
317 else:
317 else:
318 lines = []
318 lines = []
319 for i in range(fl, fl + lncount):
319 for i in range(fl, fl + lncount):
320 if i % st == 0:
320 if i % st == 0:
321 if aln:
321 if aln:
322 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
322 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
323 else:
323 else:
324 lines.append('%*d' % (mw, i))
324 lines.append('%*d' % (mw, i))
325 else:
325 else:
326 lines.append('')
326 lines.append('')
327 ls = '\n'.join(lines)
327 ls = '\n'.join(lines)
328
328
329 # in case you wonder about the seemingly redundant <div> here: since the
329 # in case you wonder about the seemingly redundant <div> here: since the
330 # content in the other cell also is wrapped in a div, some browsers in
330 # content in the other cell also is wrapped in a div, some browsers in
331 # some configurations seem to mess up the formatting...
331 # some configurations seem to mess up the formatting...
332 if nocls:
332 if nocls:
333 yield 0, ('<table class="%stable">' % self.cssclass +
333 yield 0, ('<table class="%stable">' % self.cssclass +
334 '<tr><td><div class="linenodiv" '
334 '<tr><td><div class="linenodiv" '
335 'style="background-color: #f0f0f0; padding-right: 10px">'
335 'style="background-color: #f0f0f0; padding-right: 10px">'
336 '<pre style="line-height: 125%">' +
336 '<pre style="line-height: 125%">' +
337 ls + '</pre></div></td><td id="hlcode" class="code">')
337 ls + '</pre></div></td><td id="hlcode" class="code">')
338 else:
338 else:
339 yield 0, ('<table class="%stable">' % self.cssclass +
339 yield 0, ('<table class="%stable">' % self.cssclass +
340 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
340 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
341 ls + '</pre></div></td><td id="hlcode" class="code">')
341 ls + '</pre></div></td><td id="hlcode" class="code">')
342 yield 0, dummyoutfile.getvalue()
342 yield 0, dummyoutfile.getvalue()
343 yield 0, '</td></tr></table>'
343 yield 0, '</td></tr></table>'
344
344
345
345
346 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
346 class SearchContentCodeHtmlFormatter(CodeHtmlFormatter):
347 def __init__(self, **kw):
347 def __init__(self, **kw):
348 # only show these line numbers if set
348 # only show these line numbers if set
349 self.only_lines = kw.pop('only_line_numbers', [])
349 self.only_lines = kw.pop('only_line_numbers', [])
350 self.query_terms = kw.pop('query_terms', [])
350 self.query_terms = kw.pop('query_terms', [])
351 self.max_lines = kw.pop('max_lines', 5)
351 self.max_lines = kw.pop('max_lines', 5)
352 self.line_context = kw.pop('line_context', 3)
352 self.line_context = kw.pop('line_context', 3)
353 self.url = kw.pop('url', None)
353 self.url = kw.pop('url', None)
354
354
355 super(CodeHtmlFormatter, self).__init__(**kw)
355 super(CodeHtmlFormatter, self).__init__(**kw)
356
356
357 def _wrap_code(self, source):
357 def _wrap_code(self, source):
358 for cnt, it in enumerate(source):
358 for cnt, it in enumerate(source):
359 i, t = it
359 i, t = it
360 t = '<pre>%s</pre>' % t
360 t = '<pre>%s</pre>' % t
361 yield i, t
361 yield i, t
362
362
363 def _wrap_tablelinenos(self, inner):
363 def _wrap_tablelinenos(self, inner):
364 yield 0, '<table class="code-highlight %stable">' % self.cssclass
364 yield 0, '<table class="code-highlight %stable">' % self.cssclass
365
365
366 last_shown_line_number = 0
366 last_shown_line_number = 0
367 current_line_number = 1
367 current_line_number = 1
368
368
369 for t, line in inner:
369 for t, line in inner:
370 if not t:
370 if not t:
371 yield t, line
371 yield t, line
372 continue
372 continue
373
373
374 if current_line_number in self.only_lines:
374 if current_line_number in self.only_lines:
375 if last_shown_line_number + 1 != current_line_number:
375 if last_shown_line_number + 1 != current_line_number:
376 yield 0, '<tr>'
376 yield 0, '<tr>'
377 yield 0, '<td class="line">...</td>'
377 yield 0, '<td class="line">...</td>'
378 yield 0, '<td id="hlcode" class="code"></td>'
378 yield 0, '<td id="hlcode" class="code"></td>'
379 yield 0, '</tr>'
379 yield 0, '</tr>'
380
380
381 yield 0, '<tr>'
381 yield 0, '<tr>'
382 if self.url:
382 if self.url:
383 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
383 yield 0, '<td class="line"><a href="%s#L%i">%i</a></td>' % (
384 self.url, current_line_number, current_line_number)
384 self.url, current_line_number, current_line_number)
385 else:
385 else:
386 yield 0, '<td class="line"><a href="">%i</a></td>' % (
386 yield 0, '<td class="line"><a href="">%i</a></td>' % (
387 current_line_number)
387 current_line_number)
388 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
388 yield 0, '<td id="hlcode" class="code">' + line + '</td>'
389 yield 0, '</tr>'
389 yield 0, '</tr>'
390
390
391 last_shown_line_number = current_line_number
391 last_shown_line_number = current_line_number
392
392
393 current_line_number += 1
393 current_line_number += 1
394
394
395
395
396 yield 0, '</table>'
396 yield 0, '</table>'
397
397
398
398
399 def extract_phrases(text_query):
399 def extract_phrases(text_query):
400 """
400 """
401 Extracts phrases from search term string making sure phrases
401 Extracts phrases from search term string making sure phrases
402 contained in double quotes are kept together - and discarding empty values
402 contained in double quotes are kept together - and discarding empty values
403 or fully whitespace values eg.
403 or fully whitespace values eg.
404
404
405 'some text "a phrase" more' => ['some', 'text', 'a phrase', 'more']
405 'some text "a phrase" more' => ['some', 'text', 'a phrase', 'more']
406
406
407 """
407 """
408
408
409 in_phrase = False
409 in_phrase = False
410 buf = ''
410 buf = ''
411 phrases = []
411 phrases = []
412 for char in text_query:
412 for char in text_query:
413 if in_phrase:
413 if in_phrase:
414 if char == '"': # end phrase
414 if char == '"': # end phrase
415 phrases.append(buf)
415 phrases.append(buf)
416 buf = ''
416 buf = ''
417 in_phrase = False
417 in_phrase = False
418 continue
418 continue
419 else:
419 else:
420 buf += char
420 buf += char
421 continue
421 continue
422 else:
422 else:
423 if char == '"': # start phrase
423 if char == '"': # start phrase
424 in_phrase = True
424 in_phrase = True
425 phrases.append(buf)
425 phrases.append(buf)
426 buf = ''
426 buf = ''
427 continue
427 continue
428 elif char == ' ':
428 elif char == ' ':
429 phrases.append(buf)
429 phrases.append(buf)
430 buf = ''
430 buf = ''
431 continue
431 continue
432 else:
432 else:
433 buf += char
433 buf += char
434
434
435 phrases.append(buf)
435 phrases.append(buf)
436 phrases = [phrase.strip() for phrase in phrases if phrase.strip()]
436 phrases = [phrase.strip() for phrase in phrases if phrase.strip()]
437 return phrases
437 return phrases
438
438
439
439
440 def get_matching_offsets(text, phrases):
440 def get_matching_offsets(text, phrases):
441 """
441 """
442 Returns a list of string offsets in `text` that the list of `terms` match
442 Returns a list of string offsets in `text` that the list of `terms` match
443
443
444 >>> get_matching_offsets('some text here', ['some', 'here'])
444 >>> get_matching_offsets('some text here', ['some', 'here'])
445 [(0, 4), (10, 14)]
445 [(0, 4), (10, 14)]
446
446
447 """
447 """
448 offsets = []
448 offsets = []
449 for phrase in phrases:
449 for phrase in phrases:
450 for match in re.finditer(phrase, text):
450 for match in re.finditer(phrase, text):
451 offsets.append((match.start(), match.end()))
451 offsets.append((match.start(), match.end()))
452
452
453 return offsets
453 return offsets
454
454
455
455
456 def normalize_text_for_matching(x):
456 def normalize_text_for_matching(x):
457 """
457 """
458 Replaces all non alnum characters to spaces and lower cases the string,
458 Replaces all non alnum characters to spaces and lower cases the string,
459 useful for comparing two text strings without punctuation
459 useful for comparing two text strings without punctuation
460 """
460 """
461 return re.sub(r'[^\w]', ' ', x.lower())
461 return re.sub(r'[^\w]', ' ', x.lower())
462
462
463
463
464 def get_matching_line_offsets(lines, terms):
464 def get_matching_line_offsets(lines, terms):
465 """ Return a set of `lines` indices (starting from 1) matching a
465 """ Return a set of `lines` indices (starting from 1) matching a
466 text search query, along with `context` lines above/below matching lines
466 text search query, along with `context` lines above/below matching lines
467
467
468 :param lines: list of strings representing lines
468 :param lines: list of strings representing lines
469 :param terms: search term string to match in lines eg. 'some text'
469 :param terms: search term string to match in lines eg. 'some text'
470 :param context: number of lines above/below a matching line to add to result
470 :param context: number of lines above/below a matching line to add to result
471 :param max_lines: cut off for lines of interest
471 :param max_lines: cut off for lines of interest
472 eg.
472 eg.
473
473
474 text = '''
474 text = '''
475 words words words
475 words words words
476 words words words
476 words words words
477 some text some
477 some text some
478 words words words
478 words words words
479 words words words
479 words words words
480 text here what
480 text here what
481 '''
481 '''
482 get_matching_line_offsets(text, 'text', context=1)
482 get_matching_line_offsets(text, 'text', context=1)
483 {3: [(5, 9)], 6: [(0, 4)]]
483 {3: [(5, 9)], 6: [(0, 4)]]
484
484
485 """
485 """
486 matching_lines = {}
486 matching_lines = {}
487 phrases = [normalize_text_for_matching(phrase)
487 phrases = [normalize_text_for_matching(phrase)
488 for phrase in extract_phrases(terms)]
488 for phrase in extract_phrases(terms)]
489
489
490 for line_index, line in enumerate(lines, start=1):
490 for line_index, line in enumerate(lines, start=1):
491 match_offsets = get_matching_offsets(
491 match_offsets = get_matching_offsets(
492 normalize_text_for_matching(line), phrases)
492 normalize_text_for_matching(line), phrases)
493 if match_offsets:
493 if match_offsets:
494 matching_lines[line_index] = match_offsets
494 matching_lines[line_index] = match_offsets
495
495
496 return matching_lines
496 return matching_lines
497
497
498
498
499 def get_lexer_safe(mimetype=None, filepath=None):
499 def get_lexer_safe(mimetype=None, filepath=None):
500 """
500 """
501 Tries to return a relevant pygments lexer using mimetype/filepath name,
501 Tries to return a relevant pygments lexer using mimetype/filepath name,
502 defaulting to plain text if none could be found
502 defaulting to plain text if none could be found
503 """
503 """
504 lexer = None
504 lexer = None
505 try:
505 try:
506 if mimetype:
506 if mimetype:
507 lexer = get_lexer_for_mimetype(mimetype)
507 lexer = get_lexer_for_mimetype(mimetype)
508 if not lexer:
508 if not lexer:
509 lexer = get_lexer_for_filename(filepath)
509 lexer = get_lexer_for_filename(filepath)
510 except pygments.util.ClassNotFound:
510 except pygments.util.ClassNotFound:
511 pass
511 pass
512
512
513 if not lexer:
513 if not lexer:
514 lexer = get_lexer_by_name('text')
514 lexer = get_lexer_by_name('text')
515
515
516 return lexer
516 return lexer
517
517
518
518
519 def pygmentize(filenode, **kwargs):
519 def pygmentize(filenode, **kwargs):
520 """
520 """
521 pygmentize function using pygments
521 pygmentize function using pygments
522
522
523 :param filenode:
523 :param filenode:
524 """
524 """
525 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
525 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
526 return literal(code_highlight(filenode.content, lexer,
526 return literal(code_highlight(filenode.content, lexer,
527 CodeHtmlFormatter(**kwargs)))
527 CodeHtmlFormatter(**kwargs)))
528
528
529
529
530 def pygmentize_annotation(repo_name, filenode, **kwargs):
530 def pygmentize_annotation(repo_name, filenode, **kwargs):
531 """
531 """
532 pygmentize function for annotation
532 pygmentize function for annotation
533
533
534 :param filenode:
534 :param filenode:
535 """
535 """
536
536
537 color_dict = {}
537 color_dict = {}
538
538
539 def gen_color(n=10000):
539 def gen_color(n=10000):
540 """generator for getting n of evenly distributed colors using
540 """generator for getting n of evenly distributed colors using
541 hsv color and golden ratio. It always return same order of colors
541 hsv color and golden ratio. It always return same order of colors
542
542
543 :returns: RGB tuple
543 :returns: RGB tuple
544 """
544 """
545
545
546 def hsv_to_rgb(h, s, v):
546 def hsv_to_rgb(h, s, v):
547 if s == 0.0:
547 if s == 0.0:
548 return v, v, v
548 return v, v, v
549 i = int(h * 6.0) # XXX assume int() truncates!
549 i = int(h * 6.0) # XXX assume int() truncates!
550 f = (h * 6.0) - i
550 f = (h * 6.0) - i
551 p = v * (1.0 - s)
551 p = v * (1.0 - s)
552 q = v * (1.0 - s * f)
552 q = v * (1.0 - s * f)
553 t = v * (1.0 - s * (1.0 - f))
553 t = v * (1.0 - s * (1.0 - f))
554 i = i % 6
554 i = i % 6
555 if i == 0:
555 if i == 0:
556 return v, t, p
556 return v, t, p
557 if i == 1:
557 if i == 1:
558 return q, v, p
558 return q, v, p
559 if i == 2:
559 if i == 2:
560 return p, v, t
560 return p, v, t
561 if i == 3:
561 if i == 3:
562 return p, q, v
562 return p, q, v
563 if i == 4:
563 if i == 4:
564 return t, p, v
564 return t, p, v
565 if i == 5:
565 if i == 5:
566 return v, p, q
566 return v, p, q
567
567
568 golden_ratio = 0.618033988749895
568 golden_ratio = 0.618033988749895
569 h = 0.22717784590367374
569 h = 0.22717784590367374
570
570
571 for _ in xrange(n):
571 for _ in xrange(n):
572 h += golden_ratio
572 h += golden_ratio
573 h %= 1
573 h %= 1
574 HSV_tuple = [h, 0.95, 0.95]
574 HSV_tuple = [h, 0.95, 0.95]
575 RGB_tuple = hsv_to_rgb(*HSV_tuple)
575 RGB_tuple = hsv_to_rgb(*HSV_tuple)
576 yield map(lambda x: str(int(x * 256)), RGB_tuple)
576 yield map(lambda x: str(int(x * 256)), RGB_tuple)
577
577
578 cgenerator = gen_color()
578 cgenerator = gen_color()
579
579
580 def get_color_string(commit_id):
580 def get_color_string(commit_id):
581 if commit_id in color_dict:
581 if commit_id in color_dict:
582 col = color_dict[commit_id]
582 col = color_dict[commit_id]
583 else:
583 else:
584 col = color_dict[commit_id] = cgenerator.next()
584 col = color_dict[commit_id] = cgenerator.next()
585 return "color: rgb(%s)! important;" % (', '.join(col))
585 return "color: rgb(%s)! important;" % (', '.join(col))
586
586
587 def url_func(repo_name):
587 def url_func(repo_name):
588
588
589 def _url_func(commit):
589 def _url_func(commit):
590 author = commit.author
590 author = commit.author
591 date = commit.date
591 date = commit.date
592 message = tooltip(commit.message)
592 message = tooltip(commit.message)
593
593
594 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
594 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
595 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
595 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
596 "</b> %s<br/></div>")
596 "</b> %s<br/></div>")
597
597
598 tooltip_html = tooltip_html % (author, date, message)
598 tooltip_html = tooltip_html % (author, date, message)
599 lnk_format = '%5s:%s' % ('r%s' % commit.idx, commit.short_id)
599 lnk_format = '%5s:%s' % ('r%s' % commit.idx, commit.short_id)
600 uri = link_to(
600 uri = link_to(
601 lnk_format,
601 lnk_format,
602 url('changeset_home', repo_name=repo_name,
602 url('changeset_home', repo_name=repo_name,
603 revision=commit.raw_id),
603 revision=commit.raw_id),
604 style=get_color_string(commit.raw_id),
604 style=get_color_string(commit.raw_id),
605 class_='tooltip',
605 class_='tooltip',
606 title=tooltip_html
606 title=tooltip_html
607 )
607 )
608
608
609 uri += '\n'
609 uri += '\n'
610 return uri
610 return uri
611 return _url_func
611 return _url_func
612
612
613 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
613 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
614
614
615
615
616 def is_following_repo(repo_name, user_id):
616 def is_following_repo(repo_name, user_id):
617 from rhodecode.model.scm import ScmModel
617 from rhodecode.model.scm import ScmModel
618 return ScmModel().is_following_repo(repo_name, user_id)
618 return ScmModel().is_following_repo(repo_name, user_id)
619
619
620
620
621 class _Message(object):
621 class _Message(object):
622 """A message returned by ``Flash.pop_messages()``.
622 """A message returned by ``Flash.pop_messages()``.
623
623
624 Converting the message to a string returns the message text. Instances
624 Converting the message to a string returns the message text. Instances
625 also have the following attributes:
625 also have the following attributes:
626
626
627 * ``message``: the message text.
627 * ``message``: the message text.
628 * ``category``: the category specified when the message was created.
628 * ``category``: the category specified when the message was created.
629 """
629 """
630
630
631 def __init__(self, category, message):
631 def __init__(self, category, message):
632 self.category = category
632 self.category = category
633 self.message = message
633 self.message = message
634
634
635 def __str__(self):
635 def __str__(self):
636 return self.message
636 return self.message
637
637
638 __unicode__ = __str__
638 __unicode__ = __str__
639
639
640 def __html__(self):
640 def __html__(self):
641 return escape(safe_unicode(self.message))
641 return escape(safe_unicode(self.message))
642
642
643
643
644 class Flash(_Flash):
644 class Flash(_Flash):
645
645
646 def pop_messages(self):
646 def pop_messages(self):
647 """Return all accumulated messages and delete them from the session.
647 """Return all accumulated messages and delete them from the session.
648
648
649 The return value is a list of ``Message`` objects.
649 The return value is a list of ``Message`` objects.
650 """
650 """
651 from pylons import session
651 from pylons import session
652
652
653 messages = []
653 messages = []
654
654
655 # Pop the 'old' pylons flash messages. They are tuples of the form
655 # Pop the 'old' pylons flash messages. They are tuples of the form
656 # (category, message)
656 # (category, message)
657 for cat, msg in session.pop(self.session_key, []):
657 for cat, msg in session.pop(self.session_key, []):
658 messages.append(_Message(cat, msg))
658 messages.append(_Message(cat, msg))
659
659
660 # Pop the 'new' pyramid flash messages for each category as list
660 # Pop the 'new' pyramid flash messages for each category as list
661 # of strings.
661 # of strings.
662 for cat in self.categories:
662 for cat in self.categories:
663 for msg in session.pop_flash(queue=cat):
663 for msg in session.pop_flash(queue=cat):
664 messages.append(_Message(cat, msg))
664 messages.append(_Message(cat, msg))
665 # Map messages from the default queue to the 'notice' category.
665 # Map messages from the default queue to the 'notice' category.
666 for msg in session.pop_flash():
666 for msg in session.pop_flash():
667 messages.append(_Message('notice', msg))
667 messages.append(_Message('notice', msg))
668
668
669 session.save()
669 session.save()
670 return messages
670 return messages
671
671
672 flash = Flash()
672 flash = Flash()
673
673
674 #==============================================================================
674 #==============================================================================
675 # SCM FILTERS available via h.
675 # SCM FILTERS available via h.
676 #==============================================================================
676 #==============================================================================
677 from rhodecode.lib.vcs.utils import author_name, author_email
677 from rhodecode.lib.vcs.utils import author_name, author_email
678 from rhodecode.lib.utils2 import credentials_filter, age as _age
678 from rhodecode.lib.utils2 import credentials_filter, age as _age
679 from rhodecode.model.db import User, ChangesetStatus
679 from rhodecode.model.db import User, ChangesetStatus
680
680
681 age = _age
681 age = _age
682 capitalize = lambda x: x.capitalize()
682 capitalize = lambda x: x.capitalize()
683 email = author_email
683 email = author_email
684 short_id = lambda x: x[:12]
684 short_id = lambda x: x[:12]
685 hide_credentials = lambda x: ''.join(credentials_filter(x))
685 hide_credentials = lambda x: ''.join(credentials_filter(x))
686
686
687
687
688 def age_component(datetime_iso, value=None, time_is_local=False):
688 def age_component(datetime_iso, value=None, time_is_local=False):
689 title = value or format_date(datetime_iso)
689 title = value or format_date(datetime_iso)
690
690
691 # detect if we have a timezone info, otherwise, add it
691 # detect if we have a timezone info, otherwise, add it
692 if isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
692 if isinstance(datetime_iso, datetime) and not datetime_iso.tzinfo:
693 tzinfo = '+00:00'
693 tzinfo = '+00:00'
694
694
695 if time_is_local:
695 if time_is_local:
696 tzinfo = time.strftime("+%H:%M",
696 tzinfo = time.strftime("+%H:%M",
697 time.gmtime(
697 time.gmtime(
698 (datetime.now() - datetime.utcnow()).seconds + 1
698 (datetime.now() - datetime.utcnow()).seconds + 1
699 )
699 )
700 )
700 )
701
701
702 return literal(
702 return literal(
703 '<time class="timeago tooltip" '
703 '<time class="timeago tooltip" '
704 'title="{1}" datetime="{0}{2}">{1}</time>'.format(
704 'title="{1}" datetime="{0}{2}">{1}</time>'.format(
705 datetime_iso, title, tzinfo))
705 datetime_iso, title, tzinfo))
706
706
707
707
708 def _shorten_commit_id(commit_id):
708 def _shorten_commit_id(commit_id):
709 from rhodecode import CONFIG
709 from rhodecode import CONFIG
710 def_len = safe_int(CONFIG.get('rhodecode_show_sha_length', 12))
710 def_len = safe_int(CONFIG.get('rhodecode_show_sha_length', 12))
711 return commit_id[:def_len]
711 return commit_id[:def_len]
712
712
713
713
714 def show_id(commit):
714 def show_id(commit):
715 """
715 """
716 Configurable function that shows ID
716 Configurable function that shows ID
717 by default it's r123:fffeeefffeee
717 by default it's r123:fffeeefffeee
718
718
719 :param commit: commit instance
719 :param commit: commit instance
720 """
720 """
721 from rhodecode import CONFIG
721 from rhodecode import CONFIG
722 show_idx = str2bool(CONFIG.get('rhodecode_show_revision_number', True))
722 show_idx = str2bool(CONFIG.get('rhodecode_show_revision_number', True))
723
723
724 raw_id = _shorten_commit_id(commit.raw_id)
724 raw_id = _shorten_commit_id(commit.raw_id)
725 if show_idx:
725 if show_idx:
726 return 'r%s:%s' % (commit.idx, raw_id)
726 return 'r%s:%s' % (commit.idx, raw_id)
727 else:
727 else:
728 return '%s' % (raw_id, )
728 return '%s' % (raw_id, )
729
729
730
730
731 def format_date(date):
731 def format_date(date):
732 """
732 """
733 use a standardized formatting for dates used in RhodeCode
733 use a standardized formatting for dates used in RhodeCode
734
734
735 :param date: date/datetime object
735 :param date: date/datetime object
736 :return: formatted date
736 :return: formatted date
737 """
737 """
738
738
739 if date:
739 if date:
740 _fmt = "%a, %d %b %Y %H:%M:%S"
740 _fmt = "%a, %d %b %Y %H:%M:%S"
741 return safe_unicode(date.strftime(_fmt))
741 return safe_unicode(date.strftime(_fmt))
742
742
743 return u""
743 return u""
744
744
745
745
746 class _RepoChecker(object):
746 class _RepoChecker(object):
747
747
748 def __init__(self, backend_alias):
748 def __init__(self, backend_alias):
749 self._backend_alias = backend_alias
749 self._backend_alias = backend_alias
750
750
751 def __call__(self, repository):
751 def __call__(self, repository):
752 if hasattr(repository, 'alias'):
752 if hasattr(repository, 'alias'):
753 _type = repository.alias
753 _type = repository.alias
754 elif hasattr(repository, 'repo_type'):
754 elif hasattr(repository, 'repo_type'):
755 _type = repository.repo_type
755 _type = repository.repo_type
756 else:
756 else:
757 _type = repository
757 _type = repository
758 return _type == self._backend_alias
758 return _type == self._backend_alias
759
759
760 is_git = _RepoChecker('git')
760 is_git = _RepoChecker('git')
761 is_hg = _RepoChecker('hg')
761 is_hg = _RepoChecker('hg')
762 is_svn = _RepoChecker('svn')
762 is_svn = _RepoChecker('svn')
763
763
764
764
765 def get_repo_type_by_name(repo_name):
765 def get_repo_type_by_name(repo_name):
766 repo = Repository.get_by_repo_name(repo_name)
766 repo = Repository.get_by_repo_name(repo_name)
767 return repo.repo_type
767 return repo.repo_type
768
768
769
769
770 def is_svn_without_proxy(repository):
770 def is_svn_without_proxy(repository):
771 from rhodecode import CONFIG
771 from rhodecode import CONFIG
772 if is_svn(repository):
772 if is_svn(repository):
773 if not CONFIG.get('rhodecode_proxy_subversion_http_requests', False):
773 if not CONFIG.get('rhodecode_proxy_subversion_http_requests', False):
774 return True
774 return True
775 return False
775 return False
776
776
777
777
778 def discover_user(author):
778 def discover_user(author):
779 """
779 """
780 Tries to discover RhodeCode User based on the autho string. Author string
780 Tries to discover RhodeCode User based on the autho string. Author string
781 is typically `FirstName LastName <email@address.com>`
781 is typically `FirstName LastName <email@address.com>`
782 """
782 """
783
783
784 # if author is already an instance use it for extraction
784 # if author is already an instance use it for extraction
785 if isinstance(author, User):
785 if isinstance(author, User):
786 return author
786 return author
787
787
788 # Valid email in the attribute passed, see if they're in the system
788 # Valid email in the attribute passed, see if they're in the system
789 _email = author_email(author)
789 _email = author_email(author)
790 if _email != '':
790 if _email != '':
791 user = User.get_by_email(_email, case_insensitive=True, cache=True)
791 user = User.get_by_email(_email, case_insensitive=True, cache=True)
792 if user is not None:
792 if user is not None:
793 return user
793 return user
794
794
795 # Maybe it's a username, we try to extract it and fetch by username ?
795 # Maybe it's a username, we try to extract it and fetch by username ?
796 _author = author_name(author)
796 _author = author_name(author)
797 user = User.get_by_username(_author, case_insensitive=True, cache=True)
797 user = User.get_by_username(_author, case_insensitive=True, cache=True)
798 if user is not None:
798 if user is not None:
799 return user
799 return user
800
800
801 return None
801 return None
802
802
803
803
804 def email_or_none(author):
804 def email_or_none(author):
805 # extract email from the commit string
805 # extract email from the commit string
806 _email = author_email(author)
806 _email = author_email(author)
807
807
808 # If we have an email, use it, otherwise
808 # If we have an email, use it, otherwise
809 # see if it contains a username we can get an email from
809 # see if it contains a username we can get an email from
810 if _email != '':
810 if _email != '':
811 return _email
811 return _email
812 else:
812 else:
813 user = User.get_by_username(
813 user = User.get_by_username(
814 author_name(author), case_insensitive=True, cache=True)
814 author_name(author), case_insensitive=True, cache=True)
815
815
816 if user is not None:
816 if user is not None:
817 return user.email
817 return user.email
818
818
819 # No valid email, not a valid user in the system, none!
819 # No valid email, not a valid user in the system, none!
820 return None
820 return None
821
821
822
822
823 def link_to_user(author, length=0, **kwargs):
823 def link_to_user(author, length=0, **kwargs):
824 user = discover_user(author)
824 user = discover_user(author)
825 # user can be None, but if we have it already it means we can re-use it
825 # user can be None, but if we have it already it means we can re-use it
826 # in the person() function, so we save 1 intensive-query
826 # in the person() function, so we save 1 intensive-query
827 if user:
827 if user:
828 author = user
828 author = user
829
829
830 display_person = person(author, 'username_or_name_or_email')
830 display_person = person(author, 'username_or_name_or_email')
831 if length:
831 if length:
832 display_person = shorter(display_person, length)
832 display_person = shorter(display_person, length)
833
833
834 if user:
834 if user:
835 return link_to(
835 return link_to(
836 escape(display_person),
836 escape(display_person),
837 url('user_profile', username=user.username),
837 url('user_profile', username=user.username),
838 **kwargs)
838 **kwargs)
839 else:
839 else:
840 return escape(display_person)
840 return escape(display_person)
841
841
842
842
843 def person(author, show_attr="username_and_name"):
843 def person(author, show_attr="username_and_name"):
844 user = discover_user(author)
844 user = discover_user(author)
845 if user:
845 if user:
846 return getattr(user, show_attr)
846 return getattr(user, show_attr)
847 else:
847 else:
848 _author = author_name(author)
848 _author = author_name(author)
849 _email = email(author)
849 _email = email(author)
850 return _author or _email
850 return _author or _email
851
851
852
852
853 def author_string(email):
853 def author_string(email):
854 if email:
854 if email:
855 user = User.get_by_email(email, case_insensitive=True, cache=True)
855 user = User.get_by_email(email, case_insensitive=True, cache=True)
856 if user:
856 if user:
857 if user.firstname or user.lastname:
857 if user.firstname or user.lastname:
858 return '%s %s &lt;%s&gt;' % (user.firstname, user.lastname, email)
858 return '%s %s &lt;%s&gt;' % (user.firstname, user.lastname, email)
859 else:
859 else:
860 return email
860 return email
861 else:
861 else:
862 return email
862 return email
863 else:
863 else:
864 return None
864 return None
865
865
866
866
867 def person_by_id(id_, show_attr="username_and_name"):
867 def person_by_id(id_, show_attr="username_and_name"):
868 # attr to return from fetched user
868 # attr to return from fetched user
869 person_getter = lambda usr: getattr(usr, show_attr)
869 person_getter = lambda usr: getattr(usr, show_attr)
870
870
871 #maybe it's an ID ?
871 #maybe it's an ID ?
872 if str(id_).isdigit() or isinstance(id_, int):
872 if str(id_).isdigit() or isinstance(id_, int):
873 id_ = int(id_)
873 id_ = int(id_)
874 user = User.get(id_)
874 user = User.get(id_)
875 if user is not None:
875 if user is not None:
876 return person_getter(user)
876 return person_getter(user)
877 return id_
877 return id_
878
878
879
879
880 def gravatar_with_user(author, show_disabled=False):
880 def gravatar_with_user(author, show_disabled=False):
881 from rhodecode.lib.utils import PartialRenderer
881 from rhodecode.lib.utils import PartialRenderer
882 _render = PartialRenderer('base/base.html')
882 _render = PartialRenderer('base/base.html')
883 return _render('gravatar_with_user', author, show_disabled=show_disabled)
883 return _render('gravatar_with_user', author, show_disabled=show_disabled)
884
884
885
885
886 def desc_stylize(value):
886 def desc_stylize(value):
887 """
887 """
888 converts tags from value into html equivalent
888 converts tags from value into html equivalent
889
889
890 :param value:
890 :param value:
891 """
891 """
892 if not value:
892 if not value:
893 return ''
893 return ''
894
894
895 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
895 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
896 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
896 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
897 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
897 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
898 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
898 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
899 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
899 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
900 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
900 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
901 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
901 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
902 '<div class="metatag" tag="lang">\\2</div>', value)
902 '<div class="metatag" tag="lang">\\2</div>', value)
903 value = re.sub(r'\[([a-z]+)\]',
903 value = re.sub(r'\[([a-z]+)\]',
904 '<div class="metatag" tag="\\1">\\1</div>', value)
904 '<div class="metatag" tag="\\1">\\1</div>', value)
905
905
906 return value
906 return value
907
907
908
908
909 def escaped_stylize(value):
909 def escaped_stylize(value):
910 """
910 """
911 converts tags from value into html equivalent, but escaping its value first
911 converts tags from value into html equivalent, but escaping its value first
912 """
912 """
913 if not value:
913 if not value:
914 return ''
914 return ''
915
915
916 # Using default webhelper escape method, but has to force it as a
916 # Using default webhelper escape method, but has to force it as a
917 # plain unicode instead of a markup tag to be used in regex expressions
917 # plain unicode instead of a markup tag to be used in regex expressions
918 value = unicode(escape(safe_unicode(value)))
918 value = unicode(escape(safe_unicode(value)))
919
919
920 value = re.sub(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
920 value = re.sub(r'\[see\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
921 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
921 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
922 value = re.sub(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
922 value = re.sub(r'\[license\ \=\&gt;\ *([a-zA-Z0-9\/\=\?\&amp;\ \:\/\.\-]*)\]',
923 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
923 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
924 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]',
924 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\&gt;\ *([a-zA-Z0-9\-\/]*)\]',
925 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
925 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
926 value = re.sub(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+]*)\]',
926 value = re.sub(r'\[(lang|language)\ \=\&gt;\ *([a-zA-Z\-\/\#\+]*)\]',
927 '<div class="metatag" tag="lang">\\2</div>', value)
927 '<div class="metatag" tag="lang">\\2</div>', value)
928 value = re.sub(r'\[([a-z]+)\]',
928 value = re.sub(r'\[([a-z]+)\]',
929 '<div class="metatag" tag="\\1">\\1</div>', value)
929 '<div class="metatag" tag="\\1">\\1</div>', value)
930
930
931 return value
931 return value
932
932
933
933
934 def bool2icon(value):
934 def bool2icon(value):
935 """
935 """
936 Returns boolean value of a given value, represented as html element with
936 Returns boolean value of a given value, represented as html element with
937 classes that will represent icons
937 classes that will represent icons
938
938
939 :param value: given value to convert to html node
939 :param value: given value to convert to html node
940 """
940 """
941
941
942 if value: # does bool conversion
942 if value: # does bool conversion
943 return HTML.tag('i', class_="icon-true")
943 return HTML.tag('i', class_="icon-true")
944 else: # not true as bool
944 else: # not true as bool
945 return HTML.tag('i', class_="icon-false")
945 return HTML.tag('i', class_="icon-false")
946
946
947
947
948 #==============================================================================
948 #==============================================================================
949 # PERMS
949 # PERMS
950 #==============================================================================
950 #==============================================================================
951 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
951 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
952 HasRepoPermissionAny, HasRepoPermissionAll, HasRepoGroupPermissionAll, \
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 # GRAVATAR URL
958 # GRAVATAR URL
958 #==============================================================================
959 #==============================================================================
959 class InitialsGravatar(object):
960 class InitialsGravatar(object):
960 def __init__(self, email_address, first_name, last_name, size=30,
961 def __init__(self, email_address, first_name, last_name, size=30,
961 background=None, text_color='#fff'):
962 background=None, text_color='#fff'):
962 self.size = size
963 self.size = size
963 self.first_name = first_name
964 self.first_name = first_name
964 self.last_name = last_name
965 self.last_name = last_name
965 self.email_address = email_address
966 self.email_address = email_address
966 self.background = background or self.str2color(email_address)
967 self.background = background or self.str2color(email_address)
967 self.text_color = text_color
968 self.text_color = text_color
968
969
969 def get_color_bank(self):
970 def get_color_bank(self):
970 """
971 """
971 returns a predefined list of colors that gravatars can use.
972 returns a predefined list of colors that gravatars can use.
972 Those are randomized distinct colors that guarantee readability and
973 Those are randomized distinct colors that guarantee readability and
973 uniqueness.
974 uniqueness.
974
975
975 generated with: http://phrogz.net/css/distinct-colors.html
976 generated with: http://phrogz.net/css/distinct-colors.html
976 """
977 """
977 return [
978 return [
978 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
979 '#bf3030', '#a67f53', '#00ff00', '#5989b3', '#392040', '#d90000',
979 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
980 '#402910', '#204020', '#79baf2', '#a700b3', '#bf6060', '#7f5320',
980 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
981 '#008000', '#003059', '#ee00ff', '#ff0000', '#8c4b00', '#007300',
981 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
982 '#005fb3', '#de73e6', '#ff4040', '#ffaa00', '#3df255', '#203140',
982 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
983 '#47004d', '#591616', '#664400', '#59b365', '#0d2133', '#83008c',
983 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
984 '#592d2d', '#bf9f60', '#73e682', '#1d3f73', '#73006b', '#402020',
984 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
985 '#b2862d', '#397341', '#597db3', '#e600d6', '#a60000', '#736039',
985 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
986 '#00b318', '#79aaf2', '#330d30', '#ff8080', '#403010', '#16591f',
986 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
987 '#002459', '#8c4688', '#e50000', '#ffbf40', '#00732e', '#102340',
987 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
988 '#bf60ac', '#8c4646', '#cc8800', '#00a642', '#1d3473', '#b32d98',
988 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
989 '#660e00', '#ffd580', '#80ffb2', '#7391e6', '#733967', '#d97b6c',
989 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
990 '#8c5e00', '#59b389', '#3967e6', '#590047', '#73281d', '#665200',
990 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
991 '#00e67a', '#2d50b3', '#8c2377', '#734139', '#b2982d', '#16593a',
991 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
992 '#001859', '#ff00aa', '#a65e53', '#ffcc00', '#0d3321', '#2d3959',
992 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
993 '#731d56', '#401610', '#4c3d00', '#468c6c', '#002ca6', '#d936a3',
993 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
994 '#d94c36', '#403920', '#36d9a3', '#0d1733', '#592d4a', '#993626',
994 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
995 '#cca300', '#00734d', '#46598c', '#8c005e', '#7f1100', '#8c7000',
995 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
996 '#00a66f', '#7382e6', '#b32d74', '#d9896c', '#ffe680', '#1d7362',
996 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
997 '#364cd9', '#73003d', '#d93a00', '#998a4d', '#59b3a1', '#5965b3',
997 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
998 '#e5007a', '#73341d', '#665f00', '#00b38f', '#0018b3', '#59163a',
998 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
999 '#b2502d', '#bfb960', '#00ffcc', '#23318c', '#a6537f', '#734939',
999 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
1000 '#b2a700', '#104036', '#3d3df2', '#402031', '#e56739', '#736f39',
1000 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
1001 '#79f2ea', '#000059', '#401029', '#4c1400', '#ffee00', '#005953',
1001 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
1002 '#101040', '#990052', '#402820', '#403d10', '#00ffee', '#0000d9',
1002 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
1003 '#ff80c4', '#a66953', '#eeff00', '#00ccbe', '#8080ff', '#e673a1',
1003 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
1004 '#a62c00', '#474d00', '#1a3331', '#46468c', '#733950', '#662900',
1004 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
1005 '#858c23', '#238c85', '#0f0073', '#b20047', '#d9986c', '#becc00',
1005 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
1006 '#396f73', '#281d73', '#ff0066', '#ff6600', '#dee673', '#59adb3',
1006 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
1007 '#6559b3', '#590024', '#b2622d', '#98b32d', '#36ced9', '#332d59',
1007 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
1008 '#40001a', '#733f1d', '#526600', '#005359', '#242040', '#bf6079',
1008 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
1009 '#735039', '#cef23d', '#007780', '#5630bf', '#66001b', '#b24700',
1009 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
1010 '#acbf60', '#1d6273', '#25008c', '#731d34', '#a67453', '#50592d',
1010 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
1011 '#00ccff', '#6600ff', '#ff0044', '#4c1f00', '#8a994d', '#79daf2',
1011 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
1012 '#a173e6', '#d93662', '#402310', '#aaff00', '#2d98b3', '#8c40ff',
1012 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
1013 '#592d39', '#ff8c40', '#354020', '#103640', '#1a0040', '#331a20',
1013 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
1014 '#331400', '#334d00', '#1d5673', '#583973', '#7f0022', '#4c3626',
1014 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
1015 '#88cc00', '#36a3d9', '#3d0073', '#d9364c', '#33241a', '#698c23',
1015 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
1016 '#5995b3', '#300059', '#e57382', '#7f3300', '#366600', '#00aaff',
1016 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
1017 '#3a1659', '#733941', '#663600', '#74b32d', '#003c59', '#7f53a6',
1017 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
1018 '#73000f', '#ff8800', '#baf279', '#79caf2', '#291040', '#a6293a',
1018 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
1019 '#b2742d', '#587339', '#0077b3', '#632699', '#400009', '#d9a66c',
1019 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
1020 '#294010', '#2d4a59', '#aa00ff', '#4c131b', '#b25f00', '#5ce600',
1020 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
1021 '#267399', '#a336d9', '#990014', '#664e33', '#86bf60', '#0088ff',
1021 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
1022 '#7700b3', '#593a16', '#073300', '#1d4b73', '#ac60bf', '#e59539',
1022 '#4f8c46', '#368dd9', '#5c0073'
1023 '#4f8c46', '#368dd9', '#5c0073'
1023 ]
1024 ]
1024
1025
1025 def rgb_to_hex_color(self, rgb_tuple):
1026 def rgb_to_hex_color(self, rgb_tuple):
1026 """
1027 """
1027 Converts an rgb_tuple passed to an hex color.
1028 Converts an rgb_tuple passed to an hex color.
1028
1029
1029 :param rgb_tuple: tuple with 3 ints represents rgb color space
1030 :param rgb_tuple: tuple with 3 ints represents rgb color space
1030 """
1031 """
1031 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
1032 return '#' + ("".join(map(chr, rgb_tuple)).encode('hex'))
1032
1033
1033 def email_to_int_list(self, email_str):
1034 def email_to_int_list(self, email_str):
1034 """
1035 """
1035 Get every byte of the hex digest value of email and turn it to integer.
1036 Get every byte of the hex digest value of email and turn it to integer.
1036 It's going to be always between 0-255
1037 It's going to be always between 0-255
1037 """
1038 """
1038 digest = md5_safe(email_str.lower())
1039 digest = md5_safe(email_str.lower())
1039 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
1040 return [int(digest[i * 2:i * 2 + 2], 16) for i in range(16)]
1040
1041
1041 def pick_color_bank_index(self, email_str, color_bank):
1042 def pick_color_bank_index(self, email_str, color_bank):
1042 return self.email_to_int_list(email_str)[0] % len(color_bank)
1043 return self.email_to_int_list(email_str)[0] % len(color_bank)
1043
1044
1044 def str2color(self, email_str):
1045 def str2color(self, email_str):
1045 """
1046 """
1046 Tries to map in a stable algorithm an email to color
1047 Tries to map in a stable algorithm an email to color
1047
1048
1048 :param email_str:
1049 :param email_str:
1049 """
1050 """
1050 color_bank = self.get_color_bank()
1051 color_bank = self.get_color_bank()
1051 # pick position (module it's length so we always find it in the
1052 # pick position (module it's length so we always find it in the
1052 # bank even if it's smaller than 256 values
1053 # bank even if it's smaller than 256 values
1053 pos = self.pick_color_bank_index(email_str, color_bank)
1054 pos = self.pick_color_bank_index(email_str, color_bank)
1054 return color_bank[pos]
1055 return color_bank[pos]
1055
1056
1056 def normalize_email(self, email_address):
1057 def normalize_email(self, email_address):
1057 import unicodedata
1058 import unicodedata
1058 # default host used to fill in the fake/missing email
1059 # default host used to fill in the fake/missing email
1059 default_host = u'localhost'
1060 default_host = u'localhost'
1060
1061
1061 if not email_address:
1062 if not email_address:
1062 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1063 email_address = u'%s@%s' % (User.DEFAULT_USER, default_host)
1063
1064
1064 email_address = safe_unicode(email_address)
1065 email_address = safe_unicode(email_address)
1065
1066
1066 if u'@' not in email_address:
1067 if u'@' not in email_address:
1067 email_address = u'%s@%s' % (email_address, default_host)
1068 email_address = u'%s@%s' % (email_address, default_host)
1068
1069
1069 if email_address.endswith(u'@'):
1070 if email_address.endswith(u'@'):
1070 email_address = u'%s%s' % (email_address, default_host)
1071 email_address = u'%s%s' % (email_address, default_host)
1071
1072
1072 email_address = unicodedata.normalize('NFKD', email_address)\
1073 email_address = unicodedata.normalize('NFKD', email_address)\
1073 .encode('ascii', 'ignore')
1074 .encode('ascii', 'ignore')
1074 return email_address
1075 return email_address
1075
1076
1076 def get_initials(self):
1077 def get_initials(self):
1077 """
1078 """
1078 Returns 2 letter initials calculated based on the input.
1079 Returns 2 letter initials calculated based on the input.
1079 The algorithm picks first given email address, and takes first letter
1080 The algorithm picks first given email address, and takes first letter
1080 of part before @, and then the first letter of server name. In case
1081 of part before @, and then the first letter of server name. In case
1081 the part before @ is in a format of `somestring.somestring2` it replaces
1082 the part before @ is in a format of `somestring.somestring2` it replaces
1082 the server letter with first letter of somestring2
1083 the server letter with first letter of somestring2
1083
1084
1084 In case function was initialized with both first and lastname, this
1085 In case function was initialized with both first and lastname, this
1085 overrides the extraction from email by first letter of the first and
1086 overrides the extraction from email by first letter of the first and
1086 last name. We add special logic to that functionality, In case Full name
1087 last name. We add special logic to that functionality, In case Full name
1087 is compound, like Guido Von Rossum, we use last part of the last name
1088 is compound, like Guido Von Rossum, we use last part of the last name
1088 (Von Rossum) picking `R`.
1089 (Von Rossum) picking `R`.
1089
1090
1090 Function also normalizes the non-ascii characters to they ascii
1091 Function also normalizes the non-ascii characters to they ascii
1091 representation, eg Δ„ => A
1092 representation, eg Δ„ => A
1092 """
1093 """
1093 import unicodedata
1094 import unicodedata
1094 # replace non-ascii to ascii
1095 # replace non-ascii to ascii
1095 first_name = unicodedata.normalize(
1096 first_name = unicodedata.normalize(
1096 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1097 'NFKD', safe_unicode(self.first_name)).encode('ascii', 'ignore')
1097 last_name = unicodedata.normalize(
1098 last_name = unicodedata.normalize(
1098 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1099 'NFKD', safe_unicode(self.last_name)).encode('ascii', 'ignore')
1099
1100
1100 # do NFKD encoding, and also make sure email has proper format
1101 # do NFKD encoding, and also make sure email has proper format
1101 email_address = self.normalize_email(self.email_address)
1102 email_address = self.normalize_email(self.email_address)
1102
1103
1103 # first push the email initials
1104 # first push the email initials
1104 prefix, server = email_address.split('@', 1)
1105 prefix, server = email_address.split('@', 1)
1105
1106
1106 # check if prefix is maybe a 'firstname.lastname' syntax
1107 # check if prefix is maybe a 'firstname.lastname' syntax
1107 _dot_split = prefix.rsplit('.', 1)
1108 _dot_split = prefix.rsplit('.', 1)
1108 if len(_dot_split) == 2:
1109 if len(_dot_split) == 2:
1109 initials = [_dot_split[0][0], _dot_split[1][0]]
1110 initials = [_dot_split[0][0], _dot_split[1][0]]
1110 else:
1111 else:
1111 initials = [prefix[0], server[0]]
1112 initials = [prefix[0], server[0]]
1112
1113
1113 # then try to replace either firtname or lastname
1114 # then try to replace either firtname or lastname
1114 fn_letter = (first_name or " ")[0].strip()
1115 fn_letter = (first_name or " ")[0].strip()
1115 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1116 ln_letter = (last_name.split(' ', 1)[-1] or " ")[0].strip()
1116
1117
1117 if fn_letter:
1118 if fn_letter:
1118 initials[0] = fn_letter
1119 initials[0] = fn_letter
1119
1120
1120 if ln_letter:
1121 if ln_letter:
1121 initials[1] = ln_letter
1122 initials[1] = ln_letter
1122
1123
1123 return ''.join(initials).upper()
1124 return ''.join(initials).upper()
1124
1125
1125 def get_img_data_by_type(self, font_family, img_type):
1126 def get_img_data_by_type(self, font_family, img_type):
1126 default_user = """
1127 default_user = """
1127 <svg xmlns="http://www.w3.org/2000/svg"
1128 <svg xmlns="http://www.w3.org/2000/svg"
1128 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1129 version="1.1" x="0px" y="0px" width="{size}" height="{size}"
1129 viewBox="-15 -10 439.165 429.164"
1130 viewBox="-15 -10 439.165 429.164"
1130
1131
1131 xml:space="preserve"
1132 xml:space="preserve"
1132 style="background:{background};" >
1133 style="background:{background};" >
1133
1134
1134 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1135 <path d="M204.583,216.671c50.664,0,91.74-48.075,
1135 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1136 91.74-107.378c0-82.237-41.074-107.377-91.74-107.377
1136 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1137 c-50.668,0-91.74,25.14-91.74,107.377C112.844,
1137 168.596,153.916,216.671,
1138 168.596,153.916,216.671,
1138 204.583,216.671z" fill="{text_color}"/>
1139 204.583,216.671z" fill="{text_color}"/>
1139 <path d="M407.164,374.717L360.88,
1140 <path d="M407.164,374.717L360.88,
1140 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1141 270.454c-2.117-4.771-5.836-8.728-10.465-11.138l-71.83-37.392
1141 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1142 c-1.584-0.823-3.502-0.663-4.926,0.415c-20.316,
1142 15.366-44.203,23.488-69.076,23.488c-24.877,
1143 15.366-44.203,23.488-69.076,23.488c-24.877,
1143 0-48.762-8.122-69.078-23.488
1144 0-48.762-8.122-69.078-23.488
1144 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1145 c-1.428-1.078-3.346-1.238-4.93-0.415L58.75,
1145 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1146 259.316c-4.631,2.41-8.346,6.365-10.465,11.138L2.001,374.717
1146 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1147 c-3.191,7.188-2.537,15.412,1.75,22.005c4.285,
1147 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1148 6.592,11.537,10.526,19.4,10.526h362.861c7.863,0,15.117-3.936,
1148 19.402-10.527 C409.699,390.129,
1149 19.402-10.527 C409.699,390.129,
1149 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1150 410.355,381.902,407.164,374.717z" fill="{text_color}"/>
1150 </svg>""".format(
1151 </svg>""".format(
1151 size=self.size,
1152 size=self.size,
1152 background='#979797', # @grey4
1153 background='#979797', # @grey4
1153 text_color=self.text_color,
1154 text_color=self.text_color,
1154 font_family=font_family)
1155 font_family=font_family)
1155
1156
1156 return {
1157 return {
1157 "default_user": default_user
1158 "default_user": default_user
1158 }[img_type]
1159 }[img_type]
1159
1160
1160 def get_img_data(self, svg_type=None):
1161 def get_img_data(self, svg_type=None):
1161 """
1162 """
1162 generates the svg metadata for image
1163 generates the svg metadata for image
1163 """
1164 """
1164
1165
1165 font_family = ','.join([
1166 font_family = ','.join([
1166 'proximanovaregular',
1167 'proximanovaregular',
1167 'Proxima Nova Regular',
1168 'Proxima Nova Regular',
1168 'Proxima Nova',
1169 'Proxima Nova',
1169 'Arial',
1170 'Arial',
1170 'Lucida Grande',
1171 'Lucida Grande',
1171 'sans-serif'
1172 'sans-serif'
1172 ])
1173 ])
1173 if svg_type:
1174 if svg_type:
1174 return self.get_img_data_by_type(font_family, svg_type)
1175 return self.get_img_data_by_type(font_family, svg_type)
1175
1176
1176 initials = self.get_initials()
1177 initials = self.get_initials()
1177 img_data = """
1178 img_data = """
1178 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1179 <svg xmlns="http://www.w3.org/2000/svg" pointer-events="none"
1179 width="{size}" height="{size}"
1180 width="{size}" height="{size}"
1180 style="width: 100%; height: 100%; background-color: {background}"
1181 style="width: 100%; height: 100%; background-color: {background}"
1181 viewBox="0 0 {size} {size}">
1182 viewBox="0 0 {size} {size}">
1182 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1183 <text text-anchor="middle" y="50%" x="50%" dy="0.35em"
1183 pointer-events="auto" fill="{text_color}"
1184 pointer-events="auto" fill="{text_color}"
1184 font-family="{font_family}"
1185 font-family="{font_family}"
1185 style="font-weight: 400; font-size: {f_size}px;">{text}
1186 style="font-weight: 400; font-size: {f_size}px;">{text}
1186 </text>
1187 </text>
1187 </svg>""".format(
1188 </svg>""".format(
1188 size=self.size,
1189 size=self.size,
1189 f_size=self.size/1.85, # scale the text inside the box nicely
1190 f_size=self.size/1.85, # scale the text inside the box nicely
1190 background=self.background,
1191 background=self.background,
1191 text_color=self.text_color,
1192 text_color=self.text_color,
1192 text=initials.upper(),
1193 text=initials.upper(),
1193 font_family=font_family)
1194 font_family=font_family)
1194
1195
1195 return img_data
1196 return img_data
1196
1197
1197 def generate_svg(self, svg_type=None):
1198 def generate_svg(self, svg_type=None):
1198 img_data = self.get_img_data(svg_type)
1199 img_data = self.get_img_data(svg_type)
1199 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1200 return "data:image/svg+xml;base64,%s" % img_data.encode('base64')
1200
1201
1201
1202
1202 def initials_gravatar(email_address, first_name, last_name, size=30):
1203 def initials_gravatar(email_address, first_name, last_name, size=30):
1203 svg_type = None
1204 svg_type = None
1204 if email_address == User.DEFAULT_USER_EMAIL:
1205 if email_address == User.DEFAULT_USER_EMAIL:
1205 svg_type = 'default_user'
1206 svg_type = 'default_user'
1206 klass = InitialsGravatar(email_address, first_name, last_name, size)
1207 klass = InitialsGravatar(email_address, first_name, last_name, size)
1207 return klass.generate_svg(svg_type=svg_type)
1208 return klass.generate_svg(svg_type=svg_type)
1208
1209
1209
1210
1210 def gravatar_url(email_address, size=30):
1211 def gravatar_url(email_address, size=30):
1211 # doh, we need to re-import those to mock it later
1212 # doh, we need to re-import those to mock it later
1212 from pylons import tmpl_context as c
1213 from pylons import tmpl_context as c
1213
1214
1214 _use_gravatar = c.visual.use_gravatar
1215 _use_gravatar = c.visual.use_gravatar
1215 _gravatar_url = c.visual.gravatar_url or User.DEFAULT_GRAVATAR_URL
1216 _gravatar_url = c.visual.gravatar_url or User.DEFAULT_GRAVATAR_URL
1216
1217
1217 email_address = email_address or User.DEFAULT_USER_EMAIL
1218 email_address = email_address or User.DEFAULT_USER_EMAIL
1218 if isinstance(email_address, unicode):
1219 if isinstance(email_address, unicode):
1219 # hashlib crashes on unicode items
1220 # hashlib crashes on unicode items
1220 email_address = safe_str(email_address)
1221 email_address = safe_str(email_address)
1221
1222
1222 # empty email or default user
1223 # empty email or default user
1223 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1224 if not email_address or email_address == User.DEFAULT_USER_EMAIL:
1224 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1225 return initials_gravatar(User.DEFAULT_USER_EMAIL, '', '', size=size)
1225
1226
1226 if _use_gravatar:
1227 if _use_gravatar:
1227 # TODO: Disuse pyramid thread locals. Think about another solution to
1228 # TODO: Disuse pyramid thread locals. Think about another solution to
1228 # get the host and schema here.
1229 # get the host and schema here.
1229 request = get_current_request()
1230 request = get_current_request()
1230 tmpl = safe_str(_gravatar_url)
1231 tmpl = safe_str(_gravatar_url)
1231 tmpl = tmpl.replace('{email}', email_address)\
1232 tmpl = tmpl.replace('{email}', email_address)\
1232 .replace('{md5email}', md5_safe(email_address.lower())) \
1233 .replace('{md5email}', md5_safe(email_address.lower())) \
1233 .replace('{netloc}', request.host)\
1234 .replace('{netloc}', request.host)\
1234 .replace('{scheme}', request.scheme)\
1235 .replace('{scheme}', request.scheme)\
1235 .replace('{size}', safe_str(size))
1236 .replace('{size}', safe_str(size))
1236 return tmpl
1237 return tmpl
1237 else:
1238 else:
1238 return initials_gravatar(email_address, '', '', size=size)
1239 return initials_gravatar(email_address, '', '', size=size)
1239
1240
1240
1241
1241 class Page(_Page):
1242 class Page(_Page):
1242 """
1243 """
1243 Custom pager to match rendering style with paginator
1244 Custom pager to match rendering style with paginator
1244 """
1245 """
1245
1246
1246 def _get_pos(self, cur_page, max_page, items):
1247 def _get_pos(self, cur_page, max_page, items):
1247 edge = (items / 2) + 1
1248 edge = (items / 2) + 1
1248 if (cur_page <= edge):
1249 if (cur_page <= edge):
1249 radius = max(items / 2, items - cur_page)
1250 radius = max(items / 2, items - cur_page)
1250 elif (max_page - cur_page) < edge:
1251 elif (max_page - cur_page) < edge:
1251 radius = (items - 1) - (max_page - cur_page)
1252 radius = (items - 1) - (max_page - cur_page)
1252 else:
1253 else:
1253 radius = items / 2
1254 radius = items / 2
1254
1255
1255 left = max(1, (cur_page - (radius)))
1256 left = max(1, (cur_page - (radius)))
1256 right = min(max_page, cur_page + (radius))
1257 right = min(max_page, cur_page + (radius))
1257 return left, cur_page, right
1258 return left, cur_page, right
1258
1259
1259 def _range(self, regexp_match):
1260 def _range(self, regexp_match):
1260 """
1261 """
1261 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1262 Return range of linked pages (e.g. '1 2 [3] 4 5 6 7 8').
1262
1263
1263 Arguments:
1264 Arguments:
1264
1265
1265 regexp_match
1266 regexp_match
1266 A "re" (regular expressions) match object containing the
1267 A "re" (regular expressions) match object containing the
1267 radius of linked pages around the current page in
1268 radius of linked pages around the current page in
1268 regexp_match.group(1) as a string
1269 regexp_match.group(1) as a string
1269
1270
1270 This function is supposed to be called as a callable in
1271 This function is supposed to be called as a callable in
1271 re.sub.
1272 re.sub.
1272
1273
1273 """
1274 """
1274 radius = int(regexp_match.group(1))
1275 radius = int(regexp_match.group(1))
1275
1276
1276 # Compute the first and last page number within the radius
1277 # Compute the first and last page number within the radius
1277 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1278 # e.g. '1 .. 5 6 [7] 8 9 .. 12'
1278 # -> leftmost_page = 5
1279 # -> leftmost_page = 5
1279 # -> rightmost_page = 9
1280 # -> rightmost_page = 9
1280 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1281 leftmost_page, _cur, rightmost_page = self._get_pos(self.page,
1281 self.last_page,
1282 self.last_page,
1282 (radius * 2) + 1)
1283 (radius * 2) + 1)
1283 nav_items = []
1284 nav_items = []
1284
1285
1285 # Create a link to the first page (unless we are on the first page
1286 # Create a link to the first page (unless we are on the first page
1286 # or there would be no need to insert '..' spacers)
1287 # or there would be no need to insert '..' spacers)
1287 if self.page != self.first_page and self.first_page < leftmost_page:
1288 if self.page != self.first_page and self.first_page < leftmost_page:
1288 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1289 nav_items.append(self._pagerlink(self.first_page, self.first_page))
1289
1290
1290 # Insert dots if there are pages between the first page
1291 # Insert dots if there are pages between the first page
1291 # and the currently displayed page range
1292 # and the currently displayed page range
1292 if leftmost_page - self.first_page > 1:
1293 if leftmost_page - self.first_page > 1:
1293 # Wrap in a SPAN tag if nolink_attr is set
1294 # Wrap in a SPAN tag if nolink_attr is set
1294 text = '..'
1295 text = '..'
1295 if self.dotdot_attr:
1296 if self.dotdot_attr:
1296 text = HTML.span(c=text, **self.dotdot_attr)
1297 text = HTML.span(c=text, **self.dotdot_attr)
1297 nav_items.append(text)
1298 nav_items.append(text)
1298
1299
1299 for thispage in xrange(leftmost_page, rightmost_page + 1):
1300 for thispage in xrange(leftmost_page, rightmost_page + 1):
1300 # Hilight the current page number and do not use a link
1301 # Hilight the current page number and do not use a link
1301 if thispage == self.page:
1302 if thispage == self.page:
1302 text = '%s' % (thispage,)
1303 text = '%s' % (thispage,)
1303 # Wrap in a SPAN tag if nolink_attr is set
1304 # Wrap in a SPAN tag if nolink_attr is set
1304 if self.curpage_attr:
1305 if self.curpage_attr:
1305 text = HTML.span(c=text, **self.curpage_attr)
1306 text = HTML.span(c=text, **self.curpage_attr)
1306 nav_items.append(text)
1307 nav_items.append(text)
1307 # Otherwise create just a link to that page
1308 # Otherwise create just a link to that page
1308 else:
1309 else:
1309 text = '%s' % (thispage,)
1310 text = '%s' % (thispage,)
1310 nav_items.append(self._pagerlink(thispage, text))
1311 nav_items.append(self._pagerlink(thispage, text))
1311
1312
1312 # Insert dots if there are pages between the displayed
1313 # Insert dots if there are pages between the displayed
1313 # page numbers and the end of the page range
1314 # page numbers and the end of the page range
1314 if self.last_page - rightmost_page > 1:
1315 if self.last_page - rightmost_page > 1:
1315 text = '..'
1316 text = '..'
1316 # Wrap in a SPAN tag if nolink_attr is set
1317 # Wrap in a SPAN tag if nolink_attr is set
1317 if self.dotdot_attr:
1318 if self.dotdot_attr:
1318 text = HTML.span(c=text, **self.dotdot_attr)
1319 text = HTML.span(c=text, **self.dotdot_attr)
1319 nav_items.append(text)
1320 nav_items.append(text)
1320
1321
1321 # Create a link to the very last page (unless we are on the last
1322 # Create a link to the very last page (unless we are on the last
1322 # page or there would be no need to insert '..' spacers)
1323 # page or there would be no need to insert '..' spacers)
1323 if self.page != self.last_page and rightmost_page < self.last_page:
1324 if self.page != self.last_page and rightmost_page < self.last_page:
1324 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1325 nav_items.append(self._pagerlink(self.last_page, self.last_page))
1325
1326
1326 ## prerender links
1327 ## prerender links
1327 #_page_link = url.current()
1328 #_page_link = url.current()
1328 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1329 #nav_items.append(literal('<link rel="prerender" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1329 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1330 #nav_items.append(literal('<link rel="prefetch" href="%s?page=%s">' % (_page_link, str(int(self.page)+1))))
1330 return self.separator.join(nav_items)
1331 return self.separator.join(nav_items)
1331
1332
1332 def pager(self, format='~2~', page_param='page', partial_param='partial',
1333 def pager(self, format='~2~', page_param='page', partial_param='partial',
1333 show_if_single_page=False, separator=' ', onclick=None,
1334 show_if_single_page=False, separator=' ', onclick=None,
1334 symbol_first='<<', symbol_last='>>',
1335 symbol_first='<<', symbol_last='>>',
1335 symbol_previous='<', symbol_next='>',
1336 symbol_previous='<', symbol_next='>',
1336 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1337 link_attr={'class': 'pager_link', 'rel': 'prerender'},
1337 curpage_attr={'class': 'pager_curpage'},
1338 curpage_attr={'class': 'pager_curpage'},
1338 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1339 dotdot_attr={'class': 'pager_dotdot'}, **kwargs):
1339
1340
1340 self.curpage_attr = curpage_attr
1341 self.curpage_attr = curpage_attr
1341 self.separator = separator
1342 self.separator = separator
1342 self.pager_kwargs = kwargs
1343 self.pager_kwargs = kwargs
1343 self.page_param = page_param
1344 self.page_param = page_param
1344 self.partial_param = partial_param
1345 self.partial_param = partial_param
1345 self.onclick = onclick
1346 self.onclick = onclick
1346 self.link_attr = link_attr
1347 self.link_attr = link_attr
1347 self.dotdot_attr = dotdot_attr
1348 self.dotdot_attr = dotdot_attr
1348
1349
1349 # Don't show navigator if there is no more than one page
1350 # Don't show navigator if there is no more than one page
1350 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1351 if self.page_count == 0 or (self.page_count == 1 and not show_if_single_page):
1351 return ''
1352 return ''
1352
1353
1353 from string import Template
1354 from string import Template
1354 # Replace ~...~ in token format by range of pages
1355 # Replace ~...~ in token format by range of pages
1355 result = re.sub(r'~(\d+)~', self._range, format)
1356 result = re.sub(r'~(\d+)~', self._range, format)
1356
1357
1357 # Interpolate '%' variables
1358 # Interpolate '%' variables
1358 result = Template(result).safe_substitute({
1359 result = Template(result).safe_substitute({
1359 'first_page': self.first_page,
1360 'first_page': self.first_page,
1360 'last_page': self.last_page,
1361 'last_page': self.last_page,
1361 'page': self.page,
1362 'page': self.page,
1362 'page_count': self.page_count,
1363 'page_count': self.page_count,
1363 'items_per_page': self.items_per_page,
1364 'items_per_page': self.items_per_page,
1364 'first_item': self.first_item,
1365 'first_item': self.first_item,
1365 'last_item': self.last_item,
1366 'last_item': self.last_item,
1366 'item_count': self.item_count,
1367 'item_count': self.item_count,
1367 'link_first': self.page > self.first_page and \
1368 'link_first': self.page > self.first_page and \
1368 self._pagerlink(self.first_page, symbol_first) or '',
1369 self._pagerlink(self.first_page, symbol_first) or '',
1369 'link_last': self.page < self.last_page and \
1370 'link_last': self.page < self.last_page and \
1370 self._pagerlink(self.last_page, symbol_last) or '',
1371 self._pagerlink(self.last_page, symbol_last) or '',
1371 'link_previous': self.previous_page and \
1372 'link_previous': self.previous_page and \
1372 self._pagerlink(self.previous_page, symbol_previous) \
1373 self._pagerlink(self.previous_page, symbol_previous) \
1373 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1374 or HTML.span(symbol_previous, class_="pg-previous disabled"),
1374 'link_next': self.next_page and \
1375 'link_next': self.next_page and \
1375 self._pagerlink(self.next_page, symbol_next) \
1376 self._pagerlink(self.next_page, symbol_next) \
1376 or HTML.span(symbol_next, class_="pg-next disabled")
1377 or HTML.span(symbol_next, class_="pg-next disabled")
1377 })
1378 })
1378
1379
1379 return literal(result)
1380 return literal(result)
1380
1381
1381
1382
1382 #==============================================================================
1383 #==============================================================================
1383 # REPO PAGER, PAGER FOR REPOSITORY
1384 # REPO PAGER, PAGER FOR REPOSITORY
1384 #==============================================================================
1385 #==============================================================================
1385 class RepoPage(Page):
1386 class RepoPage(Page):
1386
1387
1387 def __init__(self, collection, page=1, items_per_page=20,
1388 def __init__(self, collection, page=1, items_per_page=20,
1388 item_count=None, url=None, **kwargs):
1389 item_count=None, url=None, **kwargs):
1389
1390
1390 """Create a "RepoPage" instance. special pager for paging
1391 """Create a "RepoPage" instance. special pager for paging
1391 repository
1392 repository
1392 """
1393 """
1393 self._url_generator = url
1394 self._url_generator = url
1394
1395
1395 # Safe the kwargs class-wide so they can be used in the pager() method
1396 # Safe the kwargs class-wide so they can be used in the pager() method
1396 self.kwargs = kwargs
1397 self.kwargs = kwargs
1397
1398
1398 # Save a reference to the collection
1399 # Save a reference to the collection
1399 self.original_collection = collection
1400 self.original_collection = collection
1400
1401
1401 self.collection = collection
1402 self.collection = collection
1402
1403
1403 # The self.page is the number of the current page.
1404 # The self.page is the number of the current page.
1404 # The first page has the number 1!
1405 # The first page has the number 1!
1405 try:
1406 try:
1406 self.page = int(page) # make it int() if we get it as a string
1407 self.page = int(page) # make it int() if we get it as a string
1407 except (ValueError, TypeError):
1408 except (ValueError, TypeError):
1408 self.page = 1
1409 self.page = 1
1409
1410
1410 self.items_per_page = items_per_page
1411 self.items_per_page = items_per_page
1411
1412
1412 # Unless the user tells us how many items the collections has
1413 # Unless the user tells us how many items the collections has
1413 # we calculate that ourselves.
1414 # we calculate that ourselves.
1414 if item_count is not None:
1415 if item_count is not None:
1415 self.item_count = item_count
1416 self.item_count = item_count
1416 else:
1417 else:
1417 self.item_count = len(self.collection)
1418 self.item_count = len(self.collection)
1418
1419
1419 # Compute the number of the first and last available page
1420 # Compute the number of the first and last available page
1420 if self.item_count > 0:
1421 if self.item_count > 0:
1421 self.first_page = 1
1422 self.first_page = 1
1422 self.page_count = int(math.ceil(float(self.item_count) /
1423 self.page_count = int(math.ceil(float(self.item_count) /
1423 self.items_per_page))
1424 self.items_per_page))
1424 self.last_page = self.first_page + self.page_count - 1
1425 self.last_page = self.first_page + self.page_count - 1
1425
1426
1426 # Make sure that the requested page number is the range of
1427 # Make sure that the requested page number is the range of
1427 # valid pages
1428 # valid pages
1428 if self.page > self.last_page:
1429 if self.page > self.last_page:
1429 self.page = self.last_page
1430 self.page = self.last_page
1430 elif self.page < self.first_page:
1431 elif self.page < self.first_page:
1431 self.page = self.first_page
1432 self.page = self.first_page
1432
1433
1433 # Note: the number of items on this page can be less than
1434 # Note: the number of items on this page can be less than
1434 # items_per_page if the last page is not full
1435 # items_per_page if the last page is not full
1435 self.first_item = max(0, (self.item_count) - (self.page *
1436 self.first_item = max(0, (self.item_count) - (self.page *
1436 items_per_page))
1437 items_per_page))
1437 self.last_item = ((self.item_count - 1) - items_per_page *
1438 self.last_item = ((self.item_count - 1) - items_per_page *
1438 (self.page - 1))
1439 (self.page - 1))
1439
1440
1440 self.items = list(self.collection[self.first_item:self.last_item + 1])
1441 self.items = list(self.collection[self.first_item:self.last_item + 1])
1441
1442
1442 # Links to previous and next page
1443 # Links to previous and next page
1443 if self.page > self.first_page:
1444 if self.page > self.first_page:
1444 self.previous_page = self.page - 1
1445 self.previous_page = self.page - 1
1445 else:
1446 else:
1446 self.previous_page = None
1447 self.previous_page = None
1447
1448
1448 if self.page < self.last_page:
1449 if self.page < self.last_page:
1449 self.next_page = self.page + 1
1450 self.next_page = self.page + 1
1450 else:
1451 else:
1451 self.next_page = None
1452 self.next_page = None
1452
1453
1453 # No items available
1454 # No items available
1454 else:
1455 else:
1455 self.first_page = None
1456 self.first_page = None
1456 self.page_count = 0
1457 self.page_count = 0
1457 self.last_page = None
1458 self.last_page = None
1458 self.first_item = None
1459 self.first_item = None
1459 self.last_item = None
1460 self.last_item = None
1460 self.previous_page = None
1461 self.previous_page = None
1461 self.next_page = None
1462 self.next_page = None
1462 self.items = []
1463 self.items = []
1463
1464
1464 # This is a subclass of the 'list' type. Initialise the list now.
1465 # This is a subclass of the 'list' type. Initialise the list now.
1465 list.__init__(self, reversed(self.items))
1466 list.__init__(self, reversed(self.items))
1466
1467
1467
1468
1468 def changed_tooltip(nodes):
1469 def changed_tooltip(nodes):
1469 """
1470 """
1470 Generates a html string for changed nodes in commit page.
1471 Generates a html string for changed nodes in commit page.
1471 It limits the output to 30 entries
1472 It limits the output to 30 entries
1472
1473
1473 :param nodes: LazyNodesGenerator
1474 :param nodes: LazyNodesGenerator
1474 """
1475 """
1475 if nodes:
1476 if nodes:
1476 pref = ': <br/> '
1477 pref = ': <br/> '
1477 suf = ''
1478 suf = ''
1478 if len(nodes) > 30:
1479 if len(nodes) > 30:
1479 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
1480 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
1480 return literal(pref + '<br/> '.join([safe_unicode(x.path)
1481 return literal(pref + '<br/> '.join([safe_unicode(x.path)
1481 for x in nodes[:30]]) + suf)
1482 for x in nodes[:30]]) + suf)
1482 else:
1483 else:
1483 return ': ' + _('No Files')
1484 return ': ' + _('No Files')
1484
1485
1485
1486
1486 def breadcrumb_repo_link(repo):
1487 def breadcrumb_repo_link(repo):
1487 """
1488 """
1488 Makes a breadcrumbs path link to repo
1489 Makes a breadcrumbs path link to repo
1489
1490
1490 ex::
1491 ex::
1491 group >> subgroup >> repo
1492 group >> subgroup >> repo
1492
1493
1493 :param repo: a Repository instance
1494 :param repo: a Repository instance
1494 """
1495 """
1495
1496
1496 path = [
1497 path = [
1497 link_to(group.name, url('repo_group_home', group_name=group.group_name))
1498 link_to(group.name, url('repo_group_home', group_name=group.group_name))
1498 for group in repo.groups_with_parents
1499 for group in repo.groups_with_parents
1499 ] + [
1500 ] + [
1500 link_to(repo.just_name, url('summary_home', repo_name=repo.repo_name))
1501 link_to(repo.just_name, url('summary_home', repo_name=repo.repo_name))
1501 ]
1502 ]
1502
1503
1503 return literal(' &raquo; '.join(path))
1504 return literal(' &raquo; '.join(path))
1504
1505
1505
1506
1506 def format_byte_size_binary(file_size):
1507 def format_byte_size_binary(file_size):
1507 """
1508 """
1508 Formats file/folder sizes to standard.
1509 Formats file/folder sizes to standard.
1509 """
1510 """
1510 formatted_size = format_byte_size(file_size, binary=True)
1511 formatted_size = format_byte_size(file_size, binary=True)
1511 return formatted_size
1512 return formatted_size
1512
1513
1513
1514
1514 def fancy_file_stats(stats):
1515 def fancy_file_stats(stats):
1515 """
1516 """
1516 Displays a fancy two colored bar for number of added/deleted
1517 Displays a fancy two colored bar for number of added/deleted
1517 lines of code on file
1518 lines of code on file
1518
1519
1519 :param stats: two element list of added/deleted lines of code
1520 :param stats: two element list of added/deleted lines of code
1520 """
1521 """
1521 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
1522 from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
1522 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
1523 MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE
1523
1524
1524 def cgen(l_type, a_v, d_v):
1525 def cgen(l_type, a_v, d_v):
1525 mapping = {'tr': 'top-right-rounded-corner-mid',
1526 mapping = {'tr': 'top-right-rounded-corner-mid',
1526 'tl': 'top-left-rounded-corner-mid',
1527 'tl': 'top-left-rounded-corner-mid',
1527 'br': 'bottom-right-rounded-corner-mid',
1528 'br': 'bottom-right-rounded-corner-mid',
1528 'bl': 'bottom-left-rounded-corner-mid'}
1529 'bl': 'bottom-left-rounded-corner-mid'}
1529 map_getter = lambda x: mapping[x]
1530 map_getter = lambda x: mapping[x]
1530
1531
1531 if l_type == 'a' and d_v:
1532 if l_type == 'a' and d_v:
1532 #case when added and deleted are present
1533 #case when added and deleted are present
1533 return ' '.join(map(map_getter, ['tl', 'bl']))
1534 return ' '.join(map(map_getter, ['tl', 'bl']))
1534
1535
1535 if l_type == 'a' and not d_v:
1536 if l_type == 'a' and not d_v:
1536 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1537 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1537
1538
1538 if l_type == 'd' and a_v:
1539 if l_type == 'd' and a_v:
1539 return ' '.join(map(map_getter, ['tr', 'br']))
1540 return ' '.join(map(map_getter, ['tr', 'br']))
1540
1541
1541 if l_type == 'd' and not a_v:
1542 if l_type == 'd' and not a_v:
1542 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1543 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
1543
1544
1544 a, d = stats['added'], stats['deleted']
1545 a, d = stats['added'], stats['deleted']
1545 width = 100
1546 width = 100
1546
1547
1547 if stats['binary']: # binary operations like chmod/rename etc
1548 if stats['binary']: # binary operations like chmod/rename etc
1548 lbl = []
1549 lbl = []
1549 bin_op = 0 # undefined
1550 bin_op = 0 # undefined
1550
1551
1551 # prefix with bin for binary files
1552 # prefix with bin for binary files
1552 if BIN_FILENODE in stats['ops']:
1553 if BIN_FILENODE in stats['ops']:
1553 lbl += ['bin']
1554 lbl += ['bin']
1554
1555
1555 if NEW_FILENODE in stats['ops']:
1556 if NEW_FILENODE in stats['ops']:
1556 lbl += [_('new file')]
1557 lbl += [_('new file')]
1557 bin_op = NEW_FILENODE
1558 bin_op = NEW_FILENODE
1558 elif MOD_FILENODE in stats['ops']:
1559 elif MOD_FILENODE in stats['ops']:
1559 lbl += [_('mod')]
1560 lbl += [_('mod')]
1560 bin_op = MOD_FILENODE
1561 bin_op = MOD_FILENODE
1561 elif DEL_FILENODE in stats['ops']:
1562 elif DEL_FILENODE in stats['ops']:
1562 lbl += [_('del')]
1563 lbl += [_('del')]
1563 bin_op = DEL_FILENODE
1564 bin_op = DEL_FILENODE
1564 elif RENAMED_FILENODE in stats['ops']:
1565 elif RENAMED_FILENODE in stats['ops']:
1565 lbl += [_('rename')]
1566 lbl += [_('rename')]
1566 bin_op = RENAMED_FILENODE
1567 bin_op = RENAMED_FILENODE
1567
1568
1568 # chmod can go with other operations, so we add a + to lbl if needed
1569 # chmod can go with other operations, so we add a + to lbl if needed
1569 if CHMOD_FILENODE in stats['ops']:
1570 if CHMOD_FILENODE in stats['ops']:
1570 lbl += [_('chmod')]
1571 lbl += [_('chmod')]
1571 if bin_op == 0:
1572 if bin_op == 0:
1572 bin_op = CHMOD_FILENODE
1573 bin_op = CHMOD_FILENODE
1573
1574
1574 lbl = '+'.join(lbl)
1575 lbl = '+'.join(lbl)
1575 b_a = '<div class="bin bin%s %s" style="width:100%%">%s</div>' \
1576 b_a = '<div class="bin bin%s %s" style="width:100%%">%s</div>' \
1576 % (bin_op, cgen('a', a_v='', d_v=0), lbl)
1577 % (bin_op, cgen('a', a_v='', d_v=0), lbl)
1577 b_d = '<div class="bin bin1" style="width:0%%"></div>'
1578 b_d = '<div class="bin bin1" style="width:0%%"></div>'
1578 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
1579 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
1579
1580
1580 t = stats['added'] + stats['deleted']
1581 t = stats['added'] + stats['deleted']
1581 unit = float(width) / (t or 1)
1582 unit = float(width) / (t or 1)
1582
1583
1583 # needs > 9% of width to be visible or 0 to be hidden
1584 # needs > 9% of width to be visible or 0 to be hidden
1584 a_p = max(9, unit * a) if a > 0 else 0
1585 a_p = max(9, unit * a) if a > 0 else 0
1585 d_p = max(9, unit * d) if d > 0 else 0
1586 d_p = max(9, unit * d) if d > 0 else 0
1586 p_sum = a_p + d_p
1587 p_sum = a_p + d_p
1587
1588
1588 if p_sum > width:
1589 if p_sum > width:
1589 #adjust the percentage to be == 100% since we adjusted to 9
1590 #adjust the percentage to be == 100% since we adjusted to 9
1590 if a_p > d_p:
1591 if a_p > d_p:
1591 a_p = a_p - (p_sum - width)
1592 a_p = a_p - (p_sum - width)
1592 else:
1593 else:
1593 d_p = d_p - (p_sum - width)
1594 d_p = d_p - (p_sum - width)
1594
1595
1595 a_v = a if a > 0 else ''
1596 a_v = a if a > 0 else ''
1596 d_v = d if d > 0 else ''
1597 d_v = d if d > 0 else ''
1597
1598
1598 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
1599 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
1599 cgen('a', a_v, d_v), a_p, a_v
1600 cgen('a', a_v, d_v), a_p, a_v
1600 )
1601 )
1601 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
1602 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
1602 cgen('d', a_v, d_v), d_p, d_v
1603 cgen('d', a_v, d_v), d_p, d_v
1603 )
1604 )
1604 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
1605 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
1605
1606
1606
1607
1607 def urlify_text(text_, safe=True):
1608 def urlify_text(text_, safe=True):
1608 """
1609 """
1609 Extrac urls from text and make html links out of them
1610 Extrac urls from text and make html links out of them
1610
1611
1611 :param text_:
1612 :param text_:
1612 """
1613 """
1613
1614
1614 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1615 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@#.&+]'''
1615 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1616 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
1616
1617
1617 def url_func(match_obj):
1618 def url_func(match_obj):
1618 url_full = match_obj.groups()[0]
1619 url_full = match_obj.groups()[0]
1619 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1620 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
1620 _newtext = url_pat.sub(url_func, text_)
1621 _newtext = url_pat.sub(url_func, text_)
1621 if safe:
1622 if safe:
1622 return literal(_newtext)
1623 return literal(_newtext)
1623 return _newtext
1624 return _newtext
1624
1625
1625
1626
1626 def urlify_commits(text_, repository):
1627 def urlify_commits(text_, repository):
1627 """
1628 """
1628 Extract commit ids from text and make link from them
1629 Extract commit ids from text and make link from them
1629
1630
1630 :param text_:
1631 :param text_:
1631 :param repository: repo name to build the URL with
1632 :param repository: repo name to build the URL with
1632 """
1633 """
1633 from pylons import url # doh, we need to re-import url to mock it later
1634 from pylons import url # doh, we need to re-import url to mock it later
1634 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1635 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1635
1636
1636 def url_func(match_obj):
1637 def url_func(match_obj):
1637 commit_id = match_obj.groups()[1]
1638 commit_id = match_obj.groups()[1]
1638 pref = match_obj.groups()[0]
1639 pref = match_obj.groups()[0]
1639 suf = match_obj.groups()[2]
1640 suf = match_obj.groups()[2]
1640
1641
1641 tmpl = (
1642 tmpl = (
1642 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1643 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1643 '%(commit_id)s</a>%(suf)s'
1644 '%(commit_id)s</a>%(suf)s'
1644 )
1645 )
1645 return tmpl % {
1646 return tmpl % {
1646 'pref': pref,
1647 'pref': pref,
1647 'cls': 'revision-link',
1648 'cls': 'revision-link',
1648 'url': url('changeset_home', repo_name=repository,
1649 'url': url('changeset_home', repo_name=repository,
1649 revision=commit_id, qualified=True),
1650 revision=commit_id, qualified=True),
1650 'commit_id': commit_id,
1651 'commit_id': commit_id,
1651 'suf': suf
1652 'suf': suf
1652 }
1653 }
1653
1654
1654 newtext = URL_PAT.sub(url_func, text_)
1655 newtext = URL_PAT.sub(url_func, text_)
1655
1656
1656 return newtext
1657 return newtext
1657
1658
1658
1659
1659 def _process_url_func(match_obj, repo_name, uid, entry,
1660 def _process_url_func(match_obj, repo_name, uid, entry,
1660 return_raw_data=False):
1661 return_raw_data=False):
1661 pref = ''
1662 pref = ''
1662 if match_obj.group().startswith(' '):
1663 if match_obj.group().startswith(' '):
1663 pref = ' '
1664 pref = ' '
1664
1665
1665 issue_id = ''.join(match_obj.groups())
1666 issue_id = ''.join(match_obj.groups())
1666 tmpl = (
1667 tmpl = (
1667 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1668 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1668 '%(issue-prefix)s%(id-repr)s'
1669 '%(issue-prefix)s%(id-repr)s'
1669 '</a>')
1670 '</a>')
1670
1671
1671 (repo_name_cleaned,
1672 (repo_name_cleaned,
1672 parent_group_name) = RepoGroupModel().\
1673 parent_group_name) = RepoGroupModel().\
1673 _get_group_name_and_parent(repo_name)
1674 _get_group_name_and_parent(repo_name)
1674
1675
1675 # variables replacement
1676 # variables replacement
1676 named_vars = {
1677 named_vars = {
1677 'id': issue_id,
1678 'id': issue_id,
1678 'repo': repo_name,
1679 'repo': repo_name,
1679 'repo_name': repo_name_cleaned,
1680 'repo_name': repo_name_cleaned,
1680 'group_name': parent_group_name
1681 'group_name': parent_group_name
1681 }
1682 }
1682 # named regex variables
1683 # named regex variables
1683 named_vars.update(match_obj.groupdict())
1684 named_vars.update(match_obj.groupdict())
1684 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1685 _url = string.Template(entry['url']).safe_substitute(**named_vars)
1685
1686
1686 data = {
1687 data = {
1687 'pref': pref,
1688 'pref': pref,
1688 'cls': 'issue-tracker-link',
1689 'cls': 'issue-tracker-link',
1689 'url': _url,
1690 'url': _url,
1690 'id-repr': issue_id,
1691 'id-repr': issue_id,
1691 'issue-prefix': entry['pref'],
1692 'issue-prefix': entry['pref'],
1692 'serv': entry['url'],
1693 'serv': entry['url'],
1693 }
1694 }
1694 if return_raw_data:
1695 if return_raw_data:
1695 return {
1696 return {
1696 'id': issue_id,
1697 'id': issue_id,
1697 'url': _url
1698 'url': _url
1698 }
1699 }
1699 return tmpl % data
1700 return tmpl % data
1700
1701
1701
1702
1702 def process_patterns(text_string, repo_name, config=None):
1703 def process_patterns(text_string, repo_name, config=None):
1703 repo = None
1704 repo = None
1704 if repo_name:
1705 if repo_name:
1705 # Retrieving repo_name to avoid invalid repo_name to explode on
1706 # Retrieving repo_name to avoid invalid repo_name to explode on
1706 # IssueTrackerSettingsModel but still passing invalid name further down
1707 # IssueTrackerSettingsModel but still passing invalid name further down
1707 repo = Repository.get_by_repo_name(repo_name, cache=True)
1708 repo = Repository.get_by_repo_name(repo_name, cache=True)
1708
1709
1709 settings_model = IssueTrackerSettingsModel(repo=repo)
1710 settings_model = IssueTrackerSettingsModel(repo=repo)
1710 active_entries = settings_model.get_settings(cache=True)
1711 active_entries = settings_model.get_settings(cache=True)
1711
1712
1712 issues_data = []
1713 issues_data = []
1713 newtext = text_string
1714 newtext = text_string
1714 for uid, entry in active_entries.items():
1715 for uid, entry in active_entries.items():
1715 log.debug('found issue tracker entry with uid %s' % (uid,))
1716 log.debug('found issue tracker entry with uid %s' % (uid,))
1716
1717
1717 if not (entry['pat'] and entry['url']):
1718 if not (entry['pat'] and entry['url']):
1718 log.debug('skipping due to missing data')
1719 log.debug('skipping due to missing data')
1719 continue
1720 continue
1720
1721
1721 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s'
1722 log.debug('issue tracker entry: uid: `%s` PAT:%s URL:%s PREFIX:%s'
1722 % (uid, entry['pat'], entry['url'], entry['pref']))
1723 % (uid, entry['pat'], entry['url'], entry['pref']))
1723
1724
1724 try:
1725 try:
1725 pattern = re.compile(r'%s' % entry['pat'])
1726 pattern = re.compile(r'%s' % entry['pat'])
1726 except re.error:
1727 except re.error:
1727 log.exception(
1728 log.exception(
1728 'issue tracker pattern: `%s` failed to compile',
1729 'issue tracker pattern: `%s` failed to compile',
1729 entry['pat'])
1730 entry['pat'])
1730 continue
1731 continue
1731
1732
1732 data_func = partial(
1733 data_func = partial(
1733 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1734 _process_url_func, repo_name=repo_name, entry=entry, uid=uid,
1734 return_raw_data=True)
1735 return_raw_data=True)
1735
1736
1736 for match_obj in pattern.finditer(text_string):
1737 for match_obj in pattern.finditer(text_string):
1737 issues_data.append(data_func(match_obj))
1738 issues_data.append(data_func(match_obj))
1738
1739
1739 url_func = partial(
1740 url_func = partial(
1740 _process_url_func, repo_name=repo_name, entry=entry, uid=uid)
1741 _process_url_func, repo_name=repo_name, entry=entry, uid=uid)
1741
1742
1742 newtext = pattern.sub(url_func, newtext)
1743 newtext = pattern.sub(url_func, newtext)
1743 log.debug('processed prefix:uid `%s`' % (uid,))
1744 log.debug('processed prefix:uid `%s`' % (uid,))
1744
1745
1745 return newtext, issues_data
1746 return newtext, issues_data
1746
1747
1747
1748
1748 def urlify_commit_message(commit_text, repository=None):
1749 def urlify_commit_message(commit_text, repository=None):
1749 """
1750 """
1750 Parses given text message and makes proper links.
1751 Parses given text message and makes proper links.
1751 issues are linked to given issue-server, and rest is a commit link
1752 issues are linked to given issue-server, and rest is a commit link
1752
1753
1753 :param commit_text:
1754 :param commit_text:
1754 :param repository:
1755 :param repository:
1755 """
1756 """
1756 from pylons import url # doh, we need to re-import url to mock it later
1757 from pylons import url # doh, we need to re-import url to mock it later
1757
1758
1758 def escaper(string):
1759 def escaper(string):
1759 return string.replace('<', '&lt;').replace('>', '&gt;')
1760 return string.replace('<', '&lt;').replace('>', '&gt;')
1760
1761
1761 newtext = escaper(commit_text)
1762 newtext = escaper(commit_text)
1762
1763
1763 # extract http/https links and make them real urls
1764 # extract http/https links and make them real urls
1764 newtext = urlify_text(newtext, safe=False)
1765 newtext = urlify_text(newtext, safe=False)
1765
1766
1766 # urlify commits - extract commit ids and make link out of them, if we have
1767 # urlify commits - extract commit ids and make link out of them, if we have
1767 # the scope of repository present.
1768 # the scope of repository present.
1768 if repository:
1769 if repository:
1769 newtext = urlify_commits(newtext, repository)
1770 newtext = urlify_commits(newtext, repository)
1770
1771
1771 # process issue tracker patterns
1772 # process issue tracker patterns
1772 newtext, issues = process_patterns(newtext, repository or '')
1773 newtext, issues = process_patterns(newtext, repository or '')
1773
1774
1774 return literal(newtext)
1775 return literal(newtext)
1775
1776
1776
1777
1777 def rst(source, mentions=False):
1778 def rst(source, mentions=False):
1778 return literal('<div class="rst-block">%s</div>' %
1779 return literal('<div class="rst-block">%s</div>' %
1779 MarkupRenderer.rst(source, mentions=mentions))
1780 MarkupRenderer.rst(source, mentions=mentions))
1780
1781
1781
1782
1782 def markdown(source, mentions=False):
1783 def markdown(source, mentions=False):
1783 return literal('<div class="markdown-block">%s</div>' %
1784 return literal('<div class="markdown-block">%s</div>' %
1784 MarkupRenderer.markdown(source, flavored=True,
1785 MarkupRenderer.markdown(source, flavored=True,
1785 mentions=mentions))
1786 mentions=mentions))
1786
1787
1787 def renderer_from_filename(filename, exclude=None):
1788 def renderer_from_filename(filename, exclude=None):
1788 return MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1789 return MarkupRenderer.renderer_from_filename(filename, exclude=exclude)
1789
1790
1790
1791
1791 def render(source, renderer='rst', mentions=False):
1792 def render(source, renderer='rst', mentions=False):
1792 if renderer == 'rst':
1793 if renderer == 'rst':
1793 return rst(source, mentions=mentions)
1794 return rst(source, mentions=mentions)
1794 if renderer == 'markdown':
1795 if renderer == 'markdown':
1795 return markdown(source, mentions=mentions)
1796 return markdown(source, mentions=mentions)
1796
1797
1797
1798
1798 def commit_status(repo, commit_id):
1799 def commit_status(repo, commit_id):
1799 return ChangesetStatusModel().get_status(repo, commit_id)
1800 return ChangesetStatusModel().get_status(repo, commit_id)
1800
1801
1801
1802
1802 def commit_status_lbl(commit_status):
1803 def commit_status_lbl(commit_status):
1803 return dict(ChangesetStatus.STATUSES).get(commit_status)
1804 return dict(ChangesetStatus.STATUSES).get(commit_status)
1804
1805
1805
1806
1806 def commit_time(repo_name, commit_id):
1807 def commit_time(repo_name, commit_id):
1807 repo = Repository.get_by_repo_name(repo_name)
1808 repo = Repository.get_by_repo_name(repo_name)
1808 commit = repo.get_commit(commit_id=commit_id)
1809 commit = repo.get_commit(commit_id=commit_id)
1809 return commit.date
1810 return commit.date
1810
1811
1811
1812
1812 def get_permission_name(key):
1813 def get_permission_name(key):
1813 return dict(Permission.PERMS).get(key)
1814 return dict(Permission.PERMS).get(key)
1814
1815
1815
1816
1816 def journal_filter_help():
1817 def journal_filter_help():
1817 return _(
1818 return _(
1818 'Example filter terms:\n' +
1819 'Example filter terms:\n' +
1819 ' repository:vcs\n' +
1820 ' repository:vcs\n' +
1820 ' username:marcin\n' +
1821 ' username:marcin\n' +
1821 ' action:*push*\n' +
1822 ' action:*push*\n' +
1822 ' ip:127.0.0.1\n' +
1823 ' ip:127.0.0.1\n' +
1823 ' date:20120101\n' +
1824 ' date:20120101\n' +
1824 ' date:[20120101100000 TO 20120102]\n' +
1825 ' date:[20120101100000 TO 20120102]\n' +
1825 '\n' +
1826 '\n' +
1826 'Generate wildcards using \'*\' character:\n' +
1827 'Generate wildcards using \'*\' character:\n' +
1827 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1828 ' "repository:vcs*" - search everything starting with \'vcs\'\n' +
1828 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1829 ' "repository:*vcs*" - search for repository containing \'vcs\'\n' +
1829 '\n' +
1830 '\n' +
1830 'Optional AND / OR operators in queries\n' +
1831 'Optional AND / OR operators in queries\n' +
1831 ' "repository:vcs OR repository:test"\n' +
1832 ' "repository:vcs OR repository:test"\n' +
1832 ' "username:test AND repository:test*"\n'
1833 ' "username:test AND repository:test*"\n'
1833 )
1834 )
1834
1835
1835
1836
1836 def not_mapped_error(repo_name):
1837 def not_mapped_error(repo_name):
1837 flash(_('%s repository is not mapped to db perhaps'
1838 flash(_('%s repository is not mapped to db perhaps'
1838 ' it was created or renamed from the filesystem'
1839 ' it was created or renamed from the filesystem'
1839 ' please run the application again'
1840 ' please run the application again'
1840 ' in order to rescan repositories') % repo_name, category='error')
1841 ' in order to rescan repositories') % repo_name, category='error')
1841
1842
1842
1843
1843 def ip_range(ip_addr):
1844 def ip_range(ip_addr):
1844 from rhodecode.model.db import UserIpMap
1845 from rhodecode.model.db import UserIpMap
1845 s, e = UserIpMap._get_ip_range(ip_addr)
1846 s, e = UserIpMap._get_ip_range(ip_addr)
1846 return '%s - %s' % (s, e)
1847 return '%s - %s' % (s, e)
1847
1848
1848
1849
1849 def form(url, method='post', needs_csrf_token=True, **attrs):
1850 def form(url, method='post', needs_csrf_token=True, **attrs):
1850 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1851 """Wrapper around webhelpers.tags.form to prevent CSRF attacks."""
1851 if method.lower() != 'get' and needs_csrf_token:
1852 if method.lower() != 'get' and needs_csrf_token:
1852 raise Exception(
1853 raise Exception(
1853 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1854 'Forms to POST/PUT/DELETE endpoints should have (in general) a ' +
1854 'CSRF token. If the endpoint does not require such token you can ' +
1855 'CSRF token. If the endpoint does not require such token you can ' +
1855 'explicitly set the parameter needs_csrf_token to false.')
1856 'explicitly set the parameter needs_csrf_token to false.')
1856
1857
1857 return wh_form(url, method=method, **attrs)
1858 return wh_form(url, method=method, **attrs)
1858
1859
1859
1860
1860 def secure_form(url, method="POST", multipart=False, **attrs):
1861 def secure_form(url, method="POST", multipart=False, **attrs):
1861 """Start a form tag that points the action to an url. This
1862 """Start a form tag that points the action to an url. This
1862 form tag will also include the hidden field containing
1863 form tag will also include the hidden field containing
1863 the auth token.
1864 the auth token.
1864
1865
1865 The url options should be given either as a string, or as a
1866 The url options should be given either as a string, or as a
1866 ``url()`` function. The method for the form defaults to POST.
1867 ``url()`` function. The method for the form defaults to POST.
1867
1868
1868 Options:
1869 Options:
1869
1870
1870 ``multipart``
1871 ``multipart``
1871 If set to True, the enctype is set to "multipart/form-data".
1872 If set to True, the enctype is set to "multipart/form-data".
1872 ``method``
1873 ``method``
1873 The method to use when submitting the form, usually either
1874 The method to use when submitting the form, usually either
1874 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1875 "GET" or "POST". If "PUT", "DELETE", or another verb is used, a
1875 hidden input with name _method is added to simulate the verb
1876 hidden input with name _method is added to simulate the verb
1876 over POST.
1877 over POST.
1877
1878
1878 """
1879 """
1879 from webhelpers.pylonslib.secure_form import insecure_form
1880 from webhelpers.pylonslib.secure_form import insecure_form
1880 from rhodecode.lib.auth import get_csrf_token, csrf_token_key
1881 form = insecure_form(url, method, multipart, **attrs)
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 return literal("%s\n%s" % (form, token))
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 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1890 def dropdownmenu(name, selected, options, enable_filter=False, **attrs):
1886 select_html = select(name, selected, options, **attrs)
1891 select_html = select(name, selected, options, **attrs)
1887 select2 = """
1892 select2 = """
1888 <script>
1893 <script>
1889 $(document).ready(function() {
1894 $(document).ready(function() {
1890 $('#%s').select2({
1895 $('#%s').select2({
1891 containerCssClass: 'drop-menu',
1896 containerCssClass: 'drop-menu',
1892 dropdownCssClass: 'drop-menu-dropdown',
1897 dropdownCssClass: 'drop-menu-dropdown',
1893 dropdownAutoWidth: true%s
1898 dropdownAutoWidth: true%s
1894 });
1899 });
1895 });
1900 });
1896 </script>
1901 </script>
1897 """
1902 """
1898 filter_option = """,
1903 filter_option = """,
1899 minimumResultsForSearch: -1
1904 minimumResultsForSearch: -1
1900 """
1905 """
1901 input_id = attrs.get('id') or name
1906 input_id = attrs.get('id') or name
1902 filter_enabled = "" if enable_filter else filter_option
1907 filter_enabled = "" if enable_filter else filter_option
1903 select_script = literal(select2 % (input_id, filter_enabled))
1908 select_script = literal(select2 % (input_id, filter_enabled))
1904
1909
1905 return literal(select_html+select_script)
1910 return literal(select_html+select_script)
1906
1911
1907
1912
1908 def get_visual_attr(tmpl_context_var, attr_name):
1913 def get_visual_attr(tmpl_context_var, attr_name):
1909 """
1914 """
1910 A safe way to get a variable from visual variable of template context
1915 A safe way to get a variable from visual variable of template context
1911
1916
1912 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
1917 :param tmpl_context_var: instance of tmpl_context, usually present as `c`
1913 :param attr_name: name of the attribute we fetch from the c.visual
1918 :param attr_name: name of the attribute we fetch from the c.visual
1914 """
1919 """
1915 visual = getattr(tmpl_context_var, 'visual', None)
1920 visual = getattr(tmpl_context_var, 'visual', None)
1916 if not visual:
1921 if not visual:
1917 return
1922 return
1918 else:
1923 else:
1919 return getattr(visual, attr_name, None)
1924 return getattr(visual, attr_name, None)
1920
1925
1921
1926
1922 def get_last_path_part(file_node):
1927 def get_last_path_part(file_node):
1923 if not file_node.path:
1928 if not file_node.path:
1924 return u''
1929 return u''
1925
1930
1926 path = safe_unicode(file_node.path.split('/')[-1])
1931 path = safe_unicode(file_node.path.split('/')[-1])
1927 return u'../' + path
1932 return u'../' + path
1928
1933
1929
1934
1930 def route_path(*args, **kwds):
1935 def route_path(*args, **kwds):
1931 """
1936 """
1932 Wrapper around pyramids `route_path` function. It is used to generate
1937 Wrapper around pyramids `route_path` function. It is used to generate
1933 URLs from within pylons views or templates. This will be removed when
1938 URLs from within pylons views or templates. This will be removed when
1934 pyramid migration if finished.
1939 pyramid migration if finished.
1935 """
1940 """
1936 req = get_current_request()
1941 req = get_current_request()
1937 return req.route_path(*args, **kwds)
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 def resource_path(*args, **kwds):
1955 def resource_path(*args, **kwds):
1941 """
1956 """
1942 Wrapper around pyramids `route_path` function. It is used to generate
1957 Wrapper around pyramids `route_path` function. It is used to generate
1943 URLs from within pylons views or templates. This will be removed when
1958 URLs from within pylons views or templates. This will be removed when
1944 pyramid migration if finished.
1959 pyramid migration if finished.
1945 """
1960 """
1946 req = get_current_request()
1961 req = get_current_request()
1947 return req.resource_path(*args, **kwds)
1962 return req.resource_path(*args, **kwds)
@@ -1,561 +1,571 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 this is forms validation classes
22 this is forms validation classes
23 http://formencode.org/module-formencode.validators.html
23 http://formencode.org/module-formencode.validators.html
24 for list off all availible validators
24 for list off all availible validators
25
25
26 we can create our own validators
26 we can create our own validators
27
27
28 The table below outlines the options which can be used in a schema in addition to the validators themselves
28 The table below outlines the options which can be used in a schema in addition to the validators themselves
29 pre_validators [] These validators will be applied before the schema
29 pre_validators [] These validators will be applied before the schema
30 chained_validators [] These validators will be applied after the schema
30 chained_validators [] These validators will be applied after the schema
31 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
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 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
32 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
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.
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 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
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 <name> = formencode.validators.<name of validator>
37 <name> = formencode.validators.<name of validator>
38 <name> must equal form name
38 <name> must equal form name
39 list=[1,2,3,4,5]
39 list=[1,2,3,4,5]
40 for SELECT use formencode.All(OneOf(list), Int())
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 import logging
54 import logging
45
55
46 import formencode
56 import formencode
47 from formencode import All, Pipe
57 from formencode import All, Pipe
48
58
49 from pylons.i18n.translation import _
59 from pylons.i18n.translation import _
50
60
51 from rhodecode import BACKENDS
61 from rhodecode import BACKENDS
52 from rhodecode.model import validators as v
62 from rhodecode.model import validators as v
53
63
54 log = logging.getLogger(__name__)
64 log = logging.getLogger(__name__)
55
65
56
66
57 def LoginForm():
67 def LoginForm():
58 class _LoginForm(formencode.Schema):
68 class _LoginForm(formencode.Schema):
59 allow_extra_fields = True
69 allow_extra_fields = True
60 filter_extra_fields = True
70 filter_extra_fields = True
61 username = v.UnicodeString(
71 username = v.UnicodeString(
62 strip=True,
72 strip=True,
63 min=1,
73 min=1,
64 not_empty=True,
74 not_empty=True,
65 messages={
75 messages={
66 'empty': _(u'Please enter a login'),
76 'empty': _(u'Please enter a login'),
67 'tooShort': _(u'Enter a value %(min)i characters long or more')
77 'tooShort': _(u'Enter a value %(min)i characters long or more')
68 }
78 }
69 )
79 )
70
80
71 password = v.UnicodeString(
81 password = v.UnicodeString(
72 strip=False,
82 strip=False,
73 min=3,
83 min=3,
74 not_empty=True,
84 not_empty=True,
75 messages={
85 messages={
76 'empty': _(u'Please enter a password'),
86 'empty': _(u'Please enter a password'),
77 'tooShort': _(u'Enter %(min)i characters or more')}
87 'tooShort': _(u'Enter %(min)i characters or more')}
78 )
88 )
79
89
80 remember = v.StringBoolean(if_missing=False)
90 remember = v.StringBoolean(if_missing=False)
81
91
82 chained_validators = [v.ValidAuth()]
92 chained_validators = [v.ValidAuth()]
83 return _LoginForm
93 return _LoginForm
84
94
85
95
86 def PasswordChangeForm(username):
96 def PasswordChangeForm(username):
87 class _PasswordChangeForm(formencode.Schema):
97 class _PasswordChangeForm(formencode.Schema):
88 allow_extra_fields = True
98 allow_extra_fields = True
89 filter_extra_fields = True
99 filter_extra_fields = True
90
100
91 current_password = v.ValidOldPassword(username)(not_empty=True)
101 current_password = v.ValidOldPassword(username)(not_empty=True)
92 new_password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
102 new_password = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
93 new_password_confirmation = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
103 new_password_confirmation = All(v.ValidPassword(), v.UnicodeString(strip=False, min=6))
94
104
95 chained_validators = [v.ValidPasswordsMatch('new_password',
105 chained_validators = [v.ValidPasswordsMatch('new_password',
96 'new_password_confirmation')]
106 'new_password_confirmation')]
97 return _PasswordChangeForm
107 return _PasswordChangeForm
98
108
99
109
100 def UserForm(edit=False, available_languages=[], old_data={}):
110 def UserForm(edit=False, available_languages=[], old_data={}):
101 class _UserForm(formencode.Schema):
111 class _UserForm(formencode.Schema):
102 allow_extra_fields = True
112 allow_extra_fields = True
103 filter_extra_fields = True
113 filter_extra_fields = True
104 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
114 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
105 v.ValidUsername(edit, old_data))
115 v.ValidUsername(edit, old_data))
106 if edit:
116 if edit:
107 new_password = All(
117 new_password = All(
108 v.ValidPassword(),
118 v.ValidPassword(),
109 v.UnicodeString(strip=False, min=6, not_empty=False)
119 v.UnicodeString(strip=False, min=6, not_empty=False)
110 )
120 )
111 password_confirmation = All(
121 password_confirmation = All(
112 v.ValidPassword(),
122 v.ValidPassword(),
113 v.UnicodeString(strip=False, min=6, not_empty=False),
123 v.UnicodeString(strip=False, min=6, not_empty=False),
114 )
124 )
115 admin = v.StringBoolean(if_missing=False)
125 admin = v.StringBoolean(if_missing=False)
116 else:
126 else:
117 password = All(
127 password = All(
118 v.ValidPassword(),
128 v.ValidPassword(),
119 v.UnicodeString(strip=False, min=6, not_empty=True)
129 v.UnicodeString(strip=False, min=6, not_empty=True)
120 )
130 )
121 password_confirmation = All(
131 password_confirmation = All(
122 v.ValidPassword(),
132 v.ValidPassword(),
123 v.UnicodeString(strip=False, min=6, not_empty=False)
133 v.UnicodeString(strip=False, min=6, not_empty=False)
124 )
134 )
125
135
126 password_change = v.StringBoolean(if_missing=False)
136 password_change = v.StringBoolean(if_missing=False)
127 create_repo_group = v.StringBoolean(if_missing=False)
137 create_repo_group = v.StringBoolean(if_missing=False)
128
138
129 active = v.StringBoolean(if_missing=False)
139 active = v.StringBoolean(if_missing=False)
130 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
140 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
131 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
141 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
132 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
142 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
133 extern_name = v.UnicodeString(strip=True)
143 extern_name = v.UnicodeString(strip=True)
134 extern_type = v.UnicodeString(strip=True)
144 extern_type = v.UnicodeString(strip=True)
135 language = v.OneOf(available_languages, hideList=False,
145 language = v.OneOf(available_languages, hideList=False,
136 testValueList=True, if_missing=None)
146 testValueList=True, if_missing=None)
137 chained_validators = [v.ValidPasswordsMatch()]
147 chained_validators = [v.ValidPasswordsMatch()]
138 return _UserForm
148 return _UserForm
139
149
140
150
141 def UserGroupForm(edit=False, old_data=None, available_members=None,
151 def UserGroupForm(edit=False, old_data=None, available_members=None,
142 allow_disabled=False):
152 allow_disabled=False):
143 old_data = old_data or {}
153 old_data = old_data or {}
144 available_members = available_members or []
154 available_members = available_members or []
145
155
146 class _UserGroupForm(formencode.Schema):
156 class _UserGroupForm(formencode.Schema):
147 allow_extra_fields = True
157 allow_extra_fields = True
148 filter_extra_fields = True
158 filter_extra_fields = True
149
159
150 users_group_name = All(
160 users_group_name = All(
151 v.UnicodeString(strip=True, min=1, not_empty=True),
161 v.UnicodeString(strip=True, min=1, not_empty=True),
152 v.ValidUserGroup(edit, old_data)
162 v.ValidUserGroup(edit, old_data)
153 )
163 )
154 user_group_description = v.UnicodeString(strip=True, min=1,
164 user_group_description = v.UnicodeString(strip=True, min=1,
155 not_empty=False)
165 not_empty=False)
156
166
157 users_group_active = v.StringBoolean(if_missing=False)
167 users_group_active = v.StringBoolean(if_missing=False)
158
168
159 if edit:
169 if edit:
160 users_group_members = v.OneOf(
170 users_group_members = v.OneOf(
161 available_members, hideList=False, testValueList=True,
171 available_members, hideList=False, testValueList=True,
162 if_missing=None, not_empty=False
172 if_missing=None, not_empty=False
163 )
173 )
164 # this is user group owner
174 # this is user group owner
165 user = All(
175 user = All(
166 v.UnicodeString(not_empty=True),
176 v.UnicodeString(not_empty=True),
167 v.ValidRepoUser(allow_disabled))
177 v.ValidRepoUser(allow_disabled))
168 return _UserGroupForm
178 return _UserGroupForm
169
179
170
180
171 def RepoGroupForm(edit=False, old_data=None, available_groups=None,
181 def RepoGroupForm(edit=False, old_data=None, available_groups=None,
172 can_create_in_root=False, allow_disabled=False):
182 can_create_in_root=False, allow_disabled=False):
173 old_data = old_data or {}
183 old_data = old_data or {}
174 available_groups = available_groups or []
184 available_groups = available_groups or []
175
185
176 class _RepoGroupForm(formencode.Schema):
186 class _RepoGroupForm(formencode.Schema):
177 allow_extra_fields = True
187 allow_extra_fields = True
178 filter_extra_fields = False
188 filter_extra_fields = False
179
189
180 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
190 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
181 v.SlugifyName(),)
191 v.SlugifyName(),)
182 group_description = v.UnicodeString(strip=True, min=1,
192 group_description = v.UnicodeString(strip=True, min=1,
183 not_empty=False)
193 not_empty=False)
184 group_copy_permissions = v.StringBoolean(if_missing=False)
194 group_copy_permissions = v.StringBoolean(if_missing=False)
185
195
186 group_parent_id = v.OneOf(available_groups, hideList=False,
196 group_parent_id = v.OneOf(available_groups, hideList=False,
187 testValueList=True, not_empty=True)
197 testValueList=True, not_empty=True)
188 enable_locking = v.StringBoolean(if_missing=False)
198 enable_locking = v.StringBoolean(if_missing=False)
189 chained_validators = [
199 chained_validators = [
190 v.ValidRepoGroup(edit, old_data, can_create_in_root)]
200 v.ValidRepoGroup(edit, old_data, can_create_in_root)]
191
201
192 if edit:
202 if edit:
193 # this is repo group owner
203 # this is repo group owner
194 user = All(
204 user = All(
195 v.UnicodeString(not_empty=True),
205 v.UnicodeString(not_empty=True),
196 v.ValidRepoUser(allow_disabled))
206 v.ValidRepoUser(allow_disabled))
197
207
198 return _RepoGroupForm
208 return _RepoGroupForm
199
209
200
210
201 def RegisterForm(edit=False, old_data={}):
211 def RegisterForm(edit=False, old_data={}):
202 class _RegisterForm(formencode.Schema):
212 class _RegisterForm(formencode.Schema):
203 allow_extra_fields = True
213 allow_extra_fields = True
204 filter_extra_fields = True
214 filter_extra_fields = True
205 username = All(
215 username = All(
206 v.ValidUsername(edit, old_data),
216 v.ValidUsername(edit, old_data),
207 v.UnicodeString(strip=True, min=1, not_empty=True)
217 v.UnicodeString(strip=True, min=1, not_empty=True)
208 )
218 )
209 password = All(
219 password = All(
210 v.ValidPassword(),
220 v.ValidPassword(),
211 v.UnicodeString(strip=False, min=6, not_empty=True)
221 v.UnicodeString(strip=False, min=6, not_empty=True)
212 )
222 )
213 password_confirmation = All(
223 password_confirmation = All(
214 v.ValidPassword(),
224 v.ValidPassword(),
215 v.UnicodeString(strip=False, min=6, not_empty=True)
225 v.UnicodeString(strip=False, min=6, not_empty=True)
216 )
226 )
217 active = v.StringBoolean(if_missing=False)
227 active = v.StringBoolean(if_missing=False)
218 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
228 firstname = v.UnicodeString(strip=True, min=1, not_empty=False)
219 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
229 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
220 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
230 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
221
231
222 chained_validators = [v.ValidPasswordsMatch()]
232 chained_validators = [v.ValidPasswordsMatch()]
223
233
224 return _RegisterForm
234 return _RegisterForm
225
235
226
236
227 def PasswordResetForm():
237 def PasswordResetForm():
228 class _PasswordResetForm(formencode.Schema):
238 class _PasswordResetForm(formencode.Schema):
229 allow_extra_fields = True
239 allow_extra_fields = True
230 filter_extra_fields = True
240 filter_extra_fields = True
231 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
241 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
232 return _PasswordResetForm
242 return _PasswordResetForm
233
243
234
244
235 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None,
245 def RepoForm(edit=False, old_data=None, repo_groups=None, landing_revs=None,
236 allow_disabled=False):
246 allow_disabled=False):
237 old_data = old_data or {}
247 old_data = old_data or {}
238 repo_groups = repo_groups or []
248 repo_groups = repo_groups or []
239 landing_revs = landing_revs or []
249 landing_revs = landing_revs or []
240 supported_backends = BACKENDS.keys()
250 supported_backends = BACKENDS.keys()
241
251
242 class _RepoForm(formencode.Schema):
252 class _RepoForm(formencode.Schema):
243 allow_extra_fields = True
253 allow_extra_fields = True
244 filter_extra_fields = False
254 filter_extra_fields = False
245 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
255 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
246 v.SlugifyName())
256 v.SlugifyName())
247 repo_group = All(v.CanWriteGroup(old_data),
257 repo_group = All(v.CanWriteGroup(old_data),
248 v.OneOf(repo_groups, hideList=True))
258 v.OneOf(repo_groups, hideList=True))
249 repo_type = v.OneOf(supported_backends, required=False,
259 repo_type = v.OneOf(supported_backends, required=False,
250 if_missing=old_data.get('repo_type'))
260 if_missing=old_data.get('repo_type'))
251 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
261 repo_description = v.UnicodeString(strip=True, min=1, not_empty=False)
252 repo_private = v.StringBoolean(if_missing=False)
262 repo_private = v.StringBoolean(if_missing=False)
253 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
263 repo_landing_rev = v.OneOf(landing_revs, hideList=True)
254 repo_copy_permissions = v.StringBoolean(if_missing=False)
264 repo_copy_permissions = v.StringBoolean(if_missing=False)
255 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
265 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
256
266
257 repo_enable_statistics = v.StringBoolean(if_missing=False)
267 repo_enable_statistics = v.StringBoolean(if_missing=False)
258 repo_enable_downloads = v.StringBoolean(if_missing=False)
268 repo_enable_downloads = v.StringBoolean(if_missing=False)
259 repo_enable_locking = v.StringBoolean(if_missing=False)
269 repo_enable_locking = v.StringBoolean(if_missing=False)
260
270
261 if edit:
271 if edit:
262 # this is repo owner
272 # this is repo owner
263 user = All(
273 user = All(
264 v.UnicodeString(not_empty=True),
274 v.UnicodeString(not_empty=True),
265 v.ValidRepoUser(allow_disabled))
275 v.ValidRepoUser(allow_disabled))
266 clone_uri_change = v.UnicodeString(
276 clone_uri_change = v.UnicodeString(
267 not_empty=False, if_missing=v.Missing)
277 not_empty=False, if_missing=v.Missing)
268
278
269 chained_validators = [v.ValidCloneUri(),
279 chained_validators = [v.ValidCloneUri(),
270 v.ValidRepoName(edit, old_data)]
280 v.ValidRepoName(edit, old_data)]
271 return _RepoForm
281 return _RepoForm
272
282
273
283
274 def RepoPermsForm():
284 def RepoPermsForm():
275 class _RepoPermsForm(formencode.Schema):
285 class _RepoPermsForm(formencode.Schema):
276 allow_extra_fields = True
286 allow_extra_fields = True
277 filter_extra_fields = False
287 filter_extra_fields = False
278 chained_validators = [v.ValidPerms(type_='repo')]
288 chained_validators = [v.ValidPerms(type_='repo')]
279 return _RepoPermsForm
289 return _RepoPermsForm
280
290
281
291
282 def RepoGroupPermsForm(valid_recursive_choices):
292 def RepoGroupPermsForm(valid_recursive_choices):
283 class _RepoGroupPermsForm(formencode.Schema):
293 class _RepoGroupPermsForm(formencode.Schema):
284 allow_extra_fields = True
294 allow_extra_fields = True
285 filter_extra_fields = False
295 filter_extra_fields = False
286 recursive = v.OneOf(valid_recursive_choices)
296 recursive = v.OneOf(valid_recursive_choices)
287 chained_validators = [v.ValidPerms(type_='repo_group')]
297 chained_validators = [v.ValidPerms(type_='repo_group')]
288 return _RepoGroupPermsForm
298 return _RepoGroupPermsForm
289
299
290
300
291 def UserGroupPermsForm():
301 def UserGroupPermsForm():
292 class _UserPermsForm(formencode.Schema):
302 class _UserPermsForm(formencode.Schema):
293 allow_extra_fields = True
303 allow_extra_fields = True
294 filter_extra_fields = False
304 filter_extra_fields = False
295 chained_validators = [v.ValidPerms(type_='user_group')]
305 chained_validators = [v.ValidPerms(type_='user_group')]
296 return _UserPermsForm
306 return _UserPermsForm
297
307
298
308
299 def RepoFieldForm():
309 def RepoFieldForm():
300 class _RepoFieldForm(formencode.Schema):
310 class _RepoFieldForm(formencode.Schema):
301 filter_extra_fields = True
311 filter_extra_fields = True
302 allow_extra_fields = True
312 allow_extra_fields = True
303
313
304 new_field_key = All(v.FieldKey(),
314 new_field_key = All(v.FieldKey(),
305 v.UnicodeString(strip=True, min=3, not_empty=True))
315 v.UnicodeString(strip=True, min=3, not_empty=True))
306 new_field_value = v.UnicodeString(not_empty=False, if_missing=u'')
316 new_field_value = v.UnicodeString(not_empty=False, if_missing=u'')
307 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
317 new_field_type = v.OneOf(['str', 'unicode', 'list', 'tuple'],
308 if_missing='str')
318 if_missing='str')
309 new_field_label = v.UnicodeString(not_empty=False)
319 new_field_label = v.UnicodeString(not_empty=False)
310 new_field_desc = v.UnicodeString(not_empty=False)
320 new_field_desc = v.UnicodeString(not_empty=False)
311
321
312 return _RepoFieldForm
322 return _RepoFieldForm
313
323
314
324
315 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
325 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
316 repo_groups=[], landing_revs=[]):
326 repo_groups=[], landing_revs=[]):
317 class _RepoForkForm(formencode.Schema):
327 class _RepoForkForm(formencode.Schema):
318 allow_extra_fields = True
328 allow_extra_fields = True
319 filter_extra_fields = False
329 filter_extra_fields = False
320 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
330 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
321 v.SlugifyName())
331 v.SlugifyName())
322 repo_group = All(v.CanWriteGroup(),
332 repo_group = All(v.CanWriteGroup(),
323 v.OneOf(repo_groups, hideList=True))
333 v.OneOf(repo_groups, hideList=True))
324 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
334 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
325 description = v.UnicodeString(strip=True, min=1, not_empty=True)
335 description = v.UnicodeString(strip=True, min=1, not_empty=True)
326 private = v.StringBoolean(if_missing=False)
336 private = v.StringBoolean(if_missing=False)
327 copy_permissions = v.StringBoolean(if_missing=False)
337 copy_permissions = v.StringBoolean(if_missing=False)
328 fork_parent_id = v.UnicodeString()
338 fork_parent_id = v.UnicodeString()
329 chained_validators = [v.ValidForkName(edit, old_data)]
339 chained_validators = [v.ValidForkName(edit, old_data)]
330 landing_rev = v.OneOf(landing_revs, hideList=True)
340 landing_rev = v.OneOf(landing_revs, hideList=True)
331
341
332 return _RepoForkForm
342 return _RepoForkForm
333
343
334
344
335 def ApplicationSettingsForm():
345 def ApplicationSettingsForm():
336 class _ApplicationSettingsForm(formencode.Schema):
346 class _ApplicationSettingsForm(formencode.Schema):
337 allow_extra_fields = True
347 allow_extra_fields = True
338 filter_extra_fields = False
348 filter_extra_fields = False
339 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
349 rhodecode_title = v.UnicodeString(strip=True, max=40, not_empty=False)
340 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
350 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
341 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
351 rhodecode_pre_code = v.UnicodeString(strip=True, min=1, not_empty=False)
342 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
352 rhodecode_post_code = v.UnicodeString(strip=True, min=1, not_empty=False)
343 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
353 rhodecode_captcha_public_key = v.UnicodeString(strip=True, min=1, not_empty=False)
344 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
354 rhodecode_captcha_private_key = v.UnicodeString(strip=True, min=1, not_empty=False)
345
355
346 return _ApplicationSettingsForm
356 return _ApplicationSettingsForm
347
357
348
358
349 def ApplicationVisualisationForm():
359 def ApplicationVisualisationForm():
350 class _ApplicationVisualisationForm(formencode.Schema):
360 class _ApplicationVisualisationForm(formencode.Schema):
351 allow_extra_fields = True
361 allow_extra_fields = True
352 filter_extra_fields = False
362 filter_extra_fields = False
353 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
363 rhodecode_show_public_icon = v.StringBoolean(if_missing=False)
354 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
364 rhodecode_show_private_icon = v.StringBoolean(if_missing=False)
355 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
365 rhodecode_stylify_metatags = v.StringBoolean(if_missing=False)
356
366
357 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
367 rhodecode_repository_fields = v.StringBoolean(if_missing=False)
358 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
368 rhodecode_lightweight_journal = v.StringBoolean(if_missing=False)
359 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
369 rhodecode_dashboard_items = v.Int(min=5, not_empty=True)
360 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
370 rhodecode_admin_grid_items = v.Int(min=5, not_empty=True)
361 rhodecode_show_version = v.StringBoolean(if_missing=False)
371 rhodecode_show_version = v.StringBoolean(if_missing=False)
362 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
372 rhodecode_use_gravatar = v.StringBoolean(if_missing=False)
363 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
373 rhodecode_markup_renderer = v.OneOf(['markdown', 'rst'])
364 rhodecode_gravatar_url = v.UnicodeString(min=3)
374 rhodecode_gravatar_url = v.UnicodeString(min=3)
365 rhodecode_clone_uri_tmpl = v.UnicodeString(min=3)
375 rhodecode_clone_uri_tmpl = v.UnicodeString(min=3)
366 rhodecode_support_url = v.UnicodeString()
376 rhodecode_support_url = v.UnicodeString()
367 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
377 rhodecode_show_revision_number = v.StringBoolean(if_missing=False)
368 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
378 rhodecode_show_sha_length = v.Int(min=4, not_empty=True)
369
379
370 return _ApplicationVisualisationForm
380 return _ApplicationVisualisationForm
371
381
372
382
373 class _BaseVcsSettingsForm(formencode.Schema):
383 class _BaseVcsSettingsForm(formencode.Schema):
374 allow_extra_fields = True
384 allow_extra_fields = True
375 filter_extra_fields = False
385 filter_extra_fields = False
376 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
386 hooks_changegroup_repo_size = v.StringBoolean(if_missing=False)
377 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
387 hooks_changegroup_push_logger = v.StringBoolean(if_missing=False)
378 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
388 hooks_outgoing_pull_logger = v.StringBoolean(if_missing=False)
379
389
380 extensions_largefiles = v.StringBoolean(if_missing=False)
390 extensions_largefiles = v.StringBoolean(if_missing=False)
381 phases_publish = v.StringBoolean(if_missing=False)
391 phases_publish = v.StringBoolean(if_missing=False)
382
392
383 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
393 rhodecode_pr_merge_enabled = v.StringBoolean(if_missing=False)
384 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
394 rhodecode_use_outdated_comments = v.StringBoolean(if_missing=False)
385 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
395 rhodecode_hg_use_rebase_for_merging = v.StringBoolean(if_missing=False)
386
396
387
397
388 def ApplicationUiSettingsForm():
398 def ApplicationUiSettingsForm():
389 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
399 class _ApplicationUiSettingsForm(_BaseVcsSettingsForm):
390 web_push_ssl = v.StringBoolean(if_missing=False)
400 web_push_ssl = v.StringBoolean(if_missing=False)
391 paths_root_path = All(
401 paths_root_path = All(
392 v.ValidPath(),
402 v.ValidPath(),
393 v.UnicodeString(strip=True, min=1, not_empty=True)
403 v.UnicodeString(strip=True, min=1, not_empty=True)
394 )
404 )
395 extensions_hgsubversion = v.StringBoolean(if_missing=False)
405 extensions_hgsubversion = v.StringBoolean(if_missing=False)
396 extensions_hggit = v.StringBoolean(if_missing=False)
406 extensions_hggit = v.StringBoolean(if_missing=False)
397 new_svn_branch = v.ValidSvnPattern(section='vcs_svn_branch')
407 new_svn_branch = v.ValidSvnPattern(section='vcs_svn_branch')
398 new_svn_tag = v.ValidSvnPattern(section='vcs_svn_tag')
408 new_svn_tag = v.ValidSvnPattern(section='vcs_svn_tag')
399
409
400 return _ApplicationUiSettingsForm
410 return _ApplicationUiSettingsForm
401
411
402
412
403 def RepoVcsSettingsForm(repo_name):
413 def RepoVcsSettingsForm(repo_name):
404 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
414 class _RepoVcsSettingsForm(_BaseVcsSettingsForm):
405 inherit_global_settings = v.StringBoolean(if_missing=False)
415 inherit_global_settings = v.StringBoolean(if_missing=False)
406 new_svn_branch = v.ValidSvnPattern(
416 new_svn_branch = v.ValidSvnPattern(
407 section='vcs_svn_branch', repo_name=repo_name)
417 section='vcs_svn_branch', repo_name=repo_name)
408 new_svn_tag = v.ValidSvnPattern(
418 new_svn_tag = v.ValidSvnPattern(
409 section='vcs_svn_tag', repo_name=repo_name)
419 section='vcs_svn_tag', repo_name=repo_name)
410
420
411 return _RepoVcsSettingsForm
421 return _RepoVcsSettingsForm
412
422
413
423
414 def LabsSettingsForm():
424 def LabsSettingsForm():
415 class _LabSettingsForm(formencode.Schema):
425 class _LabSettingsForm(formencode.Schema):
416 allow_extra_fields = True
426 allow_extra_fields = True
417 filter_extra_fields = False
427 filter_extra_fields = False
418
428
419 rhodecode_proxy_subversion_http_requests = v.StringBoolean(
429 rhodecode_proxy_subversion_http_requests = v.StringBoolean(
420 if_missing=False)
430 if_missing=False)
421 rhodecode_subversion_http_server_url = v.UnicodeString(
431 rhodecode_subversion_http_server_url = v.UnicodeString(
422 strip=True, if_missing=None)
432 strip=True, if_missing=None)
423
433
424 return _LabSettingsForm
434 return _LabSettingsForm
425
435
426
436
427 def ApplicationPermissionsForm(register_choices, extern_activate_choices):
437 def ApplicationPermissionsForm(register_choices, extern_activate_choices):
428 class _DefaultPermissionsForm(formencode.Schema):
438 class _DefaultPermissionsForm(formencode.Schema):
429 allow_extra_fields = True
439 allow_extra_fields = True
430 filter_extra_fields = True
440 filter_extra_fields = True
431
441
432 anonymous = v.StringBoolean(if_missing=False)
442 anonymous = v.StringBoolean(if_missing=False)
433 default_register = v.OneOf(register_choices)
443 default_register = v.OneOf(register_choices)
434 default_register_message = v.UnicodeString()
444 default_register_message = v.UnicodeString()
435 default_extern_activate = v.OneOf(extern_activate_choices)
445 default_extern_activate = v.OneOf(extern_activate_choices)
436
446
437 return _DefaultPermissionsForm
447 return _DefaultPermissionsForm
438
448
439
449
440 def ObjectPermissionsForm(repo_perms_choices, group_perms_choices,
450 def ObjectPermissionsForm(repo_perms_choices, group_perms_choices,
441 user_group_perms_choices):
451 user_group_perms_choices):
442 class _ObjectPermissionsForm(formencode.Schema):
452 class _ObjectPermissionsForm(formencode.Schema):
443 allow_extra_fields = True
453 allow_extra_fields = True
444 filter_extra_fields = True
454 filter_extra_fields = True
445 overwrite_default_repo = v.StringBoolean(if_missing=False)
455 overwrite_default_repo = v.StringBoolean(if_missing=False)
446 overwrite_default_group = v.StringBoolean(if_missing=False)
456 overwrite_default_group = v.StringBoolean(if_missing=False)
447 overwrite_default_user_group = v.StringBoolean(if_missing=False)
457 overwrite_default_user_group = v.StringBoolean(if_missing=False)
448 default_repo_perm = v.OneOf(repo_perms_choices)
458 default_repo_perm = v.OneOf(repo_perms_choices)
449 default_group_perm = v.OneOf(group_perms_choices)
459 default_group_perm = v.OneOf(group_perms_choices)
450 default_user_group_perm = v.OneOf(user_group_perms_choices)
460 default_user_group_perm = v.OneOf(user_group_perms_choices)
451
461
452 return _ObjectPermissionsForm
462 return _ObjectPermissionsForm
453
463
454
464
455 def UserPermissionsForm(create_choices, create_on_write_choices,
465 def UserPermissionsForm(create_choices, create_on_write_choices,
456 repo_group_create_choices, user_group_create_choices,
466 repo_group_create_choices, user_group_create_choices,
457 fork_choices, inherit_default_permissions_choices):
467 fork_choices, inherit_default_permissions_choices):
458 class _DefaultPermissionsForm(formencode.Schema):
468 class _DefaultPermissionsForm(formencode.Schema):
459 allow_extra_fields = True
469 allow_extra_fields = True
460 filter_extra_fields = True
470 filter_extra_fields = True
461
471
462 anonymous = v.StringBoolean(if_missing=False)
472 anonymous = v.StringBoolean(if_missing=False)
463
473
464 default_repo_create = v.OneOf(create_choices)
474 default_repo_create = v.OneOf(create_choices)
465 default_repo_create_on_write = v.OneOf(create_on_write_choices)
475 default_repo_create_on_write = v.OneOf(create_on_write_choices)
466 default_user_group_create = v.OneOf(user_group_create_choices)
476 default_user_group_create = v.OneOf(user_group_create_choices)
467 default_repo_group_create = v.OneOf(repo_group_create_choices)
477 default_repo_group_create = v.OneOf(repo_group_create_choices)
468 default_fork_create = v.OneOf(fork_choices)
478 default_fork_create = v.OneOf(fork_choices)
469 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
479 default_inherit_default_permissions = v.OneOf(inherit_default_permissions_choices)
470
480
471 return _DefaultPermissionsForm
481 return _DefaultPermissionsForm
472
482
473
483
474 def UserIndividualPermissionsForm():
484 def UserIndividualPermissionsForm():
475 class _DefaultPermissionsForm(formencode.Schema):
485 class _DefaultPermissionsForm(formencode.Schema):
476 allow_extra_fields = True
486 allow_extra_fields = True
477 filter_extra_fields = True
487 filter_extra_fields = True
478
488
479 inherit_default_permissions = v.StringBoolean(if_missing=False)
489 inherit_default_permissions = v.StringBoolean(if_missing=False)
480
490
481 return _DefaultPermissionsForm
491 return _DefaultPermissionsForm
482
492
483
493
484 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
494 def DefaultsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
485 class _DefaultsForm(formencode.Schema):
495 class _DefaultsForm(formencode.Schema):
486 allow_extra_fields = True
496 allow_extra_fields = True
487 filter_extra_fields = True
497 filter_extra_fields = True
488 default_repo_type = v.OneOf(supported_backends)
498 default_repo_type = v.OneOf(supported_backends)
489 default_repo_private = v.StringBoolean(if_missing=False)
499 default_repo_private = v.StringBoolean(if_missing=False)
490 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
500 default_repo_enable_statistics = v.StringBoolean(if_missing=False)
491 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
501 default_repo_enable_downloads = v.StringBoolean(if_missing=False)
492 default_repo_enable_locking = v.StringBoolean(if_missing=False)
502 default_repo_enable_locking = v.StringBoolean(if_missing=False)
493
503
494 return _DefaultsForm
504 return _DefaultsForm
495
505
496
506
497 def AuthSettingsForm():
507 def AuthSettingsForm():
498 class _AuthSettingsForm(formencode.Schema):
508 class _AuthSettingsForm(formencode.Schema):
499 allow_extra_fields = True
509 allow_extra_fields = True
500 filter_extra_fields = True
510 filter_extra_fields = True
501 auth_plugins = All(v.ValidAuthPlugins(),
511 auth_plugins = All(v.ValidAuthPlugins(),
502 v.UniqueListFromString()(not_empty=True))
512 v.UniqueListFromString()(not_empty=True))
503
513
504 return _AuthSettingsForm
514 return _AuthSettingsForm
505
515
506
516
507 def UserExtraEmailForm():
517 def UserExtraEmailForm():
508 class _UserExtraEmailForm(formencode.Schema):
518 class _UserExtraEmailForm(formencode.Schema):
509 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
519 email = All(v.UniqSystemEmail(), v.Email(not_empty=True))
510 return _UserExtraEmailForm
520 return _UserExtraEmailForm
511
521
512
522
513 def UserExtraIpForm():
523 def UserExtraIpForm():
514 class _UserExtraIpForm(formencode.Schema):
524 class _UserExtraIpForm(formencode.Schema):
515 ip = v.ValidIp()(not_empty=True)
525 ip = v.ValidIp()(not_empty=True)
516 return _UserExtraIpForm
526 return _UserExtraIpForm
517
527
518
528
519 def PullRequestForm(repo_id):
529 def PullRequestForm(repo_id):
520 class _PullRequestForm(formencode.Schema):
530 class _PullRequestForm(formencode.Schema):
521 allow_extra_fields = True
531 allow_extra_fields = True
522 filter_extra_fields = True
532 filter_extra_fields = True
523
533
524 user = v.UnicodeString(strip=True, required=True)
534 user = v.UnicodeString(strip=True, required=True)
525 source_repo = v.UnicodeString(strip=True, required=True)
535 source_repo = v.UnicodeString(strip=True, required=True)
526 source_ref = v.UnicodeString(strip=True, required=True)
536 source_ref = v.UnicodeString(strip=True, required=True)
527 target_repo = v.UnicodeString(strip=True, required=True)
537 target_repo = v.UnicodeString(strip=True, required=True)
528 target_ref = v.UnicodeString(strip=True, required=True)
538 target_ref = v.UnicodeString(strip=True, required=True)
529 revisions = All(#v.NotReviewedRevisions(repo_id)(),
539 revisions = All(#v.NotReviewedRevisions(repo_id)(),
530 v.UniqueList()(not_empty=True))
540 v.UniqueList()(not_empty=True))
531 review_members = v.UniqueList(convert=int)(not_empty=True)
541 review_members = v.UniqueList(convert=int)(not_empty=True)
532
542
533 pullrequest_title = v.UnicodeString(strip=True, required=True)
543 pullrequest_title = v.UnicodeString(strip=True, required=True)
534 pullrequest_desc = v.UnicodeString(strip=True, required=False)
544 pullrequest_desc = v.UnicodeString(strip=True, required=False)
535
545
536 return _PullRequestForm
546 return _PullRequestForm
537
547
538
548
539 def GistForm(lifetime_options, acl_level_options):
549 def GistForm(lifetime_options, acl_level_options):
540 class _GistForm(formencode.Schema):
550 class _GistForm(formencode.Schema):
541
551
542 gistid = All(v.UniqGistId(), v.UnicodeString(strip=True, min=3, not_empty=False, if_missing=None))
552 gistid = All(v.UniqGistId(), v.UnicodeString(strip=True, min=3, not_empty=False, if_missing=None))
543 filename = All(v.BasePath()(),
553 filename = All(v.BasePath()(),
544 v.UnicodeString(strip=True, required=False))
554 v.UnicodeString(strip=True, required=False))
545 description = v.UnicodeString(required=False, if_missing=u'')
555 description = v.UnicodeString(required=False, if_missing=u'')
546 lifetime = v.OneOf(lifetime_options)
556 lifetime = v.OneOf(lifetime_options)
547 mimetype = v.UnicodeString(required=False, if_missing=None)
557 mimetype = v.UnicodeString(required=False, if_missing=None)
548 content = v.UnicodeString(required=True, not_empty=True)
558 content = v.UnicodeString(required=True, not_empty=True)
549 public = v.UnicodeString(required=False, if_missing=u'')
559 public = v.UnicodeString(required=False, if_missing=u'')
550 private = v.UnicodeString(required=False, if_missing=u'')
560 private = v.UnicodeString(required=False, if_missing=u'')
551 acl_level = v.OneOf(acl_level_options)
561 acl_level = v.OneOf(acl_level_options)
552
562
553 return _GistForm
563 return _GistForm
554
564
555
565
556 def IssueTrackerPatternsForm():
566 def IssueTrackerPatternsForm():
557 class _IssueTrackerPatternsForm(formencode.Schema):
567 class _IssueTrackerPatternsForm(formencode.Schema):
558 allow_extra_fields = True
568 allow_extra_fields = True
559 filter_extra_fields = False
569 filter_extra_fields = False
560 chained_validators = [v.ValidPattern()]
570 chained_validators = [v.ValidPattern()]
561 return _IssueTrackerPatternsForm
571 return _IssueTrackerPatternsForm
@@ -1,2099 +1,2100 b''
1 //Primary CSS
1 //Primary CSS
2
2
3 //--- IMPORTS ------------------//
3 //--- IMPORTS ------------------//
4
4
5 @import 'helpers';
5 @import 'helpers';
6 @import 'mixins';
6 @import 'mixins';
7 @import 'rcicons';
7 @import 'rcicons';
8 @import 'fonts';
8 @import 'fonts';
9 @import 'variables';
9 @import 'variables';
10 @import 'bootstrap-variables';
10 @import 'bootstrap-variables';
11 @import 'form-bootstrap';
11 @import 'form-bootstrap';
12 @import 'codemirror';
12 @import 'codemirror';
13 @import 'legacy_code_styles';
13 @import 'legacy_code_styles';
14 @import 'progress-bar';
14 @import 'progress-bar';
15
15
16 @import 'type';
16 @import 'type';
17 @import 'alerts';
17 @import 'alerts';
18 @import 'buttons';
18 @import 'buttons';
19 @import 'tags';
19 @import 'tags';
20 @import 'code-block';
20 @import 'code-block';
21 @import 'examples';
21 @import 'examples';
22 @import 'login';
22 @import 'login';
23 @import 'main-content';
23 @import 'main-content';
24 @import 'select2';
24 @import 'select2';
25 @import 'comments';
25 @import 'comments';
26 @import 'panels-bootstrap';
26 @import 'panels-bootstrap';
27 @import 'panels';
27 @import 'panels';
28 @import 'deform';
28
29
29
30
30 //--- BASE ------------------//
31 //--- BASE ------------------//
31 .noscript-error {
32 .noscript-error {
32 top: 0;
33 top: 0;
33 left: 0;
34 left: 0;
34 width: 100%;
35 width: 100%;
35 z-index: 101;
36 z-index: 101;
36 text-align: center;
37 text-align: center;
37 font-family: @text-semibold;
38 font-family: @text-semibold;
38 font-size: 120%;
39 font-size: 120%;
39 color: white;
40 color: white;
40 background-color: @alert2;
41 background-color: @alert2;
41 padding: 5px 0 5px 0;
42 padding: 5px 0 5px 0;
42 }
43 }
43
44
44 html {
45 html {
45 display: table;
46 display: table;
46 height: 100%;
47 height: 100%;
47 width: 100%;
48 width: 100%;
48 }
49 }
49
50
50 body {
51 body {
51 display: table-cell;
52 display: table-cell;
52 width: 100%;
53 width: 100%;
53 }
54 }
54
55
55 //--- LAYOUT ------------------//
56 //--- LAYOUT ------------------//
56
57
57 .hidden{
58 .hidden{
58 display: none !important;
59 display: none !important;
59 }
60 }
60
61
61 .box{
62 .box{
62 float: left;
63 float: left;
63 width: 100%;
64 width: 100%;
64 }
65 }
65
66
66 .browser-header {
67 .browser-header {
67 clear: both;
68 clear: both;
68 }
69 }
69 .main {
70 .main {
70 clear: both;
71 clear: both;
71 padding:0 0 @pagepadding;
72 padding:0 0 @pagepadding;
72 height: auto;
73 height: auto;
73
74
74 &:after { //clearfix
75 &:after { //clearfix
75 content:"";
76 content:"";
76 clear:both;
77 clear:both;
77 width:100%;
78 width:100%;
78 display:block;
79 display:block;
79 }
80 }
80 }
81 }
81
82
82 .action-link{
83 .action-link{
83 margin-left: @padding;
84 margin-left: @padding;
84 padding-left: @padding;
85 padding-left: @padding;
85 border-left: @border-thickness solid @border-default-color;
86 border-left: @border-thickness solid @border-default-color;
86 }
87 }
87
88
88 input + .action-link, .action-link.first{
89 input + .action-link, .action-link.first{
89 border-left: none;
90 border-left: none;
90 }
91 }
91
92
92 .action-link.last{
93 .action-link.last{
93 margin-right: @padding;
94 margin-right: @padding;
94 padding-right: @padding;
95 padding-right: @padding;
95 }
96 }
96
97
97 .action-link.active,
98 .action-link.active,
98 .action-link.active a{
99 .action-link.active a{
99 color: @grey4;
100 color: @grey4;
100 }
101 }
101
102
102 ul.simple-list{
103 ul.simple-list{
103 list-style: none;
104 list-style: none;
104 margin: 0;
105 margin: 0;
105 padding: 0;
106 padding: 0;
106 }
107 }
107
108
108 .main-content {
109 .main-content {
109 padding-bottom: @pagepadding;
110 padding-bottom: @pagepadding;
110 }
111 }
111
112
112 .wrapper {
113 .wrapper {
113 position: relative;
114 position: relative;
114 max-width: @wrapper-maxwidth;
115 max-width: @wrapper-maxwidth;
115 margin: 0 auto;
116 margin: 0 auto;
116 }
117 }
117
118
118 #content {
119 #content {
119 clear: both;
120 clear: both;
120 padding: 0 @contentpadding;
121 padding: 0 @contentpadding;
121 }
122 }
122
123
123 .advanced-settings-fields{
124 .advanced-settings-fields{
124 input{
125 input{
125 margin-left: @textmargin;
126 margin-left: @textmargin;
126 margin-right: @padding/2;
127 margin-right: @padding/2;
127 }
128 }
128 }
129 }
129
130
130 .cs_files_title {
131 .cs_files_title {
131 margin: @pagepadding 0 0;
132 margin: @pagepadding 0 0;
132 }
133 }
133
134
134 input.inline[type="file"] {
135 input.inline[type="file"] {
135 display: inline;
136 display: inline;
136 }
137 }
137
138
138 .error_page {
139 .error_page {
139 margin: 10% auto;
140 margin: 10% auto;
140
141
141 h1 {
142 h1 {
142 color: @grey2;
143 color: @grey2;
143 }
144 }
144
145
145 .error-branding {
146 .error-branding {
146 font-family: @text-semibold;
147 font-family: @text-semibold;
147 color: @grey4;
148 color: @grey4;
148 }
149 }
149
150
150 .error_message {
151 .error_message {
151 font-family: @text-regular;
152 font-family: @text-regular;
152 }
153 }
153
154
154 .sidebar {
155 .sidebar {
155 min-height: 275px;
156 min-height: 275px;
156 margin: 0;
157 margin: 0;
157 padding: 0 0 @sidebarpadding @sidebarpadding;
158 padding: 0 0 @sidebarpadding @sidebarpadding;
158 border: none;
159 border: none;
159 }
160 }
160
161
161 .main-content {
162 .main-content {
162 position: relative;
163 position: relative;
163 margin: 0 @sidebarpadding @sidebarpadding;
164 margin: 0 @sidebarpadding @sidebarpadding;
164 padding: 0 0 0 @sidebarpadding;
165 padding: 0 0 0 @sidebarpadding;
165 border-left: @border-thickness solid @grey5;
166 border-left: @border-thickness solid @grey5;
166
167
167 @media (max-width:767px) {
168 @media (max-width:767px) {
168 clear: both;
169 clear: both;
169 width: 100%;
170 width: 100%;
170 margin: 0;
171 margin: 0;
171 border: none;
172 border: none;
172 }
173 }
173 }
174 }
174
175
175 .inner-column {
176 .inner-column {
176 float: left;
177 float: left;
177 width: 29.75%;
178 width: 29.75%;
178 min-height: 150px;
179 min-height: 150px;
179 margin: @sidebarpadding 2% 0 0;
180 margin: @sidebarpadding 2% 0 0;
180 padding: 0 2% 0 0;
181 padding: 0 2% 0 0;
181 border-right: @border-thickness solid @grey5;
182 border-right: @border-thickness solid @grey5;
182
183
183 @media (max-width:767px) {
184 @media (max-width:767px) {
184 clear: both;
185 clear: both;
185 width: 100%;
186 width: 100%;
186 border: none;
187 border: none;
187 }
188 }
188
189
189 ul {
190 ul {
190 padding-left: 1.25em;
191 padding-left: 1.25em;
191 }
192 }
192
193
193 &:last-child {
194 &:last-child {
194 margin: @sidebarpadding 0 0;
195 margin: @sidebarpadding 0 0;
195 border: none;
196 border: none;
196 }
197 }
197
198
198 h4 {
199 h4 {
199 margin: 0 0 @padding;
200 margin: 0 0 @padding;
200 font-family: @text-semibold;
201 font-family: @text-semibold;
201 }
202 }
202 }
203 }
203 }
204 }
204 .error-page-logo {
205 .error-page-logo {
205 width: 130px;
206 width: 130px;
206 height: 160px;
207 height: 160px;
207 }
208 }
208
209
209 // HEADER
210 // HEADER
210 .header {
211 .header {
211
212
212 // TODO: johbo: Fix login pages, so that they work without a min-height
213 // TODO: johbo: Fix login pages, so that they work without a min-height
213 // for the header and then remove the min-height. I chose a smaller value
214 // for the header and then remove the min-height. I chose a smaller value
214 // intentionally here to avoid rendering issues in the main navigation.
215 // intentionally here to avoid rendering issues in the main navigation.
215 min-height: 49px;
216 min-height: 49px;
216
217
217 position: relative;
218 position: relative;
218 vertical-align: bottom;
219 vertical-align: bottom;
219 padding: 0 @header-padding;
220 padding: 0 @header-padding;
220 background-color: @grey2;
221 background-color: @grey2;
221 color: @grey5;
222 color: @grey5;
222
223
223 .title {
224 .title {
224 overflow: visible;
225 overflow: visible;
225 }
226 }
226
227
227 &:before,
228 &:before,
228 &:after {
229 &:after {
229 content: "";
230 content: "";
230 clear: both;
231 clear: both;
231 width: 100%;
232 width: 100%;
232 }
233 }
233
234
234 // TODO: johbo: Avoids breaking "Repositories" chooser
235 // TODO: johbo: Avoids breaking "Repositories" chooser
235 .select2-container .select2-choice .select2-arrow {
236 .select2-container .select2-choice .select2-arrow {
236 display: none;
237 display: none;
237 }
238 }
238 }
239 }
239
240
240 #header-inner {
241 #header-inner {
241 &.title {
242 &.title {
242 margin: 0;
243 margin: 0;
243 }
244 }
244 &:before,
245 &:before,
245 &:after {
246 &:after {
246 content: "";
247 content: "";
247 clear: both;
248 clear: both;
248 }
249 }
249 }
250 }
250
251
251 // Gists
252 // Gists
252 #files_data {
253 #files_data {
253 clear: both; //for firefox
254 clear: both; //for firefox
254 }
255 }
255 #gistid {
256 #gistid {
256 margin-right: @padding;
257 margin-right: @padding;
257 }
258 }
258
259
259 // Global Settings Editor
260 // Global Settings Editor
260 .textarea.editor {
261 .textarea.editor {
261 float: left;
262 float: left;
262 position: relative;
263 position: relative;
263 max-width: @texteditor-width;
264 max-width: @texteditor-width;
264
265
265 select {
266 select {
266 position: absolute;
267 position: absolute;
267 top:10px;
268 top:10px;
268 right:0;
269 right:0;
269 }
270 }
270
271
271 .CodeMirror {
272 .CodeMirror {
272 margin: 0;
273 margin: 0;
273 }
274 }
274
275
275 .help-block {
276 .help-block {
276 margin: 0 0 @padding;
277 margin: 0 0 @padding;
277 padding:.5em;
278 padding:.5em;
278 background-color: @grey6;
279 background-color: @grey6;
279 }
280 }
280 }
281 }
281
282
282 ul.auth_plugins {
283 ul.auth_plugins {
283 margin: @padding 0 @padding @legend-width;
284 margin: @padding 0 @padding @legend-width;
284 padding: 0;
285 padding: 0;
285
286
286 li {
287 li {
287 margin-bottom: @padding;
288 margin-bottom: @padding;
288 line-height: 1em;
289 line-height: 1em;
289 list-style-type: none;
290 list-style-type: none;
290
291
291 .auth_buttons .btn {
292 .auth_buttons .btn {
292 margin-right: @padding;
293 margin-right: @padding;
293 }
294 }
294
295
295 &:before { content: none; }
296 &:before { content: none; }
296 }
297 }
297 }
298 }
298
299
299
300
300 // My Account PR list
301 // My Account PR list
301
302
302 #show_closed {
303 #show_closed {
303 margin: 0 1em 0 0;
304 margin: 0 1em 0 0;
304 }
305 }
305
306
306 .pullrequestlist {
307 .pullrequestlist {
307 .closed {
308 .closed {
308 background-color: @grey6;
309 background-color: @grey6;
309 }
310 }
310 .td-status {
311 .td-status {
311 padding-left: .5em;
312 padding-left: .5em;
312 }
313 }
313 .log-container .truncate {
314 .log-container .truncate {
314 height: 2.75em;
315 height: 2.75em;
315 white-space: pre-line;
316 white-space: pre-line;
316 }
317 }
317 table.rctable .user {
318 table.rctable .user {
318 padding-left: 0;
319 padding-left: 0;
319 }
320 }
320 table.rctable {
321 table.rctable {
321 td.td-description,
322 td.td-description,
322 .rc-user {
323 .rc-user {
323 min-width: auto;
324 min-width: auto;
324 }
325 }
325 }
326 }
326 }
327 }
327
328
328 // Pull Requests
329 // Pull Requests
329
330
330 .pullrequests_section_head {
331 .pullrequests_section_head {
331 display: block;
332 display: block;
332 clear: both;
333 clear: both;
333 margin: @padding 0;
334 margin: @padding 0;
334 font-family: @text-bold;
335 font-family: @text-bold;
335 }
336 }
336
337
337 .pr-origininfo, .pr-targetinfo {
338 .pr-origininfo, .pr-targetinfo {
338 position: relative;
339 position: relative;
339
340
340 .tag {
341 .tag {
341 display: inline-block;
342 display: inline-block;
342 margin: 0 1em .5em 0;
343 margin: 0 1em .5em 0;
343 }
344 }
344
345
345 .clone-url {
346 .clone-url {
346 display: inline-block;
347 display: inline-block;
347 margin: 0 0 .5em 0;
348 margin: 0 0 .5em 0;
348 padding: 0;
349 padding: 0;
349 line-height: 1.2em;
350 line-height: 1.2em;
350 }
351 }
351 }
352 }
352
353
353 .pr-pullinfo {
354 .pr-pullinfo {
354 clear: both;
355 clear: both;
355 margin: .5em 0;
356 margin: .5em 0;
356 }
357 }
357
358
358 #pr-title-input {
359 #pr-title-input {
359 width: 72%;
360 width: 72%;
360 font-size: 1em;
361 font-size: 1em;
361 font-family: @text-bold;
362 font-family: @text-bold;
362 margin: 0;
363 margin: 0;
363 padding: 0 0 0 @padding/4;
364 padding: 0 0 0 @padding/4;
364 line-height: 1.7em;
365 line-height: 1.7em;
365 color: @text-color;
366 color: @text-color;
366 letter-spacing: .02em;
367 letter-spacing: .02em;
367 }
368 }
368
369
369 #pullrequest_title {
370 #pullrequest_title {
370 width: 100%;
371 width: 100%;
371 box-sizing: border-box;
372 box-sizing: border-box;
372 }
373 }
373
374
374 #pr_open_message {
375 #pr_open_message {
375 border: @border-thickness solid #fff;
376 border: @border-thickness solid #fff;
376 border-radius: @border-radius;
377 border-radius: @border-radius;
377 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
378 padding: @padding-large-vertical @padding-large-vertical @padding-large-vertical 0;
378 text-align: right;
379 text-align: right;
379 overflow: hidden;
380 overflow: hidden;
380 }
381 }
381
382
382 .pr-submit-button {
383 .pr-submit-button {
383 float: right;
384 float: right;
384 margin: 0 0 0 5px;
385 margin: 0 0 0 5px;
385 }
386 }
386
387
387 .pr-spacing-container {
388 .pr-spacing-container {
388 padding: 20px;
389 padding: 20px;
389 clear: both
390 clear: both
390 }
391 }
391
392
392 #pr-description-input {
393 #pr-description-input {
393 margin-bottom: 0;
394 margin-bottom: 0;
394 }
395 }
395
396
396 .pr-description-label {
397 .pr-description-label {
397 vertical-align: top;
398 vertical-align: top;
398 }
399 }
399
400
400 .perms_section_head {
401 .perms_section_head {
401 min-width: 625px;
402 min-width: 625px;
402
403
403 h2 {
404 h2 {
404 margin-bottom: 0;
405 margin-bottom: 0;
405 }
406 }
406
407
407 .label-checkbox {
408 .label-checkbox {
408 float: left;
409 float: left;
409 }
410 }
410
411
411 &.field {
412 &.field {
412 margin: @space 0 @padding;
413 margin: @space 0 @padding;
413 }
414 }
414
415
415 &:first-child.field {
416 &:first-child.field {
416 margin-top: 0;
417 margin-top: 0;
417
418
418 .label {
419 .label {
419 margin-top: 0;
420 margin-top: 0;
420 padding-top: 0;
421 padding-top: 0;
421 }
422 }
422
423
423 .radios {
424 .radios {
424 padding-top: 0;
425 padding-top: 0;
425 }
426 }
426 }
427 }
427
428
428 .radios {
429 .radios {
429 float: right;
430 float: right;
430 position: relative;
431 position: relative;
431 width: 405px;
432 width: 405px;
432 }
433 }
433 }
434 }
434
435
435 //--- MODULES ------------------//
436 //--- MODULES ------------------//
436
437
437
438
438 // Fixed Sidebar Column
439 // Fixed Sidebar Column
439 .sidebar-col-wrapper {
440 .sidebar-col-wrapper {
440 padding-left: @sidebar-all-width;
441 padding-left: @sidebar-all-width;
441
442
442 .sidebar {
443 .sidebar {
443 width: @sidebar-width;
444 width: @sidebar-width;
444 margin-left: -@sidebar-all-width;
445 margin-left: -@sidebar-all-width;
445 }
446 }
446 }
447 }
447
448
448 .sidebar-col-wrapper.scw-small {
449 .sidebar-col-wrapper.scw-small {
449 padding-left: @sidebar-small-all-width;
450 padding-left: @sidebar-small-all-width;
450
451
451 .sidebar {
452 .sidebar {
452 width: @sidebar-small-width;
453 width: @sidebar-small-width;
453 margin-left: -@sidebar-small-all-width;
454 margin-left: -@sidebar-small-all-width;
454 }
455 }
455 }
456 }
456
457
457
458
458 // FOOTER
459 // FOOTER
459 #footer {
460 #footer {
460 padding: 0;
461 padding: 0;
461 text-align: center;
462 text-align: center;
462 vertical-align: middle;
463 vertical-align: middle;
463 color: @grey2;
464 color: @grey2;
464 background-color: @grey6;
465 background-color: @grey6;
465
466
466 p {
467 p {
467 margin: 0;
468 margin: 0;
468 padding: 1em;
469 padding: 1em;
469 line-height: 1em;
470 line-height: 1em;
470 }
471 }
471
472
472 .server-instance { //server instance
473 .server-instance { //server instance
473 display: none;
474 display: none;
474 }
475 }
475
476
476 .title {
477 .title {
477 float: none;
478 float: none;
478 margin: 0 auto;
479 margin: 0 auto;
479 }
480 }
480 }
481 }
481
482
482 button.close {
483 button.close {
483 padding: 0;
484 padding: 0;
484 cursor: pointer;
485 cursor: pointer;
485 background: transparent;
486 background: transparent;
486 border: 0;
487 border: 0;
487 .box-shadow(none);
488 .box-shadow(none);
488 -webkit-appearance: none;
489 -webkit-appearance: none;
489 }
490 }
490
491
491 .close {
492 .close {
492 float: right;
493 float: right;
493 font-size: 21px;
494 font-size: 21px;
494 font-family: @text-bootstrap;
495 font-family: @text-bootstrap;
495 line-height: 1em;
496 line-height: 1em;
496 font-weight: bold;
497 font-weight: bold;
497 color: @grey2;
498 color: @grey2;
498
499
499 &:hover,
500 &:hover,
500 &:focus {
501 &:focus {
501 color: @grey1;
502 color: @grey1;
502 text-decoration: none;
503 text-decoration: none;
503 cursor: pointer;
504 cursor: pointer;
504 }
505 }
505 }
506 }
506
507
507 // GRID
508 // GRID
508 .sorting,
509 .sorting,
509 .sorting_desc,
510 .sorting_desc,
510 .sorting_asc {
511 .sorting_asc {
511 cursor: pointer;
512 cursor: pointer;
512 }
513 }
513 .sorting_desc:after {
514 .sorting_desc:after {
514 content: "\00A0\25B2";
515 content: "\00A0\25B2";
515 font-size: .75em;
516 font-size: .75em;
516 }
517 }
517 .sorting_asc:after {
518 .sorting_asc:after {
518 content: "\00A0\25BC";
519 content: "\00A0\25BC";
519 font-size: .68em;
520 font-size: .68em;
520 }
521 }
521
522
522
523
523 .user_auth_tokens {
524 .user_auth_tokens {
524
525
525 &.truncate {
526 &.truncate {
526 white-space: nowrap;
527 white-space: nowrap;
527 overflow: hidden;
528 overflow: hidden;
528 text-overflow: ellipsis;
529 text-overflow: ellipsis;
529 }
530 }
530
531
531 .fields .field .input {
532 .fields .field .input {
532 margin: 0;
533 margin: 0;
533 }
534 }
534
535
535 input#description {
536 input#description {
536 width: 100px;
537 width: 100px;
537 margin: 0;
538 margin: 0;
538 }
539 }
539
540
540 .drop-menu {
541 .drop-menu {
541 // TODO: johbo: Remove this, should work out of the box when
542 // TODO: johbo: Remove this, should work out of the box when
542 // having multiple inputs inline
543 // having multiple inputs inline
543 margin: 0 0 0 5px;
544 margin: 0 0 0 5px;
544 }
545 }
545 }
546 }
546 #user_list_table {
547 #user_list_table {
547 .closed {
548 .closed {
548 background-color: @grey6;
549 background-color: @grey6;
549 }
550 }
550 }
551 }
551
552
552
553
553 input {
554 input {
554 &.disabled {
555 &.disabled {
555 opacity: .5;
556 opacity: .5;
556 }
557 }
557 }
558 }
558
559
559 // remove extra padding in firefox
560 // remove extra padding in firefox
560 input::-moz-focus-inner { border:0; padding:0 }
561 input::-moz-focus-inner { border:0; padding:0 }
561
562
562 .adjacent input {
563 .adjacent input {
563 margin-bottom: @padding;
564 margin-bottom: @padding;
564 }
565 }
565
566
566 .permissions_boxes {
567 .permissions_boxes {
567 display: block;
568 display: block;
568 }
569 }
569
570
570 //TODO: lisa: this should be in tables
571 //TODO: lisa: this should be in tables
571 .show_more_col {
572 .show_more_col {
572 width: 20px;
573 width: 20px;
573 }
574 }
574
575
575 //FORMS
576 //FORMS
576
577
577 .medium-inline,
578 .medium-inline,
578 input#description.medium-inline {
579 input#description.medium-inline {
579 display: inline;
580 display: inline;
580 width: @medium-inline-input-width;
581 width: @medium-inline-input-width;
581 min-width: 100px;
582 min-width: 100px;
582 }
583 }
583
584
584 select {
585 select {
585 //reset
586 //reset
586 -webkit-appearance: none;
587 -webkit-appearance: none;
587 -moz-appearance: none;
588 -moz-appearance: none;
588
589
589 display: inline-block;
590 display: inline-block;
590 height: 28px;
591 height: 28px;
591 width: auto;
592 width: auto;
592 margin: 0 @padding @padding 0;
593 margin: 0 @padding @padding 0;
593 padding: 0 18px 0 8px;
594 padding: 0 18px 0 8px;
594 line-height:1em;
595 line-height:1em;
595 font-size: @basefontsize;
596 font-size: @basefontsize;
596 border: @border-thickness solid @rcblue;
597 border: @border-thickness solid @rcblue;
597 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
598 background:white url("../images/dt-arrow-dn.png") no-repeat 100% 50%;
598 color: @rcblue;
599 color: @rcblue;
599
600
600 &:after {
601 &:after {
601 content: "\00A0\25BE";
602 content: "\00A0\25BE";
602 }
603 }
603
604
604 &:focus {
605 &:focus {
605 outline: none;
606 outline: none;
606 }
607 }
607 }
608 }
608
609
609 option {
610 option {
610 &:focus {
611 &:focus {
611 outline: none;
612 outline: none;
612 }
613 }
613 }
614 }
614
615
615 input,
616 input,
616 textarea {
617 textarea {
617 padding: @input-padding;
618 padding: @input-padding;
618 border: @input-border-thickness solid @border-highlight-color;
619 border: @input-border-thickness solid @border-highlight-color;
619 .border-radius (@border-radius);
620 .border-radius (@border-radius);
620 font-family: @text-light;
621 font-family: @text-light;
621 font-size: @basefontsize;
622 font-size: @basefontsize;
622
623
623 &.input-sm {
624 &.input-sm {
624 padding: 5px;
625 padding: 5px;
625 }
626 }
626
627
627 &#description {
628 &#description {
628 min-width: @input-description-minwidth;
629 min-width: @input-description-minwidth;
629 min-height: 1em;
630 min-height: 1em;
630 padding: 10px;
631 padding: 10px;
631 }
632 }
632 }
633 }
633
634
634 .field-sm {
635 .field-sm {
635 input,
636 input,
636 textarea {
637 textarea {
637 padding: 5px;
638 padding: 5px;
638 }
639 }
639 }
640 }
640
641
641 textarea {
642 textarea {
642 display: block;
643 display: block;
643 clear: both;
644 clear: both;
644 width: 100%;
645 width: 100%;
645 min-height: 100px;
646 min-height: 100px;
646 margin-bottom: @padding;
647 margin-bottom: @padding;
647 .box-sizing(border-box);
648 .box-sizing(border-box);
648 overflow: auto;
649 overflow: auto;
649 }
650 }
650
651
651 label {
652 label {
652 font-family: @text-light;
653 font-family: @text-light;
653 }
654 }
654
655
655 // GRAVATARS
656 // GRAVATARS
656 // centers gravatar on username to the right
657 // centers gravatar on username to the right
657
658
658 .gravatar {
659 .gravatar {
659 display: inline;
660 display: inline;
660 min-width: 16px;
661 min-width: 16px;
661 min-height: 16px;
662 min-height: 16px;
662 margin: -5px 0;
663 margin: -5px 0;
663 padding: 0;
664 padding: 0;
664 line-height: 1em;
665 line-height: 1em;
665 border: 1px solid @grey4;
666 border: 1px solid @grey4;
666
667
667 &.gravatar-large {
668 &.gravatar-large {
668 margin: -0.5em .25em -0.5em 0;
669 margin: -0.5em .25em -0.5em 0;
669 }
670 }
670
671
671 & + .user {
672 & + .user {
672 display: inline;
673 display: inline;
673 margin: 0;
674 margin: 0;
674 padding: 0 0 0 .17em;
675 padding: 0 0 0 .17em;
675 line-height: 1em;
676 line-height: 1em;
676 }
677 }
677 }
678 }
678
679
679 .user-inline-data {
680 .user-inline-data {
680 display: inline-block;
681 display: inline-block;
681 float: left;
682 float: left;
682 padding-left: .5em;
683 padding-left: .5em;
683 line-height: 1.3em;
684 line-height: 1.3em;
684 }
685 }
685
686
686 .rc-user { // gravatar + user wrapper
687 .rc-user { // gravatar + user wrapper
687 float: left;
688 float: left;
688 position: relative;
689 position: relative;
689 min-width: 100px;
690 min-width: 100px;
690 max-width: 200px;
691 max-width: 200px;
691 min-height: (@gravatar-size + @border-thickness * 2); // account for border
692 min-height: (@gravatar-size + @border-thickness * 2); // account for border
692 display: block;
693 display: block;
693 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
694 padding: 0 0 0 (@gravatar-size + @basefontsize/2 + @border-thickness * 2);
694
695
695
696
696 .gravatar {
697 .gravatar {
697 display: block;
698 display: block;
698 position: absolute;
699 position: absolute;
699 top: 0;
700 top: 0;
700 left: 0;
701 left: 0;
701 min-width: @gravatar-size;
702 min-width: @gravatar-size;
702 min-height: @gravatar-size;
703 min-height: @gravatar-size;
703 margin: 0;
704 margin: 0;
704 }
705 }
705
706
706 .user {
707 .user {
707 display: block;
708 display: block;
708 max-width: 175px;
709 max-width: 175px;
709 padding-top: 2px;
710 padding-top: 2px;
710 overflow: hidden;
711 overflow: hidden;
711 text-overflow: ellipsis;
712 text-overflow: ellipsis;
712 }
713 }
713 }
714 }
714
715
715 .gist-gravatar,
716 .gist-gravatar,
716 .journal_container {
717 .journal_container {
717 .gravatar-large {
718 .gravatar-large {
718 margin: 0 .5em -10px 0;
719 margin: 0 .5em -10px 0;
719 }
720 }
720 }
721 }
721
722
722
723
723 // ADMIN SETTINGS
724 // ADMIN SETTINGS
724
725
725 // Tag Patterns
726 // Tag Patterns
726 .tag_patterns {
727 .tag_patterns {
727 .tag_input {
728 .tag_input {
728 margin-bottom: @padding;
729 margin-bottom: @padding;
729 }
730 }
730 }
731 }
731
732
732 .locked_input {
733 .locked_input {
733 position: relative;
734 position: relative;
734
735
735 input {
736 input {
736 display: inline;
737 display: inline;
737 margin-top: 3px;
738 margin-top: 3px;
738 }
739 }
739
740
740 br {
741 br {
741 display: none;
742 display: none;
742 }
743 }
743
744
744 .error-message {
745 .error-message {
745 float: left;
746 float: left;
746 width: 100%;
747 width: 100%;
747 }
748 }
748
749
749 .lock_input_button {
750 .lock_input_button {
750 display: inline;
751 display: inline;
751 }
752 }
752
753
753 .help-block {
754 .help-block {
754 clear: both;
755 clear: both;
755 }
756 }
756 }
757 }
757
758
758 // Notifications
759 // Notifications
759
760
760 .notifications_buttons {
761 .notifications_buttons {
761 margin: 0 0 @space 0;
762 margin: 0 0 @space 0;
762 padding: 0;
763 padding: 0;
763
764
764 .btn {
765 .btn {
765 display: inline-block;
766 display: inline-block;
766 }
767 }
767 }
768 }
768
769
769 .notification-list {
770 .notification-list {
770
771
771 div {
772 div {
772 display: inline-block;
773 display: inline-block;
773 vertical-align: middle;
774 vertical-align: middle;
774 }
775 }
775
776
776 .container {
777 .container {
777 display: block;
778 display: block;
778 margin: 0 0 @padding 0;
779 margin: 0 0 @padding 0;
779 }
780 }
780
781
781 .delete-notifications {
782 .delete-notifications {
782 margin-left: @padding;
783 margin-left: @padding;
783 text-align: right;
784 text-align: right;
784 cursor: pointer;
785 cursor: pointer;
785 }
786 }
786
787
787 .read-notifications {
788 .read-notifications {
788 margin-left: @padding/2;
789 margin-left: @padding/2;
789 text-align: right;
790 text-align: right;
790 width: 35px;
791 width: 35px;
791 cursor: pointer;
792 cursor: pointer;
792 }
793 }
793
794
794 .icon-minus-sign {
795 .icon-minus-sign {
795 color: @alert2;
796 color: @alert2;
796 }
797 }
797
798
798 .icon-ok-sign {
799 .icon-ok-sign {
799 color: @alert1;
800 color: @alert1;
800 }
801 }
801 }
802 }
802
803
803 .user_settings {
804 .user_settings {
804 float: left;
805 float: left;
805 clear: both;
806 clear: both;
806 display: block;
807 display: block;
807 width: 100%;
808 width: 100%;
808
809
809 .gravatar_box {
810 .gravatar_box {
810 margin-bottom: @padding;
811 margin-bottom: @padding;
811
812
812 &:after {
813 &:after {
813 content: " ";
814 content: " ";
814 clear: both;
815 clear: both;
815 width: 100%;
816 width: 100%;
816 }
817 }
817 }
818 }
818
819
819 .fields .field {
820 .fields .field {
820 clear: both;
821 clear: both;
821 }
822 }
822 }
823 }
823
824
824 .advanced_settings {
825 .advanced_settings {
825 margin-bottom: @space;
826 margin-bottom: @space;
826
827
827 .help-block {
828 .help-block {
828 margin-left: 0;
829 margin-left: 0;
829 }
830 }
830
831
831 button + .help-block {
832 button + .help-block {
832 margin-top: @padding;
833 margin-top: @padding;
833 }
834 }
834 }
835 }
835
836
836 // admin settings radio buttons and labels
837 // admin settings radio buttons and labels
837 .label-2 {
838 .label-2 {
838 float: left;
839 float: left;
839 width: @label2-width;
840 width: @label2-width;
840
841
841 label {
842 label {
842 color: @grey1;
843 color: @grey1;
843 }
844 }
844 }
845 }
845 .checkboxes {
846 .checkboxes {
846 float: left;
847 float: left;
847 width: @checkboxes-width;
848 width: @checkboxes-width;
848 margin-bottom: @padding;
849 margin-bottom: @padding;
849
850
850 .checkbox {
851 .checkbox {
851 width: 100%;
852 width: 100%;
852
853
853 label {
854 label {
854 margin: 0;
855 margin: 0;
855 padding: 0;
856 padding: 0;
856 }
857 }
857 }
858 }
858
859
859 .checkbox + .checkbox {
860 .checkbox + .checkbox {
860 display: inline-block;
861 display: inline-block;
861 }
862 }
862
863
863 label {
864 label {
864 margin-right: 1em;
865 margin-right: 1em;
865 }
866 }
866 }
867 }
867
868
868 // CHANGELOG
869 // CHANGELOG
869 .container_header {
870 .container_header {
870 float: left;
871 float: left;
871 display: block;
872 display: block;
872 width: 100%;
873 width: 100%;
873 margin: @padding 0 @padding;
874 margin: @padding 0 @padding;
874
875
875 #filter_changelog {
876 #filter_changelog {
876 float: left;
877 float: left;
877 margin-right: @padding;
878 margin-right: @padding;
878 }
879 }
879
880
880 .breadcrumbs_light {
881 .breadcrumbs_light {
881 display: inline-block;
882 display: inline-block;
882 }
883 }
883 }
884 }
884
885
885 .info_box {
886 .info_box {
886 float: right;
887 float: right;
887 }
888 }
888
889
889
890
890 #graph_nodes {
891 #graph_nodes {
891 padding-top: 43px;
892 padding-top: 43px;
892 }
893 }
893
894
894 #graph_content{
895 #graph_content{
895
896
896 // adjust for table headers so that graph renders properly
897 // adjust for table headers so that graph renders properly
897 // #graph_nodes padding - table cell padding
898 // #graph_nodes padding - table cell padding
898 padding-top: (@space - (@basefontsize * 2.4));
899 padding-top: (@space - (@basefontsize * 2.4));
899
900
900 &.graph_full_width {
901 &.graph_full_width {
901 width: 100%;
902 width: 100%;
902 max-width: 100%;
903 max-width: 100%;
903 }
904 }
904 }
905 }
905
906
906 #graph {
907 #graph {
907 .flag_status {
908 .flag_status {
908 margin: 0;
909 margin: 0;
909 }
910 }
910
911
911 .pagination-left {
912 .pagination-left {
912 float: left;
913 float: left;
913 clear: both;
914 clear: both;
914 }
915 }
915
916
916 .log-container {
917 .log-container {
917 max-width: 345px;
918 max-width: 345px;
918
919
919 .message{
920 .message{
920 max-width: 340px;
921 max-width: 340px;
921 }
922 }
922 }
923 }
923
924
924 .graph-col-wrapper {
925 .graph-col-wrapper {
925 padding-left: 110px;
926 padding-left: 110px;
926
927
927 #graph_nodes {
928 #graph_nodes {
928 width: 100px;
929 width: 100px;
929 margin-left: -110px;
930 margin-left: -110px;
930 float: left;
931 float: left;
931 clear: left;
932 clear: left;
932 }
933 }
933 }
934 }
934 }
935 }
935
936
936 #filter_changelog {
937 #filter_changelog {
937 float: left;
938 float: left;
938 }
939 }
939
940
940
941
941 //--- THEME ------------------//
942 //--- THEME ------------------//
942
943
943 #logo {
944 #logo {
944 float: left;
945 float: left;
945 margin: 9px 0 0 0;
946 margin: 9px 0 0 0;
946
947
947 .header {
948 .header {
948 background-color: transparent;
949 background-color: transparent;
949 }
950 }
950
951
951 a {
952 a {
952 display: inline-block;
953 display: inline-block;
953 }
954 }
954
955
955 img {
956 img {
956 height:30px;
957 height:30px;
957 }
958 }
958 }
959 }
959
960
960 .logo-wrapper {
961 .logo-wrapper {
961 float:left;
962 float:left;
962 }
963 }
963
964
964 .branding{
965 .branding{
965 float: left;
966 float: left;
966 padding: 9px 2px;
967 padding: 9px 2px;
967 line-height: 1em;
968 line-height: 1em;
968 font-size: @navigation-fontsize;
969 font-size: @navigation-fontsize;
969 }
970 }
970
971
971 img {
972 img {
972 border: none;
973 border: none;
973 outline: none;
974 outline: none;
974 }
975 }
975 user-profile-header
976 user-profile-header
976 label {
977 label {
977
978
978 input[type="checkbox"] {
979 input[type="checkbox"] {
979 margin-right: 1em;
980 margin-right: 1em;
980 }
981 }
981 input[type="radio"] {
982 input[type="radio"] {
982 margin-right: 1em;
983 margin-right: 1em;
983 }
984 }
984 }
985 }
985
986
986 .flag_status {
987 .flag_status {
987 margin: 2px 8px 6px 2px;
988 margin: 2px 8px 6px 2px;
988 &.under_review {
989 &.under_review {
989 .circle(5px, @alert3);
990 .circle(5px, @alert3);
990 }
991 }
991 &.approved {
992 &.approved {
992 .circle(5px, @alert1);
993 .circle(5px, @alert1);
993 }
994 }
994 &.rejected,
995 &.rejected,
995 &.forced_closed{
996 &.forced_closed{
996 .circle(5px, @alert2);
997 .circle(5px, @alert2);
997 }
998 }
998 &.not_reviewed {
999 &.not_reviewed {
999 .circle(5px, @grey5);
1000 .circle(5px, @grey5);
1000 }
1001 }
1001 }
1002 }
1002
1003
1003 .flag_status_comment_box {
1004 .flag_status_comment_box {
1004 margin: 5px 6px 0px 2px;
1005 margin: 5px 6px 0px 2px;
1005 }
1006 }
1006 .test_pattern_preview {
1007 .test_pattern_preview {
1007 margin: @space 0;
1008 margin: @space 0;
1008
1009
1009 p {
1010 p {
1010 margin-bottom: 0;
1011 margin-bottom: 0;
1011 border-bottom: @border-thickness solid @border-default-color;
1012 border-bottom: @border-thickness solid @border-default-color;
1012 color: @grey3;
1013 color: @grey3;
1013 }
1014 }
1014
1015
1015 .btn {
1016 .btn {
1016 margin-bottom: @padding;
1017 margin-bottom: @padding;
1017 }
1018 }
1018 }
1019 }
1019 #test_pattern_result {
1020 #test_pattern_result {
1020 display: none;
1021 display: none;
1021 &:extend(pre);
1022 &:extend(pre);
1022 padding: .9em;
1023 padding: .9em;
1023 color: @grey3;
1024 color: @grey3;
1024 background-color: @grey7;
1025 background-color: @grey7;
1025 border-right: @border-thickness solid @border-default-color;
1026 border-right: @border-thickness solid @border-default-color;
1026 border-bottom: @border-thickness solid @border-default-color;
1027 border-bottom: @border-thickness solid @border-default-color;
1027 border-left: @border-thickness solid @border-default-color;
1028 border-left: @border-thickness solid @border-default-color;
1028 }
1029 }
1029
1030
1030 #repo_vcs_settings {
1031 #repo_vcs_settings {
1031 #inherit_overlay_vcs_default {
1032 #inherit_overlay_vcs_default {
1032 display: none;
1033 display: none;
1033 }
1034 }
1034 #inherit_overlay_vcs_custom {
1035 #inherit_overlay_vcs_custom {
1035 display: custom;
1036 display: custom;
1036 }
1037 }
1037 &.inherited {
1038 &.inherited {
1038 #inherit_overlay_vcs_default {
1039 #inherit_overlay_vcs_default {
1039 display: block;
1040 display: block;
1040 }
1041 }
1041 #inherit_overlay_vcs_custom {
1042 #inherit_overlay_vcs_custom {
1042 display: none;
1043 display: none;
1043 }
1044 }
1044 }
1045 }
1045 }
1046 }
1046
1047
1047 .issue-tracker-link {
1048 .issue-tracker-link {
1048 color: @rcblue;
1049 color: @rcblue;
1049 }
1050 }
1050
1051
1051 // Issue Tracker Table Show/Hide
1052 // Issue Tracker Table Show/Hide
1052 #repo_issue_tracker {
1053 #repo_issue_tracker {
1053 #inherit_overlay {
1054 #inherit_overlay {
1054 display: none;
1055 display: none;
1055 }
1056 }
1056 #custom_overlay {
1057 #custom_overlay {
1057 display: custom;
1058 display: custom;
1058 }
1059 }
1059 &.inherited {
1060 &.inherited {
1060 #inherit_overlay {
1061 #inherit_overlay {
1061 display: block;
1062 display: block;
1062 }
1063 }
1063 #custom_overlay {
1064 #custom_overlay {
1064 display: none;
1065 display: none;
1065 }
1066 }
1066 }
1067 }
1067 }
1068 }
1068 table.issuetracker {
1069 table.issuetracker {
1069 &.readonly {
1070 &.readonly {
1070 tr, td {
1071 tr, td {
1071 color: @grey3;
1072 color: @grey3;
1072 }
1073 }
1073 }
1074 }
1074 .edit {
1075 .edit {
1075 display: none;
1076 display: none;
1076 }
1077 }
1077 .editopen {
1078 .editopen {
1078 .edit {
1079 .edit {
1079 display: inline;
1080 display: inline;
1080 }
1081 }
1081 .entry {
1082 .entry {
1082 display: none;
1083 display: none;
1083 }
1084 }
1084 }
1085 }
1085 tr td.td-action {
1086 tr td.td-action {
1086 min-width: 117px;
1087 min-width: 117px;
1087 }
1088 }
1088 td input {
1089 td input {
1089 max-width: none;
1090 max-width: none;
1090 min-width: 30px;
1091 min-width: 30px;
1091 width: 80%;
1092 width: 80%;
1092 }
1093 }
1093 .issuetracker_pref input {
1094 .issuetracker_pref input {
1094 width: 40%;
1095 width: 40%;
1095 }
1096 }
1096 input.edit_issuetracker_update {
1097 input.edit_issuetracker_update {
1097 margin-right: 0;
1098 margin-right: 0;
1098 width: auto;
1099 width: auto;
1099 }
1100 }
1100 }
1101 }
1101
1102
1102
1103
1103 //Permissions Settings
1104 //Permissions Settings
1104 #add_perm {
1105 #add_perm {
1105 margin: 0 0 @padding;
1106 margin: 0 0 @padding;
1106 cursor: pointer;
1107 cursor: pointer;
1107 }
1108 }
1108
1109
1109 .perm_ac {
1110 .perm_ac {
1110 input {
1111 input {
1111 width: 95%;
1112 width: 95%;
1112 }
1113 }
1113 }
1114 }
1114
1115
1115 .autocomplete-suggestions {
1116 .autocomplete-suggestions {
1116 width: auto !important; // overrides autocomplete.js
1117 width: auto !important; // overrides autocomplete.js
1117 margin: 0;
1118 margin: 0;
1118 border: @border-thickness solid @rcblue;
1119 border: @border-thickness solid @rcblue;
1119 border-radius: @border-radius;
1120 border-radius: @border-radius;
1120 color: @rcblue;
1121 color: @rcblue;
1121 background-color: white;
1122 background-color: white;
1122 }
1123 }
1123 .autocomplete-selected {
1124 .autocomplete-selected {
1124 background: #F0F0F0;
1125 background: #F0F0F0;
1125 }
1126 }
1126 .ac-container-wrap {
1127 .ac-container-wrap {
1127 margin: 0;
1128 margin: 0;
1128 padding: 8px;
1129 padding: 8px;
1129 border-bottom: @border-thickness solid @rclightblue;
1130 border-bottom: @border-thickness solid @rclightblue;
1130 list-style-type: none;
1131 list-style-type: none;
1131 cursor: pointer;
1132 cursor: pointer;
1132
1133
1133 &:hover {
1134 &:hover {
1134 background-color: @rclightblue;
1135 background-color: @rclightblue;
1135 }
1136 }
1136
1137
1137 img {
1138 img {
1138 margin-right: 1em;
1139 margin-right: 1em;
1139 }
1140 }
1140
1141
1141 strong {
1142 strong {
1142 font-weight: normal;
1143 font-weight: normal;
1143 }
1144 }
1144 }
1145 }
1145
1146
1146 // Settings Dropdown
1147 // Settings Dropdown
1147 .user-menu .container {
1148 .user-menu .container {
1148 padding: 0 4px;
1149 padding: 0 4px;
1149 margin: 0;
1150 margin: 0;
1150 }
1151 }
1151
1152
1152 .user-menu .gravatar {
1153 .user-menu .gravatar {
1153 cursor: pointer;
1154 cursor: pointer;
1154 }
1155 }
1155
1156
1156 .codeblock {
1157 .codeblock {
1157 margin-bottom: @padding;
1158 margin-bottom: @padding;
1158 clear: both;
1159 clear: both;
1159
1160
1160 .stats{
1161 .stats{
1161 overflow: hidden;
1162 overflow: hidden;
1162 }
1163 }
1163
1164
1164 .message{
1165 .message{
1165 textarea{
1166 textarea{
1166 margin: 0;
1167 margin: 0;
1167 }
1168 }
1168 }
1169 }
1169
1170
1170 .code-header {
1171 .code-header {
1171 .stats {
1172 .stats {
1172 line-height: 2em;
1173 line-height: 2em;
1173
1174
1174 .revision_id {
1175 .revision_id {
1175 margin-left: 0;
1176 margin-left: 0;
1176 }
1177 }
1177 .buttons {
1178 .buttons {
1178 padding-right: 0;
1179 padding-right: 0;
1179 }
1180 }
1180 }
1181 }
1181
1182
1182 .item{
1183 .item{
1183 margin-right: 0.5em;
1184 margin-right: 0.5em;
1184 }
1185 }
1185 }
1186 }
1186
1187
1187 #editor_container{
1188 #editor_container{
1188 position: relative;
1189 position: relative;
1189 margin: @padding;
1190 margin: @padding;
1190 }
1191 }
1191 }
1192 }
1192
1193
1193 #file_history_container {
1194 #file_history_container {
1194 display: none;
1195 display: none;
1195 }
1196 }
1196
1197
1197 .file-history-inner {
1198 .file-history-inner {
1198 margin-bottom: 10px;
1199 margin-bottom: 10px;
1199 }
1200 }
1200
1201
1201 // Pull Requests
1202 // Pull Requests
1202 .summary-details {
1203 .summary-details {
1203 width: 72%;
1204 width: 72%;
1204 }
1205 }
1205 .pr-summary {
1206 .pr-summary {
1206 border-bottom: @border-thickness solid @grey5;
1207 border-bottom: @border-thickness solid @grey5;
1207 margin-bottom: @space;
1208 margin-bottom: @space;
1208 }
1209 }
1209 .reviewers-title {
1210 .reviewers-title {
1210 width: 25%;
1211 width: 25%;
1211 min-width: 200px;
1212 min-width: 200px;
1212 }
1213 }
1213 .reviewers {
1214 .reviewers {
1214 width: 25%;
1215 width: 25%;
1215 min-width: 200px;
1216 min-width: 200px;
1216 }
1217 }
1217 .reviewers ul li {
1218 .reviewers ul li {
1218 position: relative;
1219 position: relative;
1219 width: 100%;
1220 width: 100%;
1220 margin-bottom: 8px;
1221 margin-bottom: 8px;
1221 }
1222 }
1222 .reviewers_member {
1223 .reviewers_member {
1223 width: 100%;
1224 width: 100%;
1224 overflow: auto;
1225 overflow: auto;
1225 }
1226 }
1226 .reviewer_status {
1227 .reviewer_status {
1227 display: inline-block;
1228 display: inline-block;
1228 vertical-align: top;
1229 vertical-align: top;
1229 width: 7%;
1230 width: 7%;
1230 min-width: 20px;
1231 min-width: 20px;
1231 height: 1.2em;
1232 height: 1.2em;
1232 margin-top: 3px;
1233 margin-top: 3px;
1233 line-height: 1em;
1234 line-height: 1em;
1234 }
1235 }
1235
1236
1236 .reviewer_name {
1237 .reviewer_name {
1237 display: inline-block;
1238 display: inline-block;
1238 max-width: 83%;
1239 max-width: 83%;
1239 padding-right: 20px;
1240 padding-right: 20px;
1240 vertical-align: middle;
1241 vertical-align: middle;
1241 line-height: 1;
1242 line-height: 1;
1242
1243
1243 .rc-user {
1244 .rc-user {
1244 min-width: 0;
1245 min-width: 0;
1245 margin: -2px 1em 0 0;
1246 margin: -2px 1em 0 0;
1246 }
1247 }
1247
1248
1248 .reviewer {
1249 .reviewer {
1249 float: left;
1250 float: left;
1250 }
1251 }
1251
1252
1252 &.to-delete {
1253 &.to-delete {
1253 .user,
1254 .user,
1254 .reviewer {
1255 .reviewer {
1255 text-decoration: line-through;
1256 text-decoration: line-through;
1256 }
1257 }
1257 }
1258 }
1258 }
1259 }
1259
1260
1260 .reviewer_member_remove {
1261 .reviewer_member_remove {
1261 position: absolute;
1262 position: absolute;
1262 right: 0;
1263 right: 0;
1263 top: 0;
1264 top: 0;
1264 width: 16px;
1265 width: 16px;
1265 margin-bottom: 10px;
1266 margin-bottom: 10px;
1266 padding: 0;
1267 padding: 0;
1267 color: black;
1268 color: black;
1268 }
1269 }
1269 .reviewer_member_status {
1270 .reviewer_member_status {
1270 margin-top: 5px;
1271 margin-top: 5px;
1271 }
1272 }
1272 .pr-summary #summary{
1273 .pr-summary #summary{
1273 width: 100%;
1274 width: 100%;
1274 }
1275 }
1275 .pr-summary .action_button:hover {
1276 .pr-summary .action_button:hover {
1276 border: 0;
1277 border: 0;
1277 cursor: pointer;
1278 cursor: pointer;
1278 }
1279 }
1279 .pr-details-title {
1280 .pr-details-title {
1280 padding-bottom: 8px;
1281 padding-bottom: 8px;
1281 border-bottom: @border-thickness solid @grey5;
1282 border-bottom: @border-thickness solid @grey5;
1282 .action_button {
1283 .action_button {
1283 color: @rcblue;
1284 color: @rcblue;
1284 }
1285 }
1285 }
1286 }
1286 .pr-details-content {
1287 .pr-details-content {
1287 margin-top: @textmargin;
1288 margin-top: @textmargin;
1288 margin-bottom: @textmargin;
1289 margin-bottom: @textmargin;
1289 }
1290 }
1290 .pr-description {
1291 .pr-description {
1291 white-space:pre-wrap;
1292 white-space:pre-wrap;
1292 }
1293 }
1293 .group_members {
1294 .group_members {
1294 margin-top: 0;
1295 margin-top: 0;
1295 padding: 0;
1296 padding: 0;
1296 list-style: outside none none;
1297 list-style: outside none none;
1297 }
1298 }
1298 .reviewer_ac .ac-input {
1299 .reviewer_ac .ac-input {
1299 width: 92%;
1300 width: 92%;
1300 margin-bottom: 1em;
1301 margin-bottom: 1em;
1301 }
1302 }
1302 #update_commits {
1303 #update_commits {
1303 float: right;
1304 float: right;
1304 }
1305 }
1305 .compare_view_commits tr{
1306 .compare_view_commits tr{
1306 height: 20px;
1307 height: 20px;
1307 }
1308 }
1308 .compare_view_commits td {
1309 .compare_view_commits td {
1309 vertical-align: top;
1310 vertical-align: top;
1310 padding-top: 10px;
1311 padding-top: 10px;
1311 }
1312 }
1312 .compare_view_commits .author {
1313 .compare_view_commits .author {
1313 margin-left: 5px;
1314 margin-left: 5px;
1314 }
1315 }
1315
1316
1316 .compare_view_files {
1317 .compare_view_files {
1317 width: 100%;
1318 width: 100%;
1318
1319
1319 td {
1320 td {
1320 vertical-align: middle;
1321 vertical-align: middle;
1321 }
1322 }
1322 }
1323 }
1323
1324
1324 .compare_view_filepath {
1325 .compare_view_filepath {
1325 color: @grey1;
1326 color: @grey1;
1326 }
1327 }
1327
1328
1328 .show_more {
1329 .show_more {
1329 display: inline-block;
1330 display: inline-block;
1330 position: relative;
1331 position: relative;
1331 vertical-align: middle;
1332 vertical-align: middle;
1332 width: 4px;
1333 width: 4px;
1333 height: @basefontsize;
1334 height: @basefontsize;
1334
1335
1335 &:after {
1336 &:after {
1336 content: "\00A0\25BE";
1337 content: "\00A0\25BE";
1337 display: inline-block;
1338 display: inline-block;
1338 width:10px;
1339 width:10px;
1339 line-height: 5px;
1340 line-height: 5px;
1340 font-size: 12px;
1341 font-size: 12px;
1341 cursor: pointer;
1342 cursor: pointer;
1342 }
1343 }
1343 }
1344 }
1344
1345
1345 .journal_more .show_more {
1346 .journal_more .show_more {
1346 display: inline;
1347 display: inline;
1347
1348
1348 &:after {
1349 &:after {
1349 content: none;
1350 content: none;
1350 }
1351 }
1351 }
1352 }
1352
1353
1353 .open .show_more:after,
1354 .open .show_more:after,
1354 .select2-dropdown-open .show_more:after {
1355 .select2-dropdown-open .show_more:after {
1355 .rotate(180deg);
1356 .rotate(180deg);
1356 margin-left: 4px;
1357 margin-left: 4px;
1357 }
1358 }
1358
1359
1359
1360
1360 .compare_view_commits .collapse_commit:after {
1361 .compare_view_commits .collapse_commit:after {
1361 cursor: pointer;
1362 cursor: pointer;
1362 content: "\00A0\25B4";
1363 content: "\00A0\25B4";
1363 margin-left: -3px;
1364 margin-left: -3px;
1364 font-size: 17px;
1365 font-size: 17px;
1365 color: @grey4;
1366 color: @grey4;
1366 }
1367 }
1367
1368
1368 .diff_links {
1369 .diff_links {
1369 margin-left: 8px;
1370 margin-left: 8px;
1370 }
1371 }
1371
1372
1372 p.ancestor {
1373 p.ancestor {
1373 margin: @padding 0;
1374 margin: @padding 0;
1374 }
1375 }
1375
1376
1376 .cs_icon_td input[type="checkbox"] {
1377 .cs_icon_td input[type="checkbox"] {
1377 display: none;
1378 display: none;
1378 }
1379 }
1379
1380
1380 .cs_icon_td .expand_file_icon:after {
1381 .cs_icon_td .expand_file_icon:after {
1381 cursor: pointer;
1382 cursor: pointer;
1382 content: "\00A0\25B6";
1383 content: "\00A0\25B6";
1383 font-size: 12px;
1384 font-size: 12px;
1384 color: @grey4;
1385 color: @grey4;
1385 }
1386 }
1386
1387
1387 .cs_icon_td .collapse_file_icon:after {
1388 .cs_icon_td .collapse_file_icon:after {
1388 cursor: pointer;
1389 cursor: pointer;
1389 content: "\00A0\25BC";
1390 content: "\00A0\25BC";
1390 font-size: 12px;
1391 font-size: 12px;
1391 color: @grey4;
1392 color: @grey4;
1392 }
1393 }
1393
1394
1394 /*new binary
1395 /*new binary
1395 NEW_FILENODE = 1
1396 NEW_FILENODE = 1
1396 DEL_FILENODE = 2
1397 DEL_FILENODE = 2
1397 MOD_FILENODE = 3
1398 MOD_FILENODE = 3
1398 RENAMED_FILENODE = 4
1399 RENAMED_FILENODE = 4
1399 COPIED_FILENODE = 5
1400 COPIED_FILENODE = 5
1400 CHMOD_FILENODE = 6
1401 CHMOD_FILENODE = 6
1401 BIN_FILENODE = 7
1402 BIN_FILENODE = 7
1402 */
1403 */
1403 .cs_files_expand {
1404 .cs_files_expand {
1404 font-size: @basefontsize + 5px;
1405 font-size: @basefontsize + 5px;
1405 line-height: 1.8em;
1406 line-height: 1.8em;
1406 float: right;
1407 float: right;
1407 }
1408 }
1408
1409
1409 .cs_files_expand span{
1410 .cs_files_expand span{
1410 color: @rcblue;
1411 color: @rcblue;
1411 cursor: pointer;
1412 cursor: pointer;
1412 }
1413 }
1413 .cs_files {
1414 .cs_files {
1414 clear: both;
1415 clear: both;
1415 padding-bottom: @padding;
1416 padding-bottom: @padding;
1416
1417
1417 .cur_cs {
1418 .cur_cs {
1418 margin: 10px 2px;
1419 margin: 10px 2px;
1419 font-weight: bold;
1420 font-weight: bold;
1420 }
1421 }
1421
1422
1422 .node {
1423 .node {
1423 float: left;
1424 float: left;
1424 }
1425 }
1425
1426
1426 .changes {
1427 .changes {
1427 float: right;
1428 float: right;
1428 color: white;
1429 color: white;
1429 font-size: @basefontsize - 4px;
1430 font-size: @basefontsize - 4px;
1430 margin-top: 4px;
1431 margin-top: 4px;
1431 opacity: 0.6;
1432 opacity: 0.6;
1432 filter: Alpha(opacity=60); /* IE8 and earlier */
1433 filter: Alpha(opacity=60); /* IE8 and earlier */
1433
1434
1434 .added {
1435 .added {
1435 background-color: @alert1;
1436 background-color: @alert1;
1436 float: left;
1437 float: left;
1437 text-align: center;
1438 text-align: center;
1438 }
1439 }
1439
1440
1440 .deleted {
1441 .deleted {
1441 background-color: @alert2;
1442 background-color: @alert2;
1442 float: left;
1443 float: left;
1443 text-align: center;
1444 text-align: center;
1444 }
1445 }
1445
1446
1446 .bin {
1447 .bin {
1447 background-color: @alert1;
1448 background-color: @alert1;
1448 text-align: center;
1449 text-align: center;
1449 }
1450 }
1450
1451
1451 /*new binary*/
1452 /*new binary*/
1452 .bin.bin1 {
1453 .bin.bin1 {
1453 background-color: @alert1;
1454 background-color: @alert1;
1454 text-align: center;
1455 text-align: center;
1455 }
1456 }
1456
1457
1457 /*deleted binary*/
1458 /*deleted binary*/
1458 .bin.bin2 {
1459 .bin.bin2 {
1459 background-color: @alert2;
1460 background-color: @alert2;
1460 text-align: center;
1461 text-align: center;
1461 }
1462 }
1462
1463
1463 /*mod binary*/
1464 /*mod binary*/
1464 .bin.bin3 {
1465 .bin.bin3 {
1465 background-color: @grey2;
1466 background-color: @grey2;
1466 text-align: center;
1467 text-align: center;
1467 }
1468 }
1468
1469
1469 /*rename file*/
1470 /*rename file*/
1470 .bin.bin4 {
1471 .bin.bin4 {
1471 background-color: @alert4;
1472 background-color: @alert4;
1472 text-align: center;
1473 text-align: center;
1473 }
1474 }
1474
1475
1475 /*copied file*/
1476 /*copied file*/
1476 .bin.bin5 {
1477 .bin.bin5 {
1477 background-color: @alert4;
1478 background-color: @alert4;
1478 text-align: center;
1479 text-align: center;
1479 }
1480 }
1480
1481
1481 /*chmod file*/
1482 /*chmod file*/
1482 .bin.bin6 {
1483 .bin.bin6 {
1483 background-color: @grey2;
1484 background-color: @grey2;
1484 text-align: center;
1485 text-align: center;
1485 }
1486 }
1486 }
1487 }
1487 }
1488 }
1488
1489
1489 .cs_files .cs_added, .cs_files .cs_A,
1490 .cs_files .cs_added, .cs_files .cs_A,
1490 .cs_files .cs_added, .cs_files .cs_M,
1491 .cs_files .cs_added, .cs_files .cs_M,
1491 .cs_files .cs_added, .cs_files .cs_D {
1492 .cs_files .cs_added, .cs_files .cs_D {
1492 height: 16px;
1493 height: 16px;
1493 padding-right: 10px;
1494 padding-right: 10px;
1494 margin-top: 7px;
1495 margin-top: 7px;
1495 text-align: left;
1496 text-align: left;
1496 }
1497 }
1497
1498
1498 .cs_icon_td {
1499 .cs_icon_td {
1499 min-width: 16px;
1500 min-width: 16px;
1500 width: 16px;
1501 width: 16px;
1501 }
1502 }
1502
1503
1503 .pull-request-merge {
1504 .pull-request-merge {
1504 padding: 10px 0;
1505 padding: 10px 0;
1505 margin-top: 10px;
1506 margin-top: 10px;
1506 margin-bottom: 20px;
1507 margin-bottom: 20px;
1507 }
1508 }
1508
1509
1509 .pull-request-merge .pull-request-wrap {
1510 .pull-request-merge .pull-request-wrap {
1510 height: 25px;
1511 height: 25px;
1511 padding: 5px 0;
1512 padding: 5px 0;
1512 }
1513 }
1513
1514
1514 .pull-request-merge span {
1515 .pull-request-merge span {
1515 margin-right: 10px;
1516 margin-right: 10px;
1516 }
1517 }
1517 #close_pull_request {
1518 #close_pull_request {
1518 margin-right: 0px;
1519 margin-right: 0px;
1519 }
1520 }
1520
1521
1521 .empty_data {
1522 .empty_data {
1522 color: @grey4;
1523 color: @grey4;
1523 }
1524 }
1524
1525
1525 #changeset_compare_view_content {
1526 #changeset_compare_view_content {
1526 margin-bottom: @space;
1527 margin-bottom: @space;
1527 clear: both;
1528 clear: both;
1528 width: 100%;
1529 width: 100%;
1529 box-sizing: border-box;
1530 box-sizing: border-box;
1530 .border-radius(@border-radius);
1531 .border-radius(@border-radius);
1531
1532
1532 .help-block {
1533 .help-block {
1533 margin: @padding 0;
1534 margin: @padding 0;
1534 color: @text-color;
1535 color: @text-color;
1535 }
1536 }
1536
1537
1537 .empty_data {
1538 .empty_data {
1538 margin: @padding 0;
1539 margin: @padding 0;
1539 }
1540 }
1540
1541
1541 .alert {
1542 .alert {
1542 margin-bottom: @space;
1543 margin-bottom: @space;
1543 }
1544 }
1544 }
1545 }
1545
1546
1546 .table_disp {
1547 .table_disp {
1547 .status {
1548 .status {
1548 width: auto;
1549 width: auto;
1549
1550
1550 .flag_status {
1551 .flag_status {
1551 float: left;
1552 float: left;
1552 }
1553 }
1553 }
1554 }
1554 }
1555 }
1555
1556
1556 .status_box_menu {
1557 .status_box_menu {
1557 margin: 0;
1558 margin: 0;
1558 }
1559 }
1559
1560
1560 .notification-table{
1561 .notification-table{
1561 margin-bottom: @space;
1562 margin-bottom: @space;
1562 display: table;
1563 display: table;
1563 width: 100%;
1564 width: 100%;
1564
1565
1565 .container{
1566 .container{
1566 display: table-row;
1567 display: table-row;
1567
1568
1568 .notification-header{
1569 .notification-header{
1569 border-bottom: @border-thickness solid @border-default-color;
1570 border-bottom: @border-thickness solid @border-default-color;
1570 }
1571 }
1571
1572
1572 .notification-subject{
1573 .notification-subject{
1573 display: table-cell;
1574 display: table-cell;
1574 }
1575 }
1575 }
1576 }
1576 }
1577 }
1577
1578
1578 // Notifications
1579 // Notifications
1579 .notification-header{
1580 .notification-header{
1580 display: table;
1581 display: table;
1581 width: 100%;
1582 width: 100%;
1582 padding: floor(@basefontsize/2) 0;
1583 padding: floor(@basefontsize/2) 0;
1583 line-height: 1em;
1584 line-height: 1em;
1584
1585
1585 .desc, .delete-notifications, .read-notifications{
1586 .desc, .delete-notifications, .read-notifications{
1586 display: table-cell;
1587 display: table-cell;
1587 text-align: left;
1588 text-align: left;
1588 }
1589 }
1589
1590
1590 .desc{
1591 .desc{
1591 width: 1163px;
1592 width: 1163px;
1592 }
1593 }
1593
1594
1594 .delete-notifications, .read-notifications{
1595 .delete-notifications, .read-notifications{
1595 width: 35px;
1596 width: 35px;
1596 min-width: 35px; //fixes when only one button is displayed
1597 min-width: 35px; //fixes when only one button is displayed
1597 }
1598 }
1598 }
1599 }
1599
1600
1600 .notification-body {
1601 .notification-body {
1601 .markdown-block,
1602 .markdown-block,
1602 .rst-block {
1603 .rst-block {
1603 padding: @padding 0;
1604 padding: @padding 0;
1604 }
1605 }
1605
1606
1606 .notification-subject {
1607 .notification-subject {
1607 padding: @textmargin 0;
1608 padding: @textmargin 0;
1608 border-bottom: @border-thickness solid @border-default-color;
1609 border-bottom: @border-thickness solid @border-default-color;
1609 }
1610 }
1610 }
1611 }
1611
1612
1612
1613
1613 .notifications_buttons{
1614 .notifications_buttons{
1614 float: right;
1615 float: right;
1615 }
1616 }
1616
1617
1617 // Repositories
1618 // Repositories
1618
1619
1619 #summary.fields{
1620 #summary.fields{
1620 display: table;
1621 display: table;
1621
1622
1622 .field{
1623 .field{
1623 display: table-row;
1624 display: table-row;
1624
1625
1625 .label-summary{
1626 .label-summary{
1626 display: table-cell;
1627 display: table-cell;
1627 min-width: @label-summary-minwidth;
1628 min-width: @label-summary-minwidth;
1628 padding-top: @padding/2;
1629 padding-top: @padding/2;
1629 padding-bottom: @padding/2;
1630 padding-bottom: @padding/2;
1630 padding-right: @padding/2;
1631 padding-right: @padding/2;
1631 }
1632 }
1632
1633
1633 .input{
1634 .input{
1634 display: table-cell;
1635 display: table-cell;
1635 padding: @padding/2;
1636 padding: @padding/2;
1636
1637
1637 input{
1638 input{
1638 min-width: 29em;
1639 min-width: 29em;
1639 padding: @padding/4;
1640 padding: @padding/4;
1640 }
1641 }
1641 }
1642 }
1642 .statistics, .downloads{
1643 .statistics, .downloads{
1643 .disabled{
1644 .disabled{
1644 color: @grey4;
1645 color: @grey4;
1645 }
1646 }
1646 }
1647 }
1647 }
1648 }
1648 }
1649 }
1649
1650
1650 #summary{
1651 #summary{
1651 width: 70%;
1652 width: 70%;
1652 }
1653 }
1653
1654
1654
1655
1655 // Journal
1656 // Journal
1656 .journal.title {
1657 .journal.title {
1657 h5 {
1658 h5 {
1658 float: left;
1659 float: left;
1659 margin: 0;
1660 margin: 0;
1660 width: 70%;
1661 width: 70%;
1661 }
1662 }
1662
1663
1663 ul {
1664 ul {
1664 float: right;
1665 float: right;
1665 display: inline-block;
1666 display: inline-block;
1666 margin: 0;
1667 margin: 0;
1667 width: 30%;
1668 width: 30%;
1668 text-align: right;
1669 text-align: right;
1669
1670
1670 li {
1671 li {
1671 display: inline;
1672 display: inline;
1672 font-size: @journal-fontsize;
1673 font-size: @journal-fontsize;
1673 line-height: 1em;
1674 line-height: 1em;
1674
1675
1675 &:before { content: none; }
1676 &:before { content: none; }
1676 }
1677 }
1677 }
1678 }
1678 }
1679 }
1679
1680
1680 .filterexample {
1681 .filterexample {
1681 position: absolute;
1682 position: absolute;
1682 top: 95px;
1683 top: 95px;
1683 left: @contentpadding;
1684 left: @contentpadding;
1684 color: @rcblue;
1685 color: @rcblue;
1685 font-size: 11px;
1686 font-size: 11px;
1686 font-family: @text-regular;
1687 font-family: @text-regular;
1687 cursor: help;
1688 cursor: help;
1688
1689
1689 &:hover {
1690 &:hover {
1690 color: @rcdarkblue;
1691 color: @rcdarkblue;
1691 }
1692 }
1692
1693
1693 @media (max-width:768px) {
1694 @media (max-width:768px) {
1694 position: relative;
1695 position: relative;
1695 top: auto;
1696 top: auto;
1696 left: auto;
1697 left: auto;
1697 display: block;
1698 display: block;
1698 }
1699 }
1699 }
1700 }
1700
1701
1701
1702
1702 #journal{
1703 #journal{
1703 margin-bottom: @space;
1704 margin-bottom: @space;
1704
1705
1705 .journal_day{
1706 .journal_day{
1706 margin-bottom: @textmargin/2;
1707 margin-bottom: @textmargin/2;
1707 padding-bottom: @textmargin/2;
1708 padding-bottom: @textmargin/2;
1708 font-size: @journal-fontsize;
1709 font-size: @journal-fontsize;
1709 border-bottom: @border-thickness solid @border-default-color;
1710 border-bottom: @border-thickness solid @border-default-color;
1710 }
1711 }
1711
1712
1712 .journal_container{
1713 .journal_container{
1713 margin-bottom: @space;
1714 margin-bottom: @space;
1714
1715
1715 .journal_user{
1716 .journal_user{
1716 display: inline-block;
1717 display: inline-block;
1717 }
1718 }
1718 .journal_action_container{
1719 .journal_action_container{
1719 display: block;
1720 display: block;
1720 margin-top: @textmargin;
1721 margin-top: @textmargin;
1721
1722
1722 div{
1723 div{
1723 display: inline;
1724 display: inline;
1724 }
1725 }
1725
1726
1726 div.journal_action_params{
1727 div.journal_action_params{
1727 display: block;
1728 display: block;
1728 }
1729 }
1729
1730
1730 div.journal_repo:after{
1731 div.journal_repo:after{
1731 content: "\A";
1732 content: "\A";
1732 white-space: pre;
1733 white-space: pre;
1733 }
1734 }
1734
1735
1735 div.date{
1736 div.date{
1736 display: block;
1737 display: block;
1737 margin-bottom: @textmargin;
1738 margin-bottom: @textmargin;
1738 }
1739 }
1739 }
1740 }
1740 }
1741 }
1741 }
1742 }
1742
1743
1743 // Files
1744 // Files
1744 .edit-file-title {
1745 .edit-file-title {
1745 border-bottom: @border-thickness solid @border-default-color;
1746 border-bottom: @border-thickness solid @border-default-color;
1746
1747
1747 .breadcrumbs {
1748 .breadcrumbs {
1748 margin-bottom: 0;
1749 margin-bottom: 0;
1749 }
1750 }
1750 }
1751 }
1751
1752
1752 .edit-file-fieldset {
1753 .edit-file-fieldset {
1753 margin-top: @sidebarpadding;
1754 margin-top: @sidebarpadding;
1754
1755
1755 .fieldset {
1756 .fieldset {
1756 .left-label {
1757 .left-label {
1757 width: 13%;
1758 width: 13%;
1758 }
1759 }
1759 .right-content {
1760 .right-content {
1760 width: 87%;
1761 width: 87%;
1761 max-width: 100%;
1762 max-width: 100%;
1762 }
1763 }
1763 .filename-label {
1764 .filename-label {
1764 margin-top: 13px;
1765 margin-top: 13px;
1765 }
1766 }
1766 .commit-message-label {
1767 .commit-message-label {
1767 margin-top: 4px;
1768 margin-top: 4px;
1768 }
1769 }
1769 .file-upload-input {
1770 .file-upload-input {
1770 input {
1771 input {
1771 display: none;
1772 display: none;
1772 }
1773 }
1773 }
1774 }
1774 p {
1775 p {
1775 margin-top: 5px;
1776 margin-top: 5px;
1776 }
1777 }
1777
1778
1778 }
1779 }
1779 .custom-path-link {
1780 .custom-path-link {
1780 margin-left: 5px;
1781 margin-left: 5px;
1781 }
1782 }
1782 #commit {
1783 #commit {
1783 resize: vertical;
1784 resize: vertical;
1784 }
1785 }
1785 }
1786 }
1786
1787
1787 .delete-file-preview {
1788 .delete-file-preview {
1788 max-height: 250px;
1789 max-height: 250px;
1789 }
1790 }
1790
1791
1791 .new-file,
1792 .new-file,
1792 #filter_activate,
1793 #filter_activate,
1793 #filter_deactivate {
1794 #filter_deactivate {
1794 float: left;
1795 float: left;
1795 margin: 0 0 0 15px;
1796 margin: 0 0 0 15px;
1796 }
1797 }
1797
1798
1798 h3.files_location{
1799 h3.files_location{
1799 line-height: 2.4em;
1800 line-height: 2.4em;
1800 }
1801 }
1801
1802
1802 .browser-nav {
1803 .browser-nav {
1803 display: table;
1804 display: table;
1804 margin-bottom: @space;
1805 margin-bottom: @space;
1805
1806
1806
1807
1807 .info_box {
1808 .info_box {
1808 display: inline-table;
1809 display: inline-table;
1809 height: 2.5em;
1810 height: 2.5em;
1810
1811
1811 .browser-cur-rev, .info_box_elem {
1812 .browser-cur-rev, .info_box_elem {
1812 display: table-cell;
1813 display: table-cell;
1813 vertical-align: middle;
1814 vertical-align: middle;
1814 }
1815 }
1815
1816
1816 .info_box_elem {
1817 .info_box_elem {
1817 border-top: @border-thickness solid @rcblue;
1818 border-top: @border-thickness solid @rcblue;
1818 border-bottom: @border-thickness solid @rcblue;
1819 border-bottom: @border-thickness solid @rcblue;
1819
1820
1820 #at_rev, a {
1821 #at_rev, a {
1821 padding: 0.6em 0.9em;
1822 padding: 0.6em 0.9em;
1822 margin: 0;
1823 margin: 0;
1823 .box-shadow(none);
1824 .box-shadow(none);
1824 border: 0;
1825 border: 0;
1825 height: 12px;
1826 height: 12px;
1826 }
1827 }
1827
1828
1828 input#at_rev {
1829 input#at_rev {
1829 max-width: 50px;
1830 max-width: 50px;
1830 text-align: right;
1831 text-align: right;
1831 }
1832 }
1832
1833
1833 &.previous {
1834 &.previous {
1834 border: @border-thickness solid @rcblue;
1835 border: @border-thickness solid @rcblue;
1835 .disabled {
1836 .disabled {
1836 color: @grey4;
1837 color: @grey4;
1837 cursor: not-allowed;
1838 cursor: not-allowed;
1838 }
1839 }
1839 }
1840 }
1840
1841
1841 &.next {
1842 &.next {
1842 border: @border-thickness solid @rcblue;
1843 border: @border-thickness solid @rcblue;
1843 .disabled {
1844 .disabled {
1844 color: @grey4;
1845 color: @grey4;
1845 cursor: not-allowed;
1846 cursor: not-allowed;
1846 }
1847 }
1847 }
1848 }
1848 }
1849 }
1849
1850
1850 .browser-cur-rev {
1851 .browser-cur-rev {
1851
1852
1852 span{
1853 span{
1853 margin: 0;
1854 margin: 0;
1854 color: @rcblue;
1855 color: @rcblue;
1855 height: 12px;
1856 height: 12px;
1856 display: inline-block;
1857 display: inline-block;
1857 padding: 0.7em 1em ;
1858 padding: 0.7em 1em ;
1858 border: @border-thickness solid @rcblue;
1859 border: @border-thickness solid @rcblue;
1859 margin-right: @padding;
1860 margin-right: @padding;
1860 }
1861 }
1861 }
1862 }
1862 }
1863 }
1863
1864
1864 .search_activate {
1865 .search_activate {
1865 display: table-cell;
1866 display: table-cell;
1866 vertical-align: middle;
1867 vertical-align: middle;
1867
1868
1868 input, label{
1869 input, label{
1869 margin: 0;
1870 margin: 0;
1870 padding: 0;
1871 padding: 0;
1871 }
1872 }
1872
1873
1873 input{
1874 input{
1874 margin-left: @textmargin;
1875 margin-left: @textmargin;
1875 }
1876 }
1876
1877
1877 }
1878 }
1878 }
1879 }
1879
1880
1880 .browser-cur-rev{
1881 .browser-cur-rev{
1881 margin-bottom: @textmargin;
1882 margin-bottom: @textmargin;
1882 }
1883 }
1883
1884
1884 #node_filter_box_loading{
1885 #node_filter_box_loading{
1885 .info_text;
1886 .info_text;
1886 }
1887 }
1887
1888
1888 .browser-search {
1889 .browser-search {
1889 margin: -25px 0px 5px 0px;
1890 margin: -25px 0px 5px 0px;
1890 }
1891 }
1891
1892
1892 .node-filter {
1893 .node-filter {
1893 font-size: @repo-title-fontsize;
1894 font-size: @repo-title-fontsize;
1894 padding: 4px 0px 0px 0px;
1895 padding: 4px 0px 0px 0px;
1895
1896
1896 .node-filter-path {
1897 .node-filter-path {
1897 float: left;
1898 float: left;
1898 color: @grey4;
1899 color: @grey4;
1899 }
1900 }
1900 .node-filter-input {
1901 .node-filter-input {
1901 float: left;
1902 float: left;
1902 margin: -2px 0px 0px 2px;
1903 margin: -2px 0px 0px 2px;
1903 input {
1904 input {
1904 padding: 2px;
1905 padding: 2px;
1905 border: none;
1906 border: none;
1906 font-size: @repo-title-fontsize;
1907 font-size: @repo-title-fontsize;
1907 }
1908 }
1908 }
1909 }
1909 }
1910 }
1910
1911
1911
1912
1912 .browser-result{
1913 .browser-result{
1913 td a{
1914 td a{
1914 margin-left: 0.5em;
1915 margin-left: 0.5em;
1915 display: inline-block;
1916 display: inline-block;
1916
1917
1917 em{
1918 em{
1918 font-family: @text-bold;
1919 font-family: @text-bold;
1919 }
1920 }
1920 }
1921 }
1921 }
1922 }
1922
1923
1923 .browser-highlight{
1924 .browser-highlight{
1924 background-color: @grey5-alpha;
1925 background-color: @grey5-alpha;
1925 }
1926 }
1926
1927
1927
1928
1928 // Search
1929 // Search
1929
1930
1930 .search-form{
1931 .search-form{
1931 #q {
1932 #q {
1932 width: @search-form-width;
1933 width: @search-form-width;
1933 }
1934 }
1934 .fields{
1935 .fields{
1935 margin: 0 0 @space;
1936 margin: 0 0 @space;
1936 }
1937 }
1937
1938
1938 label{
1939 label{
1939 display: inline-block;
1940 display: inline-block;
1940 margin-right: @textmargin;
1941 margin-right: @textmargin;
1941 padding-top: 0.25em;
1942 padding-top: 0.25em;
1942 }
1943 }
1943
1944
1944
1945
1945 .results{
1946 .results{
1946 clear: both;
1947 clear: both;
1947 margin: 0 0 @padding;
1948 margin: 0 0 @padding;
1948 }
1949 }
1949 }
1950 }
1950
1951
1951 div.search-feedback-items {
1952 div.search-feedback-items {
1952 display: inline-block;
1953 display: inline-block;
1953 padding:0px 0px 0px 96px;
1954 padding:0px 0px 0px 96px;
1954 }
1955 }
1955
1956
1956 div.search-code-body {
1957 div.search-code-body {
1957 background-color: #ffffff; padding: 5px 0 5px 10px;
1958 background-color: #ffffff; padding: 5px 0 5px 10px;
1958 pre {
1959 pre {
1959 .match { background-color: #faffa6;}
1960 .match { background-color: #faffa6;}
1960 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
1961 .break { display: block; width: 100%; background-color: #DDE7EF; color: #747474; }
1961 }
1962 }
1962 }
1963 }
1963
1964
1964 .expand_commit.search {
1965 .expand_commit.search {
1965 .show_more.open {
1966 .show_more.open {
1966 height: auto;
1967 height: auto;
1967 max-height: none;
1968 max-height: none;
1968 }
1969 }
1969 }
1970 }
1970
1971
1971 .search-results {
1972 .search-results {
1972
1973
1973 h2 {
1974 h2 {
1974 margin-bottom: 0;
1975 margin-bottom: 0;
1975 }
1976 }
1976 .codeblock {
1977 .codeblock {
1977 border: none;
1978 border: none;
1978 background: transparent;
1979 background: transparent;
1979 }
1980 }
1980
1981
1981 .codeblock-header {
1982 .codeblock-header {
1982 border: none;
1983 border: none;
1983 background: transparent;
1984 background: transparent;
1984 }
1985 }
1985
1986
1986 .code-body {
1987 .code-body {
1987 border: @border-thickness solid @border-default-color;
1988 border: @border-thickness solid @border-default-color;
1988 .border-radius(@border-radius);
1989 .border-radius(@border-radius);
1989 }
1990 }
1990
1991
1991 .td-commit {
1992 .td-commit {
1992 &:extend(pre);
1993 &:extend(pre);
1993 border-bottom: @border-thickness solid @border-default-color;
1994 border-bottom: @border-thickness solid @border-default-color;
1994 }
1995 }
1995
1996
1996 .message {
1997 .message {
1997 height: auto;
1998 height: auto;
1998 max-width: 350px;
1999 max-width: 350px;
1999 white-space: normal;
2000 white-space: normal;
2000 text-overflow: initial;
2001 text-overflow: initial;
2001 overflow: visible;
2002 overflow: visible;
2002
2003
2003 .match { background-color: #faffa6;}
2004 .match { background-color: #faffa6;}
2004 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2005 .break { background-color: #DDE7EF; width: 100%; color: #747474; display: block; }
2005 }
2006 }
2006
2007
2007 }
2008 }
2008
2009
2009 table.rctable td.td-search-results div {
2010 table.rctable td.td-search-results div {
2010 max-width: 100%;
2011 max-width: 100%;
2011 }
2012 }
2012
2013
2013 #tip-box, .tip-box{
2014 #tip-box, .tip-box{
2014 padding: @menupadding/2;
2015 padding: @menupadding/2;
2015 display: block;
2016 display: block;
2016 border: @border-thickness solid @border-highlight-color;
2017 border: @border-thickness solid @border-highlight-color;
2017 .border-radius(@border-radius);
2018 .border-radius(@border-radius);
2018 background-color: white;
2019 background-color: white;
2019 z-index: 99;
2020 z-index: 99;
2020 white-space: pre-wrap;
2021 white-space: pre-wrap;
2021 }
2022 }
2022
2023
2023 #linktt {
2024 #linktt {
2024 width: 79px;
2025 width: 79px;
2025 }
2026 }
2026
2027
2027 #help_kb .modal-content{
2028 #help_kb .modal-content{
2028 max-width: 750px;
2029 max-width: 750px;
2029 margin: 10% auto;
2030 margin: 10% auto;
2030
2031
2031 table{
2032 table{
2032 td,th{
2033 td,th{
2033 border-bottom: none;
2034 border-bottom: none;
2034 line-height: 2.5em;
2035 line-height: 2.5em;
2035 }
2036 }
2036 th{
2037 th{
2037 padding-bottom: @textmargin/2;
2038 padding-bottom: @textmargin/2;
2038 }
2039 }
2039 td.keys{
2040 td.keys{
2040 text-align: center;
2041 text-align: center;
2041 }
2042 }
2042 }
2043 }
2043
2044
2044 .block-left{
2045 .block-left{
2045 width: 45%;
2046 width: 45%;
2046 margin-right: 5%;
2047 margin-right: 5%;
2047 }
2048 }
2048 .modal-footer{
2049 .modal-footer{
2049 clear: both;
2050 clear: both;
2050 }
2051 }
2051 .key.tag{
2052 .key.tag{
2052 padding: 0.5em;
2053 padding: 0.5em;
2053 background-color: @rcblue;
2054 background-color: @rcblue;
2054 color: white;
2055 color: white;
2055 border-color: @rcblue;
2056 border-color: @rcblue;
2056 .box-shadow(none);
2057 .box-shadow(none);
2057 }
2058 }
2058 }
2059 }
2059
2060
2060
2061
2061
2062
2062 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2063 //--- IMPORTS FOR REFACTORED STYLES ------------------//
2063
2064
2064 @import 'statistics-graph';
2065 @import 'statistics-graph';
2065 @import 'tables';
2066 @import 'tables';
2066 @import 'forms';
2067 @import 'forms';
2067 @import 'diff';
2068 @import 'diff';
2068 @import 'summary';
2069 @import 'summary';
2069 @import 'navigation';
2070 @import 'navigation';
2070
2071
2071 //--- SHOW/HIDE SECTIONS --//
2072 //--- SHOW/HIDE SECTIONS --//
2072
2073
2073 .btn-collapse {
2074 .btn-collapse {
2074 float: right;
2075 float: right;
2075 text-align: right;
2076 text-align: right;
2076 font-family: @text-light;
2077 font-family: @text-light;
2077 font-size: @basefontsize;
2078 font-size: @basefontsize;
2078 cursor: pointer;
2079 cursor: pointer;
2079 border: none;
2080 border: none;
2080 color: @rcblue;
2081 color: @rcblue;
2081 }
2082 }
2082
2083
2083 table.rctable,
2084 table.rctable,
2084 table.dataTable {
2085 table.dataTable {
2085 .btn-collapse {
2086 .btn-collapse {
2086 float: right;
2087 float: right;
2087 text-align: right;
2088 text-align: right;
2088 }
2089 }
2089 }
2090 }
2090
2091
2091
2092
2092 // TODO: johbo: Fix for IE10, this avoids that we see a border
2093 // TODO: johbo: Fix for IE10, this avoids that we see a border
2093 // and padding around checkboxes and radio boxes. Move to the right place,
2094 // and padding around checkboxes and radio boxes. Move to the right place,
2094 // or better: Remove this once we did the form refactoring.
2095 // or better: Remove this once we did the form refactoring.
2095 input[type=checkbox],
2096 input[type=checkbox],
2096 input[type=radio] {
2097 input[type=radio] {
2097 padding: 0;
2098 padding: 0;
2098 border: none;
2099 border: none;
2099 }
2100 }
@@ -1,108 +1,44 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base.html"/>
2 <%inherit file="base.html"/>
3
3
4 <%def name="breadcrumbs_links()">
4 <%def name="breadcrumbs_links()">
5 %if c.repo:
5 %if c.repo:
6 ${h.link_to('Settings',h.url('edit_repo', repo_name=c.repo.repo_name))}
6 ${h.link_to('Settings',h.url('edit_repo', repo_name=c.repo.repo_name))}
7 &raquo;
7 &raquo;
8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
8 ${h.link_to(_('Integrations'),request.route_url(route_name='repo_integrations_home', repo_name=c.repo.repo_name))}
9 &raquo;
9 &raquo;
10 ${h.link_to(current_IntegrationType.display_name,
10 ${h.link_to(current_IntegrationType.display_name,
11 request.route_url(route_name='repo_integrations_list',
11 request.route_url(route_name='repo_integrations_list',
12 repo_name=c.repo.repo_name,
12 repo_name=c.repo.repo_name,
13 integration=current_IntegrationType.key))}
13 integration=current_IntegrationType.key))}
14 %else:
14 %else:
15 ${h.link_to(_('Admin'),h.url('admin_home'))}
15 ${h.link_to(_('Admin'),h.url('admin_home'))}
16 &raquo;
16 &raquo;
17 ${h.link_to(_('Settings'),h.url('admin_settings'))}
17 ${h.link_to(_('Settings'),h.url('admin_settings'))}
18 &raquo;
18 &raquo;
19 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
19 ${h.link_to(_('Integrations'),request.route_url(route_name='global_integrations_home'))}
20 &raquo;
20 &raquo;
21 ${h.link_to(current_IntegrationType.display_name,
21 ${h.link_to(current_IntegrationType.display_name,
22 request.route_url(route_name='global_integrations_list',
22 request.route_url(route_name='global_integrations_list',
23 integration=current_IntegrationType.key))}
23 integration=current_IntegrationType.key))}
24 %endif
24 %endif
25 %if integration:
25 %if integration:
26 &raquo;
26 &raquo;
27 ${integration.name}
27 ${integration.name}
28 %endif
28 %endif
29 </%def>
29 </%def>
30
31
32 <div class="panel panel-default">
30 <div class="panel panel-default">
33 <div class="panel-heading">
31 <div class="panel-heading">
34 <h2 class="panel-title">
32 <h2 class="panel-title">
35 %if integration:
33 %if integration:
36 ${current_IntegrationType.display_name} - ${integration.name}
34 ${current_IntegrationType.display_name} - ${integration.name}
37 %else:
35 %else:
38 ${_('Create new %(integration_type)s integration') % {'integration_type': current_IntegrationType.display_name}}
36 ${_('Create new %(integration_type)s integration') % {'integration_type': current_IntegrationType.display_name}}
39 %endif
37 %endif
40 </h2>
38 </h2>
41 </div>
39 </div>
42 <div class="fields panel-body">
40 <div class="panel-body">
43 ${h.secure_form(request.url)}
41 ## TODO: dan: find way to put h in the deform context properly
44 <div class="form">
42 ${form.render(h=h) | n}
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()}
107 </div>
43 </div>
108 </div> No newline at end of file
44 </div>
@@ -1,137 +1,139 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <!DOCTYPE html>
2 <!DOCTYPE html>
3
3
4 <%
4 <%
5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
5 c.template_context['repo_name'] = getattr(c, 'repo_name', '')
6
6
7 if hasattr(c, 'rhodecode_db_repo'):
7 if hasattr(c, 'rhodecode_db_repo'):
8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
8 c.template_context['repo_type'] = c.rhodecode_db_repo.repo_type
9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
9 c.template_context['repo_landing_commit'] = c.rhodecode_db_repo.landing_rev[1]
10
10
11 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
11 if getattr(c, 'rhodecode_user', None) and c.rhodecode_user.user_id:
12 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
12 c.template_context['rhodecode_user']['username'] = c.rhodecode_user.username
13 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
13 c.template_context['rhodecode_user']['email'] = c.rhodecode_user.email
14
14
15 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
15 c.template_context['visual']['default_renderer'] = h.get_visual_attr(c, 'default_renderer')
16 %>
16 %>
17
17
18 <html xmlns="http://www.w3.org/1999/xhtml">
18 <html xmlns="http://www.w3.org/1999/xhtml">
19 <head>
19 <head>
20 <title>${self.title()}</title>
20 <title>${self.title()}</title>
21 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
21 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
22 <%def name="robots()">
22 <%def name="robots()">
23 <meta name="robots" content="index, nofollow"/>
23 <meta name="robots" content="index, nofollow"/>
24 </%def>
24 </%def>
25 ${self.robots()}
25 ${self.robots()}
26 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
26 <link rel="icon" href="${h.asset('images/favicon.ico', ver=c.rhodecode_version_hash)}" sizes="16x16 32x32" type="image/png" />
27
27
28 ## CSS definitions
28 ## CSS definitions
29 <%def name="css()">
29 <%def name="css()">
30 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
30 <link rel="stylesheet" type="text/css" href="${h.asset('css/style.css', ver=c.rhodecode_version_hash)}" media="screen"/>
31 <!--[if lt IE 9]>
31 <!--[if lt IE 9]>
32 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css', ver=c.rhodecode_version_hash)}" media="screen"/>
32 <link rel="stylesheet" type="text/css" href="${h.asset('css/ie.css', ver=c.rhodecode_version_hash)}" media="screen"/>
33 <![endif]-->
33 <![endif]-->
34 ## EXTRA FOR CSS
34 ## EXTRA FOR CSS
35 ${self.css_extra()}
35 ${self.css_extra()}
36 </%def>
36 </%def>
37 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
37 ## CSS EXTRA - optionally inject some extra CSS stuff needed for specific websites
38 <%def name="css_extra()">
38 <%def name="css_extra()">
39 </%def>
39 </%def>
40
40
41 ${self.css()}
41 ${self.css()}
42
42
43 ## JAVASCRIPT
43 ## JAVASCRIPT
44 <%def name="js()">
44 <%def name="js()">
45 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
45 <script src="${h.asset('js/rhodecode/i18n/%s.js' % c.language, ver=c.rhodecode_version_hash)}"></script>
46 <script type="text/javascript">
46 <script type="text/javascript">
47 // register templateContext to pass template variables to JS
47 // register templateContext to pass template variables to JS
48 var templateContext = ${h.json.dumps(c.template_context)|n};
48 var templateContext = ${h.json.dumps(c.template_context)|n};
49
49
50 var REPO_NAME = "${getattr(c, 'repo_name', '')}";
50 var REPO_NAME = "${getattr(c, 'repo_name', '')}";
51 %if hasattr(c, 'rhodecode_db_repo'):
51 %if hasattr(c, 'rhodecode_db_repo'):
52 var REPO_LANDING_REV = '${c.rhodecode_db_repo.landing_rev[1]}';
52 var REPO_LANDING_REV = '${c.rhodecode_db_repo.landing_rev[1]}';
53 var REPO_TYPE = '${c.rhodecode_db_repo.repo_type}';
53 var REPO_TYPE = '${c.rhodecode_db_repo.repo_type}';
54 %else:
54 %else:
55 var REPO_LANDING_REV = '';
55 var REPO_LANDING_REV = '';
56 var REPO_TYPE = '';
56 var REPO_TYPE = '';
57 %endif
57 %endif
58 var APPLICATION_URL = "${h.url('home').rstrip('/')}";
58 var APPLICATION_URL = "${h.url('home').rstrip('/')}";
59 var ASSET_URL = "${h.asset('')}";
59 var ASSET_URL = "${h.asset('')}";
60 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
60 var DEFAULT_RENDERER = "${h.get_visual_attr(c, 'default_renderer')}";
61 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
61 var CSRF_TOKEN = "${getattr(c, 'csrf_token', '')}";
62 % if getattr(c, 'rhodecode_user', None):
62 % if getattr(c, 'rhodecode_user', None):
63 var USER = {name:'${c.rhodecode_user.username}'};
63 var USER = {name:'${c.rhodecode_user.username}'};
64 % else:
64 % else:
65 var USER = {name:null};
65 var USER = {name:null};
66 % endif
66 % endif
67
67
68 var APPENLIGHT = {
68 var APPENLIGHT = {
69 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
69 enabled: ${'true' if getattr(c, 'appenlight_enabled', False) else 'false'},
70 key: '${getattr(c, "appenlight_api_public_key", "")}',
70 key: '${getattr(c, "appenlight_api_public_key", "")}',
71 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
71 serverUrl: '${getattr(c, "appenlight_server_url", "")}',
72 requestInfo: {
72 requestInfo: {
73 % if getattr(c, 'rhodecode_user', None):
73 % if getattr(c, 'rhodecode_user', None):
74 ip: '${c.rhodecode_user.ip_addr}',
74 ip: '${c.rhodecode_user.ip_addr}',
75 username: '${c.rhodecode_user.username}'
75 username: '${c.rhodecode_user.username}'
76 % endif
76 % endif
77 }
77 }
78 };
78 };
79 </script>
79 </script>
80
80
81 <!--[if lt IE 9]>
81 <!--[if lt IE 9]>
82 <script language="javascript" type="text/javascript" src="${h.asset('js/excanvas.min.js')}"></script>
82 <script language="javascript" type="text/javascript" src="${h.asset('js/excanvas.min.js')}"></script>
83 <![endif]-->
83 <![endif]-->
84 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
84 <script language="javascript" type="text/javascript" src="${h.asset('js/rhodecode/routes.js', ver=c.rhodecode_version_hash)}"></script>
85 <script language="javascript" type="text/javascript" src="${h.asset('js/scripts.js', ver=c.rhodecode_version_hash)}"></script>
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 ## avoide esaping the %N
87 ## avoide esaping the %N
87 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js'}";</script>
88 <script>CodeMirror.modeURL = "${h.asset('') + 'js/mode/%N/%N.js'}";</script>
88
89
90
89 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
91 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
90 ${self.js_extra()}
92 ${self.js_extra()}
91
93
92 <script type="text/javascript">
94 <script type="text/javascript">
93 $(document).ready(function(){
95 $(document).ready(function(){
94 show_more_event();
96 show_more_event();
95 timeagoActivate();
97 timeagoActivate();
96 })
98 })
97 </script>
99 </script>
98
100
99 </%def>
101 </%def>
100
102
101 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
103 ## JAVASCRIPT EXTRA - optionally inject some extra JS for specificed templates
102 <%def name="js_extra()"></%def>
104 <%def name="js_extra()"></%def>
103 ${self.js()}
105 ${self.js()}
104
106
105 <%def name="head_extra()"></%def>
107 <%def name="head_extra()"></%def>
106 ${self.head_extra()}
108 ${self.head_extra()}
107
109
108 <%include file="/base/plugins_base.html"/>
110 <%include file="/base/plugins_base.html"/>
109
111
110 ## extra stuff
112 ## extra stuff
111 %if c.pre_code:
113 %if c.pre_code:
112 ${c.pre_code|n}
114 ${c.pre_code|n}
113 %endif
115 %endif
114 </head>
116 </head>
115 <body id="body">
117 <body id="body">
116 <noscript>
118 <noscript>
117 <div class="noscript-error">
119 <div class="noscript-error">
118 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
120 ${_('Please enable JavaScript to use RhodeCode Enterprise')}
119 </div>
121 </div>
120 </noscript>
122 </noscript>
121 ## IE hacks
123 ## IE hacks
122 <!--[if IE 7]>
124 <!--[if IE 7]>
123 <script>$(document.body).addClass('ie7')</script>
125 <script>$(document.body).addClass('ie7')</script>
124 <![endif]-->
126 <![endif]-->
125 <!--[if IE 8]>
127 <!--[if IE 8]>
126 <script>$(document.body).addClass('ie8')</script>
128 <script>$(document.body).addClass('ie8')</script>
127 <![endif]-->
129 <![endif]-->
128 <!--[if IE 9]>
130 <!--[if IE 9]>
129 <script>$(document.body).addClass('ie9')</script>
131 <script>$(document.body).addClass('ie9')</script>
130 <![endif]-->
132 <![endif]-->
131
133
132 ${next.body()}
134 ${next.body()}
133 %if c.post_code:
135 %if c.post_code:
134 ${c.post_code|n}
136 ${c.post_code|n}
135 %endif
137 %endif
136 </body>
138 </body>
137 </html>
139 </html>
@@ -1,249 +1,250 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Import early to make sure things are patched up properly
3 # Import early to make sure things are patched up properly
4 from setuptools import setup, find_packages
4 from setuptools import setup, find_packages
5
5
6 import os
6 import os
7 import sys
7 import sys
8 import platform
8 import platform
9
9
10 if sys.version_info < (2, 7):
10 if sys.version_info < (2, 7):
11 raise Exception('RhodeCode requires Python 2.7 or later')
11 raise Exception('RhodeCode requires Python 2.7 or later')
12
12
13
13
14 here = os.path.abspath(os.path.dirname(__file__))
14 here = os.path.abspath(os.path.dirname(__file__))
15
15
16
16
17 def _get_meta_var(name, data, callback_handler=None):
17 def _get_meta_var(name, data, callback_handler=None):
18 import re
18 import re
19 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
19 matches = re.compile(r'(?:%s)\s*=\s*(.*)' % name).search(data)
20 if matches:
20 if matches:
21 if not callable(callback_handler):
21 if not callable(callback_handler):
22 callback_handler = lambda v: v
22 callback_handler = lambda v: v
23
23
24 return callback_handler(eval(matches.groups()[0]))
24 return callback_handler(eval(matches.groups()[0]))
25
25
26 _meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb')
26 _meta = open(os.path.join(here, 'rhodecode', '__init__.py'), 'rb')
27 _metadata = _meta.read()
27 _metadata = _meta.read()
28 _meta.close()
28 _meta.close()
29
29
30 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
30 callback = lambda V: ('.'.join(map(str, V[:3])) + '.'.join(V[3:]))
31 __version__ = open(os.path.join('rhodecode', 'VERSION')).read().strip()
31 __version__ = open(os.path.join('rhodecode', 'VERSION')).read().strip()
32 __license__ = _get_meta_var('__license__', _metadata)
32 __license__ = _get_meta_var('__license__', _metadata)
33 __author__ = _get_meta_var('__author__', _metadata)
33 __author__ = _get_meta_var('__author__', _metadata)
34 __url__ = _get_meta_var('__url__', _metadata)
34 __url__ = _get_meta_var('__url__', _metadata)
35 # defines current platform
35 # defines current platform
36 __platform__ = platform.system()
36 __platform__ = platform.system()
37
37
38 # Cygwin has different platform identifiers, but they all contain the
38 # Cygwin has different platform identifiers, but they all contain the
39 # term "CYGWIN"
39 # term "CYGWIN"
40 is_windows = __platform__ == 'Windows' or 'CYGWIN' in __platform__
40 is_windows = __platform__ == 'Windows' or 'CYGWIN' in __platform__
41
41
42 requirements = [
42 requirements = [
43 'Babel',
43 'Babel',
44 'Beaker',
44 'Beaker',
45 'FormEncode',
45 'FormEncode',
46 'Mako',
46 'Mako',
47 'Markdown',
47 'Markdown',
48 'MarkupSafe',
48 'MarkupSafe',
49 'MySQL-python',
49 'MySQL-python',
50 'Paste',
50 'Paste',
51 'PasteDeploy',
51 'PasteDeploy',
52 'PasteScript',
52 'PasteScript',
53 'Pygments',
53 'Pygments',
54 'Pylons',
54 'Pylons',
55 'Pyro4',
55 'Pyro4',
56 'Routes',
56 'Routes',
57 'SQLAlchemy',
57 'SQLAlchemy',
58 'Tempita',
58 'Tempita',
59 'URLObject',
59 'URLObject',
60 'WebError',
60 'WebError',
61 'WebHelpers',
61 'WebHelpers',
62 'WebHelpers2',
62 'WebHelpers2',
63 'WebOb',
63 'WebOb',
64 'WebTest',
64 'WebTest',
65 'Whoosh',
65 'Whoosh',
66 'alembic',
66 'alembic',
67 'amqplib',
67 'amqplib',
68 'anyjson',
68 'anyjson',
69 'appenlight-client',
69 'appenlight-client',
70 'authomatic',
70 'authomatic',
71 'backport_ipaddress',
71 'backport_ipaddress',
72 'celery',
72 'celery',
73 'colander',
73 'colander',
74 'decorator',
74 'decorator',
75 'deform',
75 'docutils',
76 'docutils',
76 'gunicorn',
77 'gunicorn',
77 'infrae.cache',
78 'infrae.cache',
78 'ipython',
79 'ipython',
79 'iso8601',
80 'iso8601',
80 'kombu',
81 'kombu',
81 'msgpack-python',
82 'msgpack-python',
82 'packaging',
83 'packaging',
83 'psycopg2',
84 'psycopg2',
84 'py-gfm',
85 'py-gfm',
85 'pycrypto',
86 'pycrypto',
86 'pycurl',
87 'pycurl',
87 'pyparsing',
88 'pyparsing',
88 'pyramid',
89 'pyramid',
89 'pyramid-debugtoolbar',
90 'pyramid-debugtoolbar',
90 'pyramid-mako',
91 'pyramid-mako',
91 'pyramid-beaker',
92 'pyramid-beaker',
92 'pysqlite',
93 'pysqlite',
93 'python-dateutil',
94 'python-dateutil',
94 'python-ldap',
95 'python-ldap',
95 'python-memcached',
96 'python-memcached',
96 'python-pam',
97 'python-pam',
97 'recaptcha-client',
98 'recaptcha-client',
98 'repoze.lru',
99 'repoze.lru',
99 'requests',
100 'requests',
100 'simplejson',
101 'simplejson',
101 'waitress',
102 'waitress',
102 'zope.cachedescriptors',
103 'zope.cachedescriptors',
103 'dogpile.cache',
104 'dogpile.cache',
104 'dogpile.core'
105 'dogpile.core'
105 ]
106 ]
106
107
107 if is_windows:
108 if is_windows:
108 pass
109 pass
109 else:
110 else:
110 requirements.append('psutil')
111 requirements.append('psutil')
111 requirements.append('py-bcrypt')
112 requirements.append('py-bcrypt')
112
113
113 test_requirements = [
114 test_requirements = [
114 'WebTest',
115 'WebTest',
115 'configobj',
116 'configobj',
116 'cssselect',
117 'cssselect',
117 'flake8',
118 'flake8',
118 'lxml',
119 'lxml',
119 'mock',
120 'mock',
120 'pytest',
121 'pytest',
121 'pytest-cov',
122 'pytest-cov',
122 'pytest-runner',
123 'pytest-runner',
123 ]
124 ]
124
125
125 setup_requirements = [
126 setup_requirements = [
126 'PasteScript',
127 'PasteScript',
127 'pytest-runner',
128 'pytest-runner',
128 ]
129 ]
129
130
130 dependency_links = [
131 dependency_links = [
131 ]
132 ]
132
133
133 classifiers = [
134 classifiers = [
134 'Development Status :: 6 - Mature',
135 'Development Status :: 6 - Mature',
135 'Environment :: Web Environment',
136 'Environment :: Web Environment',
136 'Framework :: Pylons',
137 'Framework :: Pylons',
137 'Intended Audience :: Developers',
138 'Intended Audience :: Developers',
138 'Operating System :: OS Independent',
139 'Operating System :: OS Independent',
139 'Programming Language :: Python',
140 'Programming Language :: Python',
140 'Programming Language :: Python :: 2.7',
141 'Programming Language :: Python :: 2.7',
141 ]
142 ]
142
143
143
144
144 # additional files from project that goes somewhere in the filesystem
145 # additional files from project that goes somewhere in the filesystem
145 # relative to sys.prefix
146 # relative to sys.prefix
146 data_files = []
147 data_files = []
147
148
148 # additional files that goes into package itself
149 # additional files that goes into package itself
149 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
150 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
150
151
151 description = ('RhodeCode is a fast and powerful management tool '
152 description = ('RhodeCode is a fast and powerful management tool '
152 'for Mercurial and GIT with a built in push/pull server, '
153 'for Mercurial and GIT with a built in push/pull server, '
153 'full text search and code-review.')
154 'full text search and code-review.')
154
155
155 keywords = ' '.join([
156 keywords = ' '.join([
156 'rhodecode', 'rhodiumcode', 'mercurial', 'git', 'code review',
157 'rhodecode', 'rhodiumcode', 'mercurial', 'git', 'code review',
157 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
158 'repo groups', 'ldap', 'repository management', 'hgweb replacement',
158 'hgwebdir', 'gitweb replacement', 'serving hgweb',
159 'hgwebdir', 'gitweb replacement', 'serving hgweb',
159 ])
160 ])
160
161
161 # long description
162 # long description
162 README_FILE = 'README.rst'
163 README_FILE = 'README.rst'
163 CHANGELOG_FILE = 'CHANGES.rst'
164 CHANGELOG_FILE = 'CHANGES.rst'
164 try:
165 try:
165 long_description = open(README_FILE).read() + '\n\n' + \
166 long_description = open(README_FILE).read() + '\n\n' + \
166 open(CHANGELOG_FILE).read()
167 open(CHANGELOG_FILE).read()
167
168
168 except IOError, err:
169 except IOError, err:
169 sys.stderr.write(
170 sys.stderr.write(
170 '[WARNING] Cannot find file specified as long_description (%s)\n or '
171 '[WARNING] Cannot find file specified as long_description (%s)\n or '
171 'changelog (%s) skipping that file' % (README_FILE, CHANGELOG_FILE)
172 'changelog (%s) skipping that file' % (README_FILE, CHANGELOG_FILE)
172 )
173 )
173 long_description = description
174 long_description = description
174
175
175 # packages
176 # packages
176 packages = find_packages()
177 packages = find_packages()
177
178
178 paster_commands = [
179 paster_commands = [
179 'make-config=rhodecode.lib.paster_commands.make_config:Command',
180 'make-config=rhodecode.lib.paster_commands.make_config:Command',
180 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command',
181 'setup-rhodecode=rhodecode.lib.paster_commands.setup_rhodecode:Command',
181 'update-repoinfo=rhodecode.lib.paster_commands.update_repoinfo:Command',
182 'update-repoinfo=rhodecode.lib.paster_commands.update_repoinfo:Command',
182 'cache-keys=rhodecode.lib.paster_commands.cache_keys:Command',
183 'cache-keys=rhodecode.lib.paster_commands.cache_keys:Command',
183 'ishell=rhodecode.lib.paster_commands.ishell:Command',
184 'ishell=rhodecode.lib.paster_commands.ishell:Command',
184 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb',
185 'upgrade-db=rhodecode.lib.dbmigrate:UpgradeDb',
185 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand',
186 'celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand',
186 ]
187 ]
187
188
188 setup(
189 setup(
189 name='rhodecode-enterprise-ce',
190 name='rhodecode-enterprise-ce',
190 version=__version__,
191 version=__version__,
191 description=description,
192 description=description,
192 long_description=long_description,
193 long_description=long_description,
193 keywords=keywords,
194 keywords=keywords,
194 license=__license__,
195 license=__license__,
195 author=__author__,
196 author=__author__,
196 author_email='marcin@rhodecode.com',
197 author_email='marcin@rhodecode.com',
197 dependency_links=dependency_links,
198 dependency_links=dependency_links,
198 url=__url__,
199 url=__url__,
199 install_requires=requirements,
200 install_requires=requirements,
200 tests_require=test_requirements,
201 tests_require=test_requirements,
201 classifiers=classifiers,
202 classifiers=classifiers,
202 setup_requires=setup_requirements,
203 setup_requires=setup_requirements,
203 data_files=data_files,
204 data_files=data_files,
204 packages=packages,
205 packages=packages,
205 include_package_data=True,
206 include_package_data=True,
206 package_data=package_data,
207 package_data=package_data,
207 message_extractors={
208 message_extractors={
208 'rhodecode': [
209 'rhodecode': [
209 ('**.py', 'python', None),
210 ('**.py', 'python', None),
210 ('**.js', 'javascript', None),
211 ('**.js', 'javascript', None),
211 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
212 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
212 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
213 ('templates/**.html', 'mako', {'input_encoding': 'utf-8'}),
213 ('public/**', 'ignore', None),
214 ('public/**', 'ignore', None),
214 ]
215 ]
215 },
216 },
216 zip_safe=False,
217 zip_safe=False,
217 paster_plugins=['PasteScript', 'Pylons'],
218 paster_plugins=['PasteScript', 'Pylons'],
218 entry_points={
219 entry_points={
219 'enterprise.plugins1': [
220 'enterprise.plugins1': [
220 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory',
221 'crowd=rhodecode.authentication.plugins.auth_crowd:plugin_factory',
221 'headers=rhodecode.authentication.plugins.auth_headers:plugin_factory',
222 'headers=rhodecode.authentication.plugins.auth_headers:plugin_factory',
222 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory',
223 'jasig_cas=rhodecode.authentication.plugins.auth_jasig_cas:plugin_factory',
223 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory',
224 'ldap=rhodecode.authentication.plugins.auth_ldap:plugin_factory',
224 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory',
225 'pam=rhodecode.authentication.plugins.auth_pam:plugin_factory',
225 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory',
226 'rhodecode=rhodecode.authentication.plugins.auth_rhodecode:plugin_factory',
226 'token=rhodecode.authentication.plugins.auth_token:plugin_factory',
227 'token=rhodecode.authentication.plugins.auth_token:plugin_factory',
227 ],
228 ],
228 'paste.app_factory': [
229 'paste.app_factory': [
229 'main=rhodecode.config.middleware:make_pyramid_app',
230 'main=rhodecode.config.middleware:make_pyramid_app',
230 'pylons=rhodecode.config.middleware:make_app',
231 'pylons=rhodecode.config.middleware:make_app',
231 ],
232 ],
232 'paste.app_install': [
233 'paste.app_install': [
233 'main=pylons.util:PylonsInstaller',
234 'main=pylons.util:PylonsInstaller',
234 'pylons=pylons.util:PylonsInstaller',
235 'pylons=pylons.util:PylonsInstaller',
235 ],
236 ],
236 'paste.global_paster_command': paster_commands,
237 'paste.global_paster_command': paster_commands,
237 'pytest11': [
238 'pytest11': [
238 'pylons=rhodecode.tests.pylons_plugin',
239 'pylons=rhodecode.tests.pylons_plugin',
239 'enterprise=rhodecode.tests.plugin',
240 'enterprise=rhodecode.tests.plugin',
240 ],
241 ],
241 'console_scripts': [
242 'console_scripts': [
242 'rcserver=rhodecode.rcserver:main',
243 'rcserver=rhodecode.rcserver:main',
243 ],
244 ],
244 'beaker.backends': [
245 'beaker.backends': [
245 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase',
246 'memorylru_base=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerBase',
246 'memorylru_debug=rhodecode.lib.memory_lru_debug:MemoryLRUNamespaceManagerDebug'
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