##// END OF EJS Templates
release: merge back stable branch into default
marcink -
r2571:4154ff95 merge default
parent child Browse files
Show More
@@ -0,0 +1,40 b''
1 |RCE| 4.11.3 |RNS|
2 ------------------
3
4 Release Date
5 ^^^^^^^^^^^^
6
7 - 2018-02-14
8
9
10 New Features
11 ^^^^^^^^^^^^
12
13
14
15 General
16 ^^^^^^^
17
18
19
20 Security
21 ^^^^^^^^
22
23
24
25 Performance
26 ^^^^^^^^^^^
27
28
29
30 Fixes
31 ^^^^^
32
33 - Pull requests: fixed problems with opening pull requests when default branches
34 in repository didn't exist or were closed.
35
36
37 Upgrade notes
38 ^^^^^^^^^^^^^
39
40 - Unscheduled bugfix release fixing pull request opening issues reported.
@@ -1,33 +1,34 b''
1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
1 1bd3e92b7e2e2d2024152b34bb88dff1db544a71 v4.0.0
2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
2 170c5398320ea6cddd50955e88d408794c21d43a v4.0.1
3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
3 c3fe200198f5aa34cf2e4066df2881a9cefe3704 v4.1.0
4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
4 7fd5c850745e2ea821fb4406af5f4bff9b0a7526 v4.1.1
5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
5 41c87da28a179953df86061d817bc35533c66dd2 v4.1.2
6 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0
6 baaf9f5bcea3bae0ef12ae20c8b270482e62abb6 v4.2.0
7 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1
7 32a70c7e56844a825f61df496ee5eaf8c3c4e189 v4.2.1
8 fa695cdb411d294679ac081d595ac654e5613b03 v4.3.0
8 fa695cdb411d294679ac081d595ac654e5613b03 v4.3.0
9 0e4dc11b58cad833c513fe17bac39e6850edf959 v4.3.1
9 0e4dc11b58cad833c513fe17bac39e6850edf959 v4.3.1
10 8a876f48f5cb1d018b837db28ff928500cb32cfb v4.4.0
10 8a876f48f5cb1d018b837db28ff928500cb32cfb v4.4.0
11 8dd86b410b1aac086ffdfc524ef300f896af5047 v4.4.1
11 8dd86b410b1aac086ffdfc524ef300f896af5047 v4.4.1
12 d2514226abc8d3b4f6fb57765f47d1b6fb360a05 v4.4.2
12 d2514226abc8d3b4f6fb57765f47d1b6fb360a05 v4.4.2
13 27d783325930af6dad2741476c0d0b1b7c8415c2 v4.5.0
13 27d783325930af6dad2741476c0d0b1b7c8415c2 v4.5.0
14 7f2016f352abcbdba4a19d4039c386e9629449da v4.5.1
14 7f2016f352abcbdba4a19d4039c386e9629449da v4.5.1
15 416fec799314c70a5c780fb28b3357b08869333a v4.5.2
15 416fec799314c70a5c780fb28b3357b08869333a v4.5.2
16 27c3b85fafc83143e6678fbc3da69e1615bcac55 v4.6.0
16 27c3b85fafc83143e6678fbc3da69e1615bcac55 v4.6.0
17 5ad13deb9118c2a5243d4032d4d9cc174e5872db v4.6.1
17 5ad13deb9118c2a5243d4032d4d9cc174e5872db v4.6.1
18 2be921e01fa24bb102696ada596f87464c3666f6 v4.7.0
18 2be921e01fa24bb102696ada596f87464c3666f6 v4.7.0
19 7198bdec29c2872c974431d55200d0398354cdb1 v4.7.1
19 7198bdec29c2872c974431d55200d0398354cdb1 v4.7.1
20 bd1c8d230fe741c2dfd7100a0ef39fd0774fd581 v4.7.2
20 bd1c8d230fe741c2dfd7100a0ef39fd0774fd581 v4.7.2
21 9731914f89765d9628dc4dddc84bc9402aa124c8 v4.8.0
21 9731914f89765d9628dc4dddc84bc9402aa124c8 v4.8.0
22 c5a2b7d0e4bbdebc4a62d7b624befe375207b659 v4.9.0
22 c5a2b7d0e4bbdebc4a62d7b624befe375207b659 v4.9.0
23 d9aa3b27ac9f7e78359775c75fedf7bfece232f1 v4.9.1
23 d9aa3b27ac9f7e78359775c75fedf7bfece232f1 v4.9.1
24 4ba4d74981cec5d6b28b158f875a2540952c2f74 v4.10.0
24 4ba4d74981cec5d6b28b158f875a2540952c2f74 v4.10.0
25 0a6821cbd6b0b3c21503002f88800679fa35ab63 v4.10.1
25 0a6821cbd6b0b3c21503002f88800679fa35ab63 v4.10.1
26 434ad90ec8d621f4416074b84f6e9ce03964defb v4.10.2
26 434ad90ec8d621f4416074b84f6e9ce03964defb v4.10.2
27 68baee10e698da2724c6e0f698c03a6abb993bf2 v4.10.3
27 68baee10e698da2724c6e0f698c03a6abb993bf2 v4.10.3
28 00821d3afd1dce3f4767cc353f84a17f7d5218a1 v4.10.4
28 00821d3afd1dce3f4767cc353f84a17f7d5218a1 v4.10.4
29 22f6744ad8cc274311825f63f953e4dee2ea5cb9 v4.10.5
29 22f6744ad8cc274311825f63f953e4dee2ea5cb9 v4.10.5
30 96eb24bea2f5f9258775245e3f09f6fa0a4dda01 v4.10.6
30 96eb24bea2f5f9258775245e3f09f6fa0a4dda01 v4.10.6
31 3121217a812c956d7dd5a5875821bd73e8002a32 v4.11.0
31 3121217a812c956d7dd5a5875821bd73e8002a32 v4.11.0
32 fa98b454715ac5b912f39e84af54345909a2a805 v4.11.1
32 fa98b454715ac5b912f39e84af54345909a2a805 v4.11.1
33 3982abcfdcc229a723cebe52d3a9bcff10bba08e v4.11.2
33 3982abcfdcc229a723cebe52d3a9bcff10bba08e v4.11.2
34 33195f145db9172f0a8f1487e09207178a6ab065 v4.11.3
@@ -1,110 +1,111 b''
1 .. _rhodecode-release-notes-ref:
1 .. _rhodecode-release-notes-ref:
2
2
3 Release Notes
3 Release Notes
4 =============
4 =============
5
5
6 |RCE| 4.x Versions
6 |RCE| 4.x Versions
7 ------------------
7 ------------------
8
8
9 .. toctree::
9 .. toctree::
10 :maxdepth: 1
10 :maxdepth: 1
11
11
12 release-notes-4.11.3.rst
12 release-notes-4.11.2.rst
13 release-notes-4.11.2.rst
13 release-notes-4.11.1.rst
14 release-notes-4.11.1.rst
14 release-notes-4.11.0.rst
15 release-notes-4.11.0.rst
15 release-notes-4.10.6.rst
16 release-notes-4.10.6.rst
16 release-notes-4.10.5.rst
17 release-notes-4.10.5.rst
17 release-notes-4.10.4.rst
18 release-notes-4.10.4.rst
18 release-notes-4.10.3.rst
19 release-notes-4.10.3.rst
19 release-notes-4.10.2.rst
20 release-notes-4.10.2.rst
20 release-notes-4.10.1.rst
21 release-notes-4.10.1.rst
21 release-notes-4.10.0.rst
22 release-notes-4.10.0.rst
22 release-notes-4.9.1.rst
23 release-notes-4.9.1.rst
23 release-notes-4.9.0.rst
24 release-notes-4.9.0.rst
24 release-notes-4.8.0.rst
25 release-notes-4.8.0.rst
25 release-notes-4.7.2.rst
26 release-notes-4.7.2.rst
26 release-notes-4.7.1.rst
27 release-notes-4.7.1.rst
27 release-notes-4.7.0.rst
28 release-notes-4.7.0.rst
28 release-notes-4.6.1.rst
29 release-notes-4.6.1.rst
29 release-notes-4.6.0.rst
30 release-notes-4.6.0.rst
30 release-notes-4.5.2.rst
31 release-notes-4.5.2.rst
31 release-notes-4.5.1.rst
32 release-notes-4.5.1.rst
32 release-notes-4.5.0.rst
33 release-notes-4.5.0.rst
33 release-notes-4.4.2.rst
34 release-notes-4.4.2.rst
34 release-notes-4.4.1.rst
35 release-notes-4.4.1.rst
35 release-notes-4.4.0.rst
36 release-notes-4.4.0.rst
36 release-notes-4.3.1.rst
37 release-notes-4.3.1.rst
37 release-notes-4.3.0.rst
38 release-notes-4.3.0.rst
38 release-notes-4.2.1.rst
39 release-notes-4.2.1.rst
39 release-notes-4.2.0.rst
40 release-notes-4.2.0.rst
40 release-notes-4.1.2.rst
41 release-notes-4.1.2.rst
41 release-notes-4.1.1.rst
42 release-notes-4.1.1.rst
42 release-notes-4.1.0.rst
43 release-notes-4.1.0.rst
43 release-notes-4.0.1.rst
44 release-notes-4.0.1.rst
44 release-notes-4.0.0.rst
45 release-notes-4.0.0.rst
45
46
46 |RCE| 3.x Versions
47 |RCE| 3.x Versions
47 ------------------
48 ------------------
48
49
49 .. toctree::
50 .. toctree::
50 :maxdepth: 1
51 :maxdepth: 1
51
52
52 release-notes-3.8.4.rst
53 release-notes-3.8.4.rst
53 release-notes-3.8.3.rst
54 release-notes-3.8.3.rst
54 release-notes-3.8.2.rst
55 release-notes-3.8.2.rst
55 release-notes-3.8.1.rst
56 release-notes-3.8.1.rst
56 release-notes-3.8.0.rst
57 release-notes-3.8.0.rst
57 release-notes-3.7.1.rst
58 release-notes-3.7.1.rst
58 release-notes-3.7.0.rst
59 release-notes-3.7.0.rst
59 release-notes-3.6.1.rst
60 release-notes-3.6.1.rst
60 release-notes-3.6.0.rst
61 release-notes-3.6.0.rst
61 release-notes-3.5.2.rst
62 release-notes-3.5.2.rst
62 release-notes-3.5.1.rst
63 release-notes-3.5.1.rst
63 release-notes-3.5.0.rst
64 release-notes-3.5.0.rst
64 release-notes-3.4.1.rst
65 release-notes-3.4.1.rst
65 release-notes-3.4.0.rst
66 release-notes-3.4.0.rst
66 release-notes-3.3.4.rst
67 release-notes-3.3.4.rst
67 release-notes-3.3.3.rst
68 release-notes-3.3.3.rst
68 release-notes-3.3.2.rst
69 release-notes-3.3.2.rst
69 release-notes-3.3.1.rst
70 release-notes-3.3.1.rst
70 release-notes-3.3.0.rst
71 release-notes-3.3.0.rst
71 release-notes-3.2.3.rst
72 release-notes-3.2.3.rst
72 release-notes-3.2.2.rst
73 release-notes-3.2.2.rst
73 release-notes-3.2.1.rst
74 release-notes-3.2.1.rst
74 release-notes-3.2.0.rst
75 release-notes-3.2.0.rst
75 release-notes-3.1.1.rst
76 release-notes-3.1.1.rst
76 release-notes-3.1.0.rst
77 release-notes-3.1.0.rst
77 release-notes-3.0.2.rst
78 release-notes-3.0.2.rst
78 release-notes-3.0.1.rst
79 release-notes-3.0.1.rst
79 release-notes-3.0.0.rst
80 release-notes-3.0.0.rst
80
81
81 |RCE| 2.x Versions
82 |RCE| 2.x Versions
82 ------------------
83 ------------------
83
84
84 .. toctree::
85 .. toctree::
85 :maxdepth: 1
86 :maxdepth: 1
86
87
87 release-notes-2.2.8.rst
88 release-notes-2.2.8.rst
88 release-notes-2.2.7.rst
89 release-notes-2.2.7.rst
89 release-notes-2.2.6.rst
90 release-notes-2.2.6.rst
90 release-notes-2.2.5.rst
91 release-notes-2.2.5.rst
91 release-notes-2.2.4.rst
92 release-notes-2.2.4.rst
92 release-notes-2.2.3.rst
93 release-notes-2.2.3.rst
93 release-notes-2.2.2.rst
94 release-notes-2.2.2.rst
94 release-notes-2.2.1.rst
95 release-notes-2.2.1.rst
95 release-notes-2.2.0.rst
96 release-notes-2.2.0.rst
96 release-notes-2.1.0.rst
97 release-notes-2.1.0.rst
97 release-notes-2.0.2.rst
98 release-notes-2.0.2.rst
98 release-notes-2.0.1.rst
99 release-notes-2.0.1.rst
99 release-notes-2.0.0.rst
100 release-notes-2.0.0.rst
100
101
101 |RCE| 1.x Versions
102 |RCE| 1.x Versions
102 ------------------
103 ------------------
103
104
104 .. toctree::
105 .. toctree::
105 :maxdepth: 1
106 :maxdepth: 1
106
107
107 release-notes-1.7.2.rst
108 release-notes-1.7.2.rst
108 release-notes-1.7.1.rst
109 release-notes-1.7.1.rst
109 release-notes-1.7.0.rst
110 release-notes-1.7.0.rst
110 release-notes-1.6.0.rst
111 release-notes-1.6.0.rst
@@ -1,587 +1,593 b''
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
1 // # Copyright (C) 2010-2018 RhodeCode GmbH
2 // #
2 // #
3 // # This program is free software: you can redistribute it and/or modify
3 // # This program is free software: you can redistribute it and/or modify
4 // # it under the terms of the GNU Affero General Public License, version 3
4 // # it under the terms of the GNU Affero General Public License, version 3
5 // # (only), as published by the Free Software Foundation.
5 // # (only), as published by the Free Software Foundation.
6 // #
6 // #
7 // # This program is distributed in the hope that it will be useful,
7 // # This program is distributed in the hope that it will be useful,
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 // # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 // # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 // # GNU General Public License for more details.
10 // # GNU General Public License for more details.
11 // #
11 // #
12 // # You should have received a copy of the GNU Affero General Public License
12 // # You should have received a copy of the GNU Affero General Public License
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 // # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 // #
14 // #
15 // # This program is dual-licensed. If you wish to learn more about the
15 // # This program is dual-licensed. If you wish to learn more about the
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
16 // # RhodeCode Enterprise Edition, including its added features, Support services,
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
17 // # and proprietary license terms, please see https://rhodecode.com/licenses/
18
18
19
19
20 var prButtonLockChecks = {
20 var prButtonLockChecks = {
21 'compare': false,
21 'compare': false,
22 'reviewers': false
22 'reviewers': false
23 };
23 };
24
24
25 /**
25 /**
26 * lock button until all checks and loads are made. E.g reviewer calculation
26 * lock button until all checks and loads are made. E.g reviewer calculation
27 * should prevent from submitting a PR
27 * should prevent from submitting a PR
28 * @param lockEnabled
28 * @param lockEnabled
29 * @param msg
29 * @param msg
30 * @param scope
30 * @param scope
31 */
31 */
32 var prButtonLock = function(lockEnabled, msg, scope) {
32 var prButtonLock = function(lockEnabled, msg, scope) {
33 scope = scope || 'all';
33 scope = scope || 'all';
34 if (scope == 'all'){
34 if (scope == 'all'){
35 prButtonLockChecks['compare'] = !lockEnabled;
35 prButtonLockChecks['compare'] = !lockEnabled;
36 prButtonLockChecks['reviewers'] = !lockEnabled;
36 prButtonLockChecks['reviewers'] = !lockEnabled;
37 } else if (scope == 'compare') {
37 } else if (scope == 'compare') {
38 prButtonLockChecks['compare'] = !lockEnabled;
38 prButtonLockChecks['compare'] = !lockEnabled;
39 } else if (scope == 'reviewers'){
39 } else if (scope == 'reviewers'){
40 prButtonLockChecks['reviewers'] = !lockEnabled;
40 prButtonLockChecks['reviewers'] = !lockEnabled;
41 }
41 }
42 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
42 var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers;
43 if (lockEnabled) {
43 if (lockEnabled) {
44 $('#save').attr('disabled', 'disabled');
44 $('#save').attr('disabled', 'disabled');
45 }
45 }
46 else if (checksMeet) {
46 else if (checksMeet) {
47 $('#save').removeAttr('disabled');
47 $('#save').removeAttr('disabled');
48 }
48 }
49
49
50 if (msg) {
50 if (msg) {
51 $('#pr_open_message').html(msg);
51 $('#pr_open_message').html(msg);
52 }
52 }
53 };
53 };
54
54
55
55
56 /**
56 /**
57 Generate Title and Description for a PullRequest.
57 Generate Title and Description for a PullRequest.
58 In case of 1 commits, the title and description is that one commit
58 In case of 1 commits, the title and description is that one commit
59 in case of multiple commits, we iterate on them with max N number of commits,
59 in case of multiple commits, we iterate on them with max N number of commits,
60 and build description in a form
60 and build description in a form
61 - commitN
61 - commitN
62 - commitN+1
62 - commitN+1
63 ...
63 ...
64
64
65 Title is then constructed from branch names, or other references,
65 Title is then constructed from branch names, or other references,
66 replacing '-' and '_' into spaces
66 replacing '-' and '_' into spaces
67
67
68 * @param sourceRef
68 * @param sourceRef
69 * @param elements
69 * @param elements
70 * @param limit
70 * @param limit
71 * @returns {*[]}
71 * @returns {*[]}
72 */
72 */
73 var getTitleAndDescription = function(sourceRef, elements, limit) {
73 var getTitleAndDescription = function(sourceRef, elements, limit) {
74 var title = '';
74 var title = '';
75 var desc = '';
75 var desc = '';
76
76
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
77 $.each($(elements).get().reverse().slice(0, limit), function(idx, value) {
78 var rawMessage = $(value).find('td.td-description .message').data('messageRaw');
78 var rawMessage = $(value).find('td.td-description .message').data('messageRaw');
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
79 desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n';
80 });
80 });
81 // only 1 commit, use commit message as title
81 // only 1 commit, use commit message as title
82 if (elements.length === 1) {
82 if (elements.length === 1) {
83 title = $(elements[0]).find('td.td-description .message').data('messageRaw').split('\n')[0];
83 title = $(elements[0]).find('td.td-description .message').data('messageRaw').split('\n')[0];
84 }
84 }
85 else {
85 else {
86 // use reference name
86 // use reference name
87 title = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter();
87 title = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter();
88 }
88 }
89
89
90 return [title, desc]
90 return [title, desc]
91 };
91 };
92
92
93
93
94
94
95 ReviewersController = function () {
95 ReviewersController = function () {
96 var self = this;
96 var self = this;
97 this.$reviewRulesContainer = $('#review_rules');
97 this.$reviewRulesContainer = $('#review_rules');
98 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
98 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
99 this.forbidReviewUsers = undefined;
99 this.forbidReviewUsers = undefined;
100 this.$reviewMembers = $('#review_members');
100 this.$reviewMembers = $('#review_members');
101 this.currentRequest = null;
101 this.currentRequest = null;
102
102
103 this.defaultForbidReviewUsers = function() {
103 this.defaultForbidReviewUsers = function() {
104 return [
104 return [
105 {'username': 'default',
105 {'username': 'default',
106 'user_id': templateContext.default_user.user_id}
106 'user_id': templateContext.default_user.user_id}
107 ];
107 ];
108 };
108 };
109
109
110 this.hideReviewRules = function() {
110 this.hideReviewRules = function() {
111 self.$reviewRulesContainer.hide();
111 self.$reviewRulesContainer.hide();
112 };
112 };
113
113
114 this.showReviewRules = function() {
114 this.showReviewRules = function() {
115 self.$reviewRulesContainer.show();
115 self.$reviewRulesContainer.show();
116 };
116 };
117
117
118 this.addRule = function(ruleText) {
118 this.addRule = function(ruleText) {
119 self.showReviewRules();
119 self.showReviewRules();
120 return '<div>- {0}</div>'.format(ruleText)
120 return '<div>- {0}</div>'.format(ruleText)
121 };
121 };
122
122
123 this.loadReviewRules = function(data) {
123 this.loadReviewRules = function(data) {
124 // reset forbidden Users
124 // reset forbidden Users
125 this.forbidReviewUsers = self.defaultForbidReviewUsers();
125 this.forbidReviewUsers = self.defaultForbidReviewUsers();
126
126
127 // reset state of review rules
127 // reset state of review rules
128 self.$rulesList.html('');
128 self.$rulesList.html('');
129
129
130 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
130 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
131 // default rule, case for older repo that don't have any rules stored
131 // default rule, case for older repo that don't have any rules stored
132 self.$rulesList.append(
132 self.$rulesList.append(
133 self.addRule(
133 self.addRule(
134 _gettext('All reviewers must vote.'))
134 _gettext('All reviewers must vote.'))
135 );
135 );
136 return self.forbidReviewUsers
136 return self.forbidReviewUsers
137 }
137 }
138
138
139 if (data.rules.voting !== undefined) {
139 if (data.rules.voting !== undefined) {
140 if (data.rules.voting < 0) {
140 if (data.rules.voting < 0) {
141 self.$rulesList.append(
141 self.$rulesList.append(
142 self.addRule(
142 self.addRule(
143 _gettext('All individual reviewers must vote.'))
143 _gettext('All individual reviewers must vote.'))
144 )
144 )
145 } else if (data.rules.voting === 1) {
145 } else if (data.rules.voting === 1) {
146 self.$rulesList.append(
146 self.$rulesList.append(
147 self.addRule(
147 self.addRule(
148 _gettext('At least {0} reviewer must vote.').format(data.rules.voting))
148 _gettext('At least {0} reviewer must vote.').format(data.rules.voting))
149 )
149 )
150
150
151 } else {
151 } else {
152 self.$rulesList.append(
152 self.$rulesList.append(
153 self.addRule(
153 self.addRule(
154 _gettext('At least {0} reviewers must vote.').format(data.rules.voting))
154 _gettext('At least {0} reviewers must vote.').format(data.rules.voting))
155 )
155 )
156 }
156 }
157 }
157 }
158
158
159 if (data.rules.voting_groups !== undefined) {
159 if (data.rules.voting_groups !== undefined) {
160 $.each(data.rules.voting_groups, function(index, rule_data) {
160 $.each(data.rules.voting_groups, function(index, rule_data) {
161 self.$rulesList.append(
161 self.$rulesList.append(
162 self.addRule(rule_data.text)
162 self.addRule(rule_data.text)
163 )
163 )
164 });
164 });
165 }
165 }
166
166
167 if (data.rules.use_code_authors_for_review) {
167 if (data.rules.use_code_authors_for_review) {
168 self.$rulesList.append(
168 self.$rulesList.append(
169 self.addRule(
169 self.addRule(
170 _gettext('Reviewers picked from source code changes.'))
170 _gettext('Reviewers picked from source code changes.'))
171 )
171 )
172 }
172 }
173 if (data.rules.forbid_adding_reviewers) {
173 if (data.rules.forbid_adding_reviewers) {
174 $('#add_reviewer_input').remove();
174 $('#add_reviewer_input').remove();
175 self.$rulesList.append(
175 self.$rulesList.append(
176 self.addRule(
176 self.addRule(
177 _gettext('Adding new reviewers is forbidden.'))
177 _gettext('Adding new reviewers is forbidden.'))
178 )
178 )
179 }
179 }
180 if (data.rules.forbid_author_to_review) {
180 if (data.rules.forbid_author_to_review) {
181 self.forbidReviewUsers.push(data.rules_data.pr_author);
181 self.forbidReviewUsers.push(data.rules_data.pr_author);
182 self.$rulesList.append(
182 self.$rulesList.append(
183 self.addRule(
183 self.addRule(
184 _gettext('Author is not allowed to be a reviewer.'))
184 _gettext('Author is not allowed to be a reviewer.'))
185 )
185 )
186 }
186 }
187 if (data.rules.forbid_commit_author_to_review) {
187 if (data.rules.forbid_commit_author_to_review) {
188
188
189 if (data.rules_data.forbidden_users) {
189 if (data.rules_data.forbidden_users) {
190 $.each(data.rules_data.forbidden_users, function(index, member_data) {
190 $.each(data.rules_data.forbidden_users, function(index, member_data) {
191 self.forbidReviewUsers.push(member_data)
191 self.forbidReviewUsers.push(member_data)
192 });
192 });
193
193
194 }
194 }
195
195
196 self.$rulesList.append(
196 self.$rulesList.append(
197 self.addRule(
197 self.addRule(
198 _gettext('Commit Authors are not allowed to be a reviewer.'))
198 _gettext('Commit Authors are not allowed to be a reviewer.'))
199 )
199 )
200 }
200 }
201
201
202 return self.forbidReviewUsers
202 return self.forbidReviewUsers
203 };
203 };
204
204
205 this.loadDefaultReviewers = function(sourceRepo, sourceRef, targetRepo, targetRef) {
205 this.loadDefaultReviewers = function(sourceRepo, sourceRef, targetRepo, targetRef) {
206
206
207 if (self.currentRequest) {
207 if (self.currentRequest) {
208 // make sure we cleanup old running requests before triggering this
208 // make sure we cleanup old running requests before triggering this
209 // again
209 // again
210 self.currentRequest.abort();
210 self.currentRequest.abort();
211 }
211 }
212
212
213 $('.calculate-reviewers').show();
213 $('.calculate-reviewers').show();
214 // reset reviewer members
214 // reset reviewer members
215 self.$reviewMembers.empty();
215 self.$reviewMembers.empty();
216
216
217 prButtonLock(true, null, 'reviewers');
217 prButtonLock(true, null, 'reviewers');
218 $('#user').hide(); // hide user autocomplete before load
218 $('#user').hide(); // hide user autocomplete before load
219
219
220 if (sourceRef.length !== 3 || targetRef.length !== 3) {
221 // don't load defaults in case we're missing some refs...
222 $('.calculate-reviewers').hide();
223 return
224 }
225
220 var url = pyroutes.url('repo_default_reviewers_data',
226 var url = pyroutes.url('repo_default_reviewers_data',
221 {
227 {
222 'repo_name': templateContext.repo_name,
228 'repo_name': templateContext.repo_name,
223 'source_repo': sourceRepo,
229 'source_repo': sourceRepo,
224 'source_ref': sourceRef[2],
230 'source_ref': sourceRef[2],
225 'target_repo': targetRepo,
231 'target_repo': targetRepo,
226 'target_ref': targetRef[2]
232 'target_ref': targetRef[2]
227 });
233 });
228
234
229 self.currentRequest = $.get(url)
235 self.currentRequest = $.get(url)
230 .done(function(data) {
236 .done(function(data) {
231 self.currentRequest = null;
237 self.currentRequest = null;
232
238
233 // review rules
239 // review rules
234 self.loadReviewRules(data);
240 self.loadReviewRules(data);
235
241
236 for (var i = 0; i < data.reviewers.length; i++) {
242 for (var i = 0; i < data.reviewers.length; i++) {
237 var reviewer = data.reviewers[i];
243 var reviewer = data.reviewers[i];
238 self.addReviewMember(
244 self.addReviewMember(
239 reviewer, reviewer.reasons, reviewer.mandatory);
245 reviewer, reviewer.reasons, reviewer.mandatory);
240 }
246 }
241 $('.calculate-reviewers').hide();
247 $('.calculate-reviewers').hide();
242 prButtonLock(false, null, 'reviewers');
248 prButtonLock(false, null, 'reviewers');
243 $('#user').show(); // show user autocomplete after load
249 $('#user').show(); // show user autocomplete after load
244 });
250 });
245 };
251 };
246
252
247 // check those, refactor
253 // check those, refactor
248 this.removeReviewMember = function(reviewer_id, mark_delete) {
254 this.removeReviewMember = function(reviewer_id, mark_delete) {
249 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
255 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
250
256
251 if(typeof(mark_delete) === undefined){
257 if(typeof(mark_delete) === undefined){
252 mark_delete = false;
258 mark_delete = false;
253 }
259 }
254
260
255 if(mark_delete === true){
261 if(mark_delete === true){
256 if (reviewer){
262 if (reviewer){
257 // now delete the input
263 // now delete the input
258 $('#reviewer_{0} input'.format(reviewer_id)).remove();
264 $('#reviewer_{0} input'.format(reviewer_id)).remove();
259 // mark as to-delete
265 // mark as to-delete
260 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
266 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
261 obj.addClass('to-delete');
267 obj.addClass('to-delete');
262 obj.css({"text-decoration":"line-through", "opacity": 0.5});
268 obj.css({"text-decoration":"line-through", "opacity": 0.5});
263 }
269 }
264 }
270 }
265 else{
271 else{
266 $('#reviewer_{0}'.format(reviewer_id)).remove();
272 $('#reviewer_{0}'.format(reviewer_id)).remove();
267 }
273 }
268 };
274 };
269 this.reviewMemberEntry = function() {
275 this.reviewMemberEntry = function() {
270
276
271 };
277 };
272 this.addReviewMember = function(reviewer_obj, reasons, mandatory) {
278 this.addReviewMember = function(reviewer_obj, reasons, mandatory) {
273 var members = self.$reviewMembers.get(0);
279 var members = self.$reviewMembers.get(0);
274 var id = reviewer_obj.user_id;
280 var id = reviewer_obj.user_id;
275 var username = reviewer_obj.username;
281 var username = reviewer_obj.username;
276
282
277 var reasons = reasons || [];
283 var reasons = reasons || [];
278 var mandatory = mandatory || false;
284 var mandatory = mandatory || false;
279
285
280 // register IDS to check if we don't have this ID already in
286 // register IDS to check if we don't have this ID already in
281 var currentIds = [];
287 var currentIds = [];
282 var _els = self.$reviewMembers.find('li').toArray();
288 var _els = self.$reviewMembers.find('li').toArray();
283 for (el in _els){
289 for (el in _els){
284 currentIds.push(_els[el].id)
290 currentIds.push(_els[el].id)
285 }
291 }
286
292
287 var userAllowedReview = function(userId) {
293 var userAllowedReview = function(userId) {
288 var allowed = true;
294 var allowed = true;
289 $.each(self.forbidReviewUsers, function(index, member_data) {
295 $.each(self.forbidReviewUsers, function(index, member_data) {
290 if (parseInt(userId) === member_data['user_id']) {
296 if (parseInt(userId) === member_data['user_id']) {
291 allowed = false;
297 allowed = false;
292 return false // breaks the loop
298 return false // breaks the loop
293 }
299 }
294 });
300 });
295 return allowed
301 return allowed
296 };
302 };
297
303
298 var userAllowed = userAllowedReview(id);
304 var userAllowed = userAllowedReview(id);
299 if (!userAllowed){
305 if (!userAllowed){
300 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
306 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
301 } else {
307 } else {
302 // only add if it's not there
308 // only add if it's not there
303 var alreadyReviewer = currentIds.indexOf('reviewer_'+id) != -1;
309 var alreadyReviewer = currentIds.indexOf('reviewer_'+id) != -1;
304
310
305 if (alreadyReviewer) {
311 if (alreadyReviewer) {
306 alert(_gettext('User `{0}` already in reviewers').format(username));
312 alert(_gettext('User `{0}` already in reviewers').format(username));
307 } else {
313 } else {
308 members.innerHTML += renderTemplate('reviewMemberEntry', {
314 members.innerHTML += renderTemplate('reviewMemberEntry', {
309 'member': reviewer_obj,
315 'member': reviewer_obj,
310 'mandatory': mandatory,
316 'mandatory': mandatory,
311 'allowed_to_update': true,
317 'allowed_to_update': true,
312 'review_status': 'not_reviewed',
318 'review_status': 'not_reviewed',
313 'review_status_label': _gettext('Not Reviewed'),
319 'review_status_label': _gettext('Not Reviewed'),
314 'reasons': reasons,
320 'reasons': reasons,
315 'create': true
321 'create': true
316 });
322 });
317 }
323 }
318 }
324 }
319
325
320 };
326 };
321
327
322 this.updateReviewers = function(repo_name, pull_request_id){
328 this.updateReviewers = function(repo_name, pull_request_id){
323 var postData = $('#reviewers input').serialize();
329 var postData = $('#reviewers input').serialize();
324 _updatePullRequest(repo_name, pull_request_id, postData);
330 _updatePullRequest(repo_name, pull_request_id, postData);
325 };
331 };
326
332
327 };
333 };
328
334
329
335
330 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
336 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
331 var url = pyroutes.url(
337 var url = pyroutes.url(
332 'pullrequest_update',
338 'pullrequest_update',
333 {"repo_name": repo_name, "pull_request_id": pull_request_id});
339 {"repo_name": repo_name, "pull_request_id": pull_request_id});
334 if (typeof postData === 'string' ) {
340 if (typeof postData === 'string' ) {
335 postData += '&csrf_token=' + CSRF_TOKEN;
341 postData += '&csrf_token=' + CSRF_TOKEN;
336 } else {
342 } else {
337 postData.csrf_token = CSRF_TOKEN;
343 postData.csrf_token = CSRF_TOKEN;
338 }
344 }
339 var success = function(o) {
345 var success = function(o) {
340 window.location.reload();
346 window.location.reload();
341 };
347 };
342 ajaxPOST(url, postData, success);
348 ajaxPOST(url, postData, success);
343 };
349 };
344
350
345 /**
351 /**
346 * PULL REQUEST update commits
352 * PULL REQUEST update commits
347 */
353 */
348 var updateCommits = function(repo_name, pull_request_id) {
354 var updateCommits = function(repo_name, pull_request_id) {
349 var postData = {
355 var postData = {
350 'update_commits': true};
356 'update_commits': true};
351 _updatePullRequest(repo_name, pull_request_id, postData);
357 _updatePullRequest(repo_name, pull_request_id, postData);
352 };
358 };
353
359
354
360
355 /**
361 /**
356 * PULL REQUEST edit info
362 * PULL REQUEST edit info
357 */
363 */
358 var editPullRequest = function(repo_name, pull_request_id, title, description) {
364 var editPullRequest = function(repo_name, pull_request_id, title, description) {
359 var url = pyroutes.url(
365 var url = pyroutes.url(
360 'pullrequest_update',
366 'pullrequest_update',
361 {"repo_name": repo_name, "pull_request_id": pull_request_id});
367 {"repo_name": repo_name, "pull_request_id": pull_request_id});
362
368
363 var postData = {
369 var postData = {
364 'title': title,
370 'title': title,
365 'description': description,
371 'description': description,
366 'edit_pull_request': true,
372 'edit_pull_request': true,
367 'csrf_token': CSRF_TOKEN
373 'csrf_token': CSRF_TOKEN
368 };
374 };
369 var success = function(o) {
375 var success = function(o) {
370 window.location.reload();
376 window.location.reload();
371 };
377 };
372 ajaxPOST(url, postData, success);
378 ajaxPOST(url, postData, success);
373 };
379 };
374
380
375 var initPullRequestsCodeMirror = function (textAreaId) {
381 var initPullRequestsCodeMirror = function (textAreaId) {
376 var ta = $(textAreaId).get(0);
382 var ta = $(textAreaId).get(0);
377 var initialHeight = '100px';
383 var initialHeight = '100px';
378
384
379 // default options
385 // default options
380 var codeMirrorOptions = {
386 var codeMirrorOptions = {
381 mode: "text",
387 mode: "text",
382 lineNumbers: false,
388 lineNumbers: false,
383 indentUnit: 4,
389 indentUnit: 4,
384 theme: 'rc-input'
390 theme: 'rc-input'
385 };
391 };
386
392
387 var codeMirrorInstance = CodeMirror.fromTextArea(ta, codeMirrorOptions);
393 var codeMirrorInstance = CodeMirror.fromTextArea(ta, codeMirrorOptions);
388 // marker for manually set description
394 // marker for manually set description
389 codeMirrorInstance._userDefinedDesc = false;
395 codeMirrorInstance._userDefinedDesc = false;
390 codeMirrorInstance.setSize(null, initialHeight);
396 codeMirrorInstance.setSize(null, initialHeight);
391 codeMirrorInstance.on("change", function(instance, changeObj) {
397 codeMirrorInstance.on("change", function(instance, changeObj) {
392 var height = initialHeight;
398 var height = initialHeight;
393 var lines = instance.lineCount();
399 var lines = instance.lineCount();
394 if (lines > 6 && lines < 20) {
400 if (lines > 6 && lines < 20) {
395 height = "auto"
401 height = "auto"
396 }
402 }
397 else if (lines >= 20) {
403 else if (lines >= 20) {
398 height = 20 * 15;
404 height = 20 * 15;
399 }
405 }
400 instance.setSize(null, height);
406 instance.setSize(null, height);
401
407
402 // detect if the change was trigger by auto desc, or user input
408 // detect if the change was trigger by auto desc, or user input
403 changeOrigin = changeObj.origin;
409 changeOrigin = changeObj.origin;
404
410
405 if (changeOrigin === "setValue") {
411 if (changeOrigin === "setValue") {
406 cmLog.debug('Change triggered by setValue');
412 cmLog.debug('Change triggered by setValue');
407 }
413 }
408 else {
414 else {
409 cmLog.debug('user triggered change !');
415 cmLog.debug('user triggered change !');
410 // set special marker to indicate user has created an input.
416 // set special marker to indicate user has created an input.
411 instance._userDefinedDesc = true;
417 instance._userDefinedDesc = true;
412 }
418 }
413
419
414 });
420 });
415
421
416 return codeMirrorInstance
422 return codeMirrorInstance
417 };
423 };
418
424
419 /**
425 /**
420 * Reviewer autocomplete
426 * Reviewer autocomplete
421 */
427 */
422 var ReviewerAutoComplete = function(inputId) {
428 var ReviewerAutoComplete = function(inputId) {
423 $(inputId).autocomplete({
429 $(inputId).autocomplete({
424 serviceUrl: pyroutes.url('user_autocomplete_data'),
430 serviceUrl: pyroutes.url('user_autocomplete_data'),
425 minChars:2,
431 minChars:2,
426 maxHeight:400,
432 maxHeight:400,
427 deferRequestBy: 300, //miliseconds
433 deferRequestBy: 300, //miliseconds
428 showNoSuggestionNotice: true,
434 showNoSuggestionNotice: true,
429 tabDisabled: true,
435 tabDisabled: true,
430 autoSelectFirst: true,
436 autoSelectFirst: true,
431 params: { user_id: templateContext.rhodecode_user.user_id, user_groups:true, user_groups_expand:true, skip_default_user:true },
437 params: { user_id: templateContext.rhodecode_user.user_id, user_groups:true, user_groups_expand:true, skip_default_user:true },
432 formatResult: autocompleteFormatResult,
438 formatResult: autocompleteFormatResult,
433 lookupFilter: autocompleteFilterResult,
439 lookupFilter: autocompleteFilterResult,
434 onSelect: function(element, data) {
440 onSelect: function(element, data) {
435 var mandatory = false;
441 var mandatory = false;
436 var reasons = [_gettext('added manually by "{0}"').format(templateContext.rhodecode_user.username)];
442 var reasons = [_gettext('added manually by "{0}"').format(templateContext.rhodecode_user.username)];
437
443
438 // add whole user groups
444 // add whole user groups
439 if (data.value_type == 'user_group') {
445 if (data.value_type == 'user_group') {
440 reasons.push(_gettext('member of "{0}"').format(data.value_display));
446 reasons.push(_gettext('member of "{0}"').format(data.value_display));
441
447
442 $.each(data.members, function(index, member_data) {
448 $.each(data.members, function(index, member_data) {
443 var reviewer = member_data;
449 var reviewer = member_data;
444 reviewer['user_id'] = member_data['id'];
450 reviewer['user_id'] = member_data['id'];
445 reviewer['gravatar_link'] = member_data['icon_link'];
451 reviewer['gravatar_link'] = member_data['icon_link'];
446 reviewer['user_link'] = member_data['profile_link'];
452 reviewer['user_link'] = member_data['profile_link'];
447 reviewer['rules'] = [];
453 reviewer['rules'] = [];
448 reviewersController.addReviewMember(reviewer, reasons, mandatory);
454 reviewersController.addReviewMember(reviewer, reasons, mandatory);
449 })
455 })
450 }
456 }
451 // add single user
457 // add single user
452 else {
458 else {
453 var reviewer = data;
459 var reviewer = data;
454 reviewer['user_id'] = data['id'];
460 reviewer['user_id'] = data['id'];
455 reviewer['gravatar_link'] = data['icon_link'];
461 reviewer['gravatar_link'] = data['icon_link'];
456 reviewer['user_link'] = data['profile_link'];
462 reviewer['user_link'] = data['profile_link'];
457 reviewer['rules'] = [];
463 reviewer['rules'] = [];
458 reviewersController.addReviewMember(reviewer, reasons, mandatory);
464 reviewersController.addReviewMember(reviewer, reasons, mandatory);
459 }
465 }
460
466
461 $(inputId).val('');
467 $(inputId).val('');
462 }
468 }
463 });
469 });
464 };
470 };
465
471
466
472
467 VersionController = function () {
473 VersionController = function () {
468 var self = this;
474 var self = this;
469 this.$verSource = $('input[name=ver_source]');
475 this.$verSource = $('input[name=ver_source]');
470 this.$verTarget = $('input[name=ver_target]');
476 this.$verTarget = $('input[name=ver_target]');
471 this.$showVersionDiff = $('#show-version-diff');
477 this.$showVersionDiff = $('#show-version-diff');
472
478
473 this.adjustRadioSelectors = function (curNode) {
479 this.adjustRadioSelectors = function (curNode) {
474 var getVal = function (item) {
480 var getVal = function (item) {
475 if (item == 'latest') {
481 if (item == 'latest') {
476 return Number.MAX_SAFE_INTEGER
482 return Number.MAX_SAFE_INTEGER
477 }
483 }
478 else {
484 else {
479 return parseInt(item)
485 return parseInt(item)
480 }
486 }
481 };
487 };
482
488
483 var curVal = getVal($(curNode).val());
489 var curVal = getVal($(curNode).val());
484 var cleared = false;
490 var cleared = false;
485
491
486 $.each(self.$verSource, function (index, value) {
492 $.each(self.$verSource, function (index, value) {
487 var elVal = getVal($(value).val());
493 var elVal = getVal($(value).val());
488
494
489 if (elVal > curVal) {
495 if (elVal > curVal) {
490 if ($(value).is(':checked')) {
496 if ($(value).is(':checked')) {
491 cleared = true;
497 cleared = true;
492 }
498 }
493 $(value).attr('disabled', 'disabled');
499 $(value).attr('disabled', 'disabled');
494 $(value).removeAttr('checked');
500 $(value).removeAttr('checked');
495 $(value).css({'opacity': 0.1});
501 $(value).css({'opacity': 0.1});
496 }
502 }
497 else {
503 else {
498 $(value).css({'opacity': 1});
504 $(value).css({'opacity': 1});
499 $(value).removeAttr('disabled');
505 $(value).removeAttr('disabled');
500 }
506 }
501 });
507 });
502
508
503 if (cleared) {
509 if (cleared) {
504 // if we unchecked an active, set the next one to same loc.
510 // if we unchecked an active, set the next one to same loc.
505 $(this.$verSource).filter('[value={0}]'.format(
511 $(this.$verSource).filter('[value={0}]'.format(
506 curVal)).attr('checked', 'checked');
512 curVal)).attr('checked', 'checked');
507 }
513 }
508
514
509 self.setLockAction(false,
515 self.setLockAction(false,
510 $(curNode).data('verPos'),
516 $(curNode).data('verPos'),
511 $(this.$verSource).filter(':checked').data('verPos')
517 $(this.$verSource).filter(':checked').data('verPos')
512 );
518 );
513 };
519 };
514
520
515
521
516 this.attachVersionListener = function () {
522 this.attachVersionListener = function () {
517 self.$verTarget.change(function (e) {
523 self.$verTarget.change(function (e) {
518 self.adjustRadioSelectors(this)
524 self.adjustRadioSelectors(this)
519 });
525 });
520 self.$verSource.change(function (e) {
526 self.$verSource.change(function (e) {
521 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
527 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
522 });
528 });
523 };
529 };
524
530
525 this.init = function () {
531 this.init = function () {
526
532
527 var curNode = self.$verTarget.filter(':checked');
533 var curNode = self.$verTarget.filter(':checked');
528 self.adjustRadioSelectors(curNode);
534 self.adjustRadioSelectors(curNode);
529 self.setLockAction(true);
535 self.setLockAction(true);
530 self.attachVersionListener();
536 self.attachVersionListener();
531
537
532 };
538 };
533
539
534 this.setLockAction = function (state, selectedVersion, otherVersion) {
540 this.setLockAction = function (state, selectedVersion, otherVersion) {
535 var $showVersionDiff = this.$showVersionDiff;
541 var $showVersionDiff = this.$showVersionDiff;
536
542
537 if (state) {
543 if (state) {
538 $showVersionDiff.attr('disabled', 'disabled');
544 $showVersionDiff.attr('disabled', 'disabled');
539 $showVersionDiff.addClass('disabled');
545 $showVersionDiff.addClass('disabled');
540 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
546 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
541 }
547 }
542 else {
548 else {
543 $showVersionDiff.removeAttr('disabled');
549 $showVersionDiff.removeAttr('disabled');
544 $showVersionDiff.removeClass('disabled');
550 $showVersionDiff.removeClass('disabled');
545
551
546 if (selectedVersion == otherVersion) {
552 if (selectedVersion == otherVersion) {
547 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
553 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
548 } else {
554 } else {
549 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
555 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
550 }
556 }
551 }
557 }
552
558
553 };
559 };
554
560
555 this.showVersionDiff = function () {
561 this.showVersionDiff = function () {
556 var target = self.$verTarget.filter(':checked');
562 var target = self.$verTarget.filter(':checked');
557 var source = self.$verSource.filter(':checked');
563 var source = self.$verSource.filter(':checked');
558
564
559 if (target.val() && source.val()) {
565 if (target.val() && source.val()) {
560 var params = {
566 var params = {
561 'pull_request_id': templateContext.pull_request_data.pull_request_id,
567 'pull_request_id': templateContext.pull_request_data.pull_request_id,
562 'repo_name': templateContext.repo_name,
568 'repo_name': templateContext.repo_name,
563 'version': target.val(),
569 'version': target.val(),
564 'from_version': source.val()
570 'from_version': source.val()
565 };
571 };
566 window.location = pyroutes.url('pullrequest_show', params)
572 window.location = pyroutes.url('pullrequest_show', params)
567 }
573 }
568
574
569 return false;
575 return false;
570 };
576 };
571
577
572 this.toggleVersionView = function (elem) {
578 this.toggleVersionView = function (elem) {
573
579
574 if (this.$showVersionDiff.is(':visible')) {
580 if (this.$showVersionDiff.is(':visible')) {
575 $('.version-pr').hide();
581 $('.version-pr').hide();
576 this.$showVersionDiff.hide();
582 this.$showVersionDiff.hide();
577 $(elem).html($(elem).data('toggleOn'))
583 $(elem).html($(elem).data('toggleOn'))
578 } else {
584 } else {
579 $('.version-pr').show();
585 $('.version-pr').show();
580 this.$showVersionDiff.show();
586 this.$showVersionDiff.show();
581 $(elem).html($(elem).data('toggleOff'))
587 $(elem).html($(elem).data('toggleOff'))
582 }
588 }
583
589
584 return false
590 return false
585 }
591 }
586
592
587 }; No newline at end of file
593 };
@@ -1,530 +1,537 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2
2
3 <%def name="title()">
3 <%def name="title()">
4 ${c.repo_name} ${_('New pull request')}
4 ${c.repo_name} ${_('New pull request')}
5 </%def>
5 </%def>
6
6
7 <%def name="breadcrumbs_links()">
7 <%def name="breadcrumbs_links()">
8 ${_('New pull request')}
8 ${_('New pull request')}
9 </%def>
9 </%def>
10
10
11 <%def name="menu_bar_nav()">
11 <%def name="menu_bar_nav()">
12 ${self.menu_items(active='repositories')}
12 ${self.menu_items(active='repositories')}
13 </%def>
13 </%def>
14
14
15 <%def name="menu_bar_subnav()">
15 <%def name="menu_bar_subnav()">
16 ${self.repo_menu(active='showpullrequest')}
16 ${self.repo_menu(active='showpullrequest')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <div class="title">
21 <div class="title">
22 ${self.repo_page_title(c.rhodecode_db_repo)}
22 ${self.repo_page_title(c.rhodecode_db_repo)}
23 </div>
23 </div>
24
24
25 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
25 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
26
26
27 ${self.breadcrumbs()}
27 ${self.breadcrumbs()}
28
28
29 <div class="box pr-summary">
29 <div class="box pr-summary">
30
30
31 <div class="summary-details block-left">
31 <div class="summary-details block-left">
32
32
33
33
34 <div class="pr-details-title">
34 <div class="pr-details-title">
35 ${_('Pull request summary')}
35 ${_('Pull request summary')}
36 </div>
36 </div>
37
37
38 <div class="form" style="padding-top: 10px">
38 <div class="form" style="padding-top: 10px">
39 <!-- fields -->
39 <!-- fields -->
40
40
41 <div class="fields" >
41 <div class="fields" >
42
42
43 <div class="field">
43 <div class="field">
44 <div class="label">
44 <div class="label">
45 <label for="pullrequest_title">${_('Title')}:</label>
45 <label for="pullrequest_title">${_('Title')}:</label>
46 </div>
46 </div>
47 <div class="input">
47 <div class="input">
48 ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")}
48 ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")}
49 </div>
49 </div>
50 </div>
50 </div>
51
51
52 <div class="field">
52 <div class="field">
53 <div class="label label-textarea">
53 <div class="label label-textarea">
54 <label for="pullrequest_desc">${_('Description')}:</label>
54 <label for="pullrequest_desc">${_('Description')}:</label>
55 </div>
55 </div>
56 <div class="textarea text-area editor">
56 <div class="textarea text-area editor">
57 ${h.textarea('pullrequest_desc',size=30, )}
57 ${h.textarea('pullrequest_desc',size=30, )}
58 <span class="help-block">${_('Write a short description on this pull request')}</span>
58 <span class="help-block">${_('Write a short description on this pull request')}</span>
59 </div>
59 </div>
60 </div>
60 </div>
61
61
62 <div class="field">
62 <div class="field">
63 <div class="label label-textarea">
63 <div class="label label-textarea">
64 <label for="pullrequest_desc">${_('Commit flow')}:</label>
64 <label for="pullrequest_desc">${_('Commit flow')}:</label>
65 </div>
65 </div>
66
66
67 ## TODO: johbo: Abusing the "content" class here to get the
67 ## TODO: johbo: Abusing the "content" class here to get the
68 ## desired effect. Should be replaced by a proper solution.
68 ## desired effect. Should be replaced by a proper solution.
69
69
70 ##ORG
70 ##ORG
71 <div class="content">
71 <div class="content">
72 <strong>${_('Source repository')}:</strong>
72 <strong>${_('Source repository')}:</strong>
73 ${c.rhodecode_db_repo.description}
73 ${c.rhodecode_db_repo.description}
74 </div>
74 </div>
75 <div class="content">
75 <div class="content">
76 ${h.hidden('source_repo')}
76 ${h.hidden('source_repo')}
77 ${h.hidden('source_ref')}
77 ${h.hidden('source_ref')}
78 </div>
78 </div>
79
79
80 ##OTHER, most Probably the PARENT OF THIS FORK
80 ##OTHER, most Probably the PARENT OF THIS FORK
81 <div class="content">
81 <div class="content">
82 ## filled with JS
82 ## filled with JS
83 <div id="target_repo_desc"></div>
83 <div id="target_repo_desc"></div>
84 </div>
84 </div>
85
85
86 <div class="content">
86 <div class="content">
87 ${h.hidden('target_repo')}
87 ${h.hidden('target_repo')}
88 ${h.hidden('target_ref')}
88 ${h.hidden('target_ref')}
89 <span id="target_ref_loading" style="display: none">
89 <span id="target_ref_loading" style="display: none">
90 ${_('Loading refs...')}
90 ${_('Loading refs...')}
91 </span>
91 </span>
92 </div>
92 </div>
93 </div>
93 </div>
94
94
95 <div class="field">
95 <div class="field">
96 <div class="label label-textarea">
96 <div class="label label-textarea">
97 <label for="pullrequest_submit"></label>
97 <label for="pullrequest_submit"></label>
98 </div>
98 </div>
99 <div class="input">
99 <div class="input">
100 <div class="pr-submit-button">
100 <div class="pr-submit-button">
101 ${h.submit('save',_('Submit Pull Request'),class_="btn")}
101 ${h.submit('save',_('Submit Pull Request'),class_="btn")}
102 </div>
102 </div>
103 <div id="pr_open_message"></div>
103 <div id="pr_open_message"></div>
104 </div>
104 </div>
105 </div>
105 </div>
106
106
107 <div class="pr-spacing-container"></div>
107 <div class="pr-spacing-container"></div>
108 </div>
108 </div>
109 </div>
109 </div>
110 </div>
110 </div>
111 <div>
111 <div>
112 ## AUTHOR
112 ## AUTHOR
113 <div class="reviewers-title block-right">
113 <div class="reviewers-title block-right">
114 <div class="pr-details-title">
114 <div class="pr-details-title">
115 ${_('Author of this pull request')}
115 ${_('Author of this pull request')}
116 </div>
116 </div>
117 </div>
117 </div>
118 <div class="block-right pr-details-content reviewers">
118 <div class="block-right pr-details-content reviewers">
119 <ul class="group_members">
119 <ul class="group_members">
120 <li>
120 <li>
121 ${self.gravatar_with_user(c.rhodecode_user.email, 16)}
121 ${self.gravatar_with_user(c.rhodecode_user.email, 16)}
122 </li>
122 </li>
123 </ul>
123 </ul>
124 </div>
124 </div>
125
125
126 ## REVIEW RULES
126 ## REVIEW RULES
127 <div id="review_rules" style="display: none" class="reviewers-title block-right">
127 <div id="review_rules" style="display: none" class="reviewers-title block-right">
128 <div class="pr-details-title">
128 <div class="pr-details-title">
129 ${_('Reviewer rules')}
129 ${_('Reviewer rules')}
130 </div>
130 </div>
131 <div class="pr-reviewer-rules">
131 <div class="pr-reviewer-rules">
132 ## review rules will be appended here, by default reviewers logic
132 ## review rules will be appended here, by default reviewers logic
133 </div>
133 </div>
134 </div>
134 </div>
135
135
136 ## REVIEWERS
136 ## REVIEWERS
137 <div class="reviewers-title block-right">
137 <div class="reviewers-title block-right">
138 <div class="pr-details-title">
138 <div class="pr-details-title">
139 ${_('Pull request reviewers')}
139 ${_('Pull request reviewers')}
140 <span class="calculate-reviewers"> - ${_('loading...')}</span>
140 <span class="calculate-reviewers"> - ${_('loading...')}</span>
141 </div>
141 </div>
142 </div>
142 </div>
143 <div id="reviewers" class="block-right pr-details-content reviewers">
143 <div id="reviewers" class="block-right pr-details-content reviewers">
144 ## members goes here, filled via JS based on initial selection !
144 ## members goes here, filled via JS based on initial selection !
145 <input type="hidden" name="__start__" value="review_members:sequence">
145 <input type="hidden" name="__start__" value="review_members:sequence">
146 <ul id="review_members" class="group_members"></ul>
146 <ul id="review_members" class="group_members"></ul>
147 <input type="hidden" name="__end__" value="review_members:sequence">
147 <input type="hidden" name="__end__" value="review_members:sequence">
148 <div id="add_reviewer_input" class='ac'>
148 <div id="add_reviewer_input" class='ac'>
149 <div class="reviewer_ac">
149 <div class="reviewer_ac">
150 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
150 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
151 <div id="reviewers_container"></div>
151 <div id="reviewers_container"></div>
152 </div>
152 </div>
153 </div>
153 </div>
154 </div>
154 </div>
155 </div>
155 </div>
156 </div>
156 </div>
157 <div class="box">
157 <div class="box">
158 <div>
158 <div>
159 ## overview pulled by ajax
159 ## overview pulled by ajax
160 <div id="pull_request_overview"></div>
160 <div id="pull_request_overview"></div>
161 </div>
161 </div>
162 </div>
162 </div>
163 ${h.end_form()}
163 ${h.end_form()}
164 </div>
164 </div>
165
165
166 <script type="text/javascript">
166 <script type="text/javascript">
167 $(function(){
167 $(function(){
168 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
168 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
169 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
169 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
170 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
170 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
171 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
171 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
172
172
173 var $pullRequestForm = $('#pull_request_form');
173 var $pullRequestForm = $('#pull_request_form');
174 var $sourceRepo = $('#source_repo', $pullRequestForm);
174 var $sourceRepo = $('#source_repo', $pullRequestForm);
175 var $targetRepo = $('#target_repo', $pullRequestForm);
175 var $targetRepo = $('#target_repo', $pullRequestForm);
176 var $sourceRef = $('#source_ref', $pullRequestForm);
176 var $sourceRef = $('#source_ref', $pullRequestForm);
177 var $targetRef = $('#target_ref', $pullRequestForm);
177 var $targetRef = $('#target_ref', $pullRequestForm);
178
178
179 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
179 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
180 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
180 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
181
181
182 var targetRepo = function() { return $targetRepo.eq(0).val() };
182 var targetRepo = function() { return $targetRepo.eq(0).val() };
183 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
183 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
184
184
185 var calculateContainerWidth = function() {
185 var calculateContainerWidth = function() {
186 var maxWidth = 0;
186 var maxWidth = 0;
187 var repoSelect2Containers = ['#source_repo', '#target_repo'];
187 var repoSelect2Containers = ['#source_repo', '#target_repo'];
188 $.each(repoSelect2Containers, function(idx, value) {
188 $.each(repoSelect2Containers, function(idx, value) {
189 $(value).select2('container').width('auto');
189 $(value).select2('container').width('auto');
190 var curWidth = $(value).select2('container').width();
190 var curWidth = $(value).select2('container').width();
191 if (maxWidth <= curWidth) {
191 if (maxWidth <= curWidth) {
192 maxWidth = curWidth;
192 maxWidth = curWidth;
193 }
193 }
194 $.each(repoSelect2Containers, function(idx, value) {
194 $.each(repoSelect2Containers, function(idx, value) {
195 $(value).select2('container').width(maxWidth + 10);
195 $(value).select2('container').width(maxWidth + 10);
196 });
196 });
197 });
197 });
198 };
198 };
199
199
200 var initRefSelection = function(selectedRef) {
200 var initRefSelection = function(selectedRef) {
201 return function(element, callback) {
201 return function(element, callback) {
202 // translate our select2 id into a text, it's a mapping to show
202 // translate our select2 id into a text, it's a mapping to show
203 // simple label when selecting by internal ID.
203 // simple label when selecting by internal ID.
204 var id, refData;
204 var id, refData;
205 if (selectedRef === undefined) {
205 if (selectedRef === undefined || selectedRef === null) {
206 id = element.val();
206 id = element.val();
207 refData = element.val().split(':');
207 refData = element.val().split(':');
208
209 if (refData.length !== 3){
210 refData = ["", "", ""]
211 }
208 } else {
212 } else {
209 id = selectedRef;
213 id = selectedRef;
210 refData = selectedRef.split(':');
214 refData = selectedRef.split(':');
211 }
215 }
212
216
213 var text = refData[1];
217 var text = refData[1];
214 if (refData[0] === 'rev') {
218 if (refData[0] === 'rev') {
215 text = text.substring(0, 12);
219 text = text.substring(0, 12);
216 }
220 }
217
221
218 var data = {id: id, text: text};
222 var data = {id: id, text: text};
219
220 callback(data);
223 callback(data);
221 };
224 };
222 };
225 };
223
226
224 var formatRefSelection = function(item) {
227 var formatRefSelection = function(item) {
225 var prefix = '';
228 var prefix = '';
226 var refData = item.id.split(':');
229 var refData = item.id.split(':');
227 if (refData[0] === 'branch') {
230 if (refData[0] === 'branch') {
228 prefix = '<i class="icon-branch"></i>';
231 prefix = '<i class="icon-branch"></i>';
229 }
232 }
230 else if (refData[0] === 'book') {
233 else if (refData[0] === 'book') {
231 prefix = '<i class="icon-bookmark"></i>';
234 prefix = '<i class="icon-bookmark"></i>';
232 }
235 }
233 else if (refData[0] === 'tag') {
236 else if (refData[0] === 'tag') {
234 prefix = '<i class="icon-tag"></i>';
237 prefix = '<i class="icon-tag"></i>';
235 }
238 }
236
239
237 var originalOption = item.element;
240 var originalOption = item.element;
238 return prefix + item.text;
241 return prefix + item.text;
239 };
242 };
240
243
241 // custom code mirror
244 // custom code mirror
242 var codeMirrorInstance = initPullRequestsCodeMirror('#pullrequest_desc');
245 var codeMirrorInstance = initPullRequestsCodeMirror('#pullrequest_desc');
243
246
244 reviewersController = new ReviewersController();
247 reviewersController = new ReviewersController();
245
248
246 var queryTargetRepo = function(self, query) {
249 var queryTargetRepo = function(self, query) {
247 // cache ALL results if query is empty
250 // cache ALL results if query is empty
248 var cacheKey = query.term || '__';
251 var cacheKey = query.term || '__';
249 var cachedData = self.cachedDataSource[cacheKey];
252 var cachedData = self.cachedDataSource[cacheKey];
250
253
251 if (cachedData) {
254 if (cachedData) {
252 query.callback({results: cachedData.results});
255 query.callback({results: cachedData.results});
253 } else {
256 } else {
254 $.ajax({
257 $.ajax({
255 url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}),
258 url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}),
256 data: {query: query.term},
259 data: {query: query.term},
257 dataType: 'json',
260 dataType: 'json',
258 type: 'GET',
261 type: 'GET',
259 success: function(data) {
262 success: function(data) {
260 self.cachedDataSource[cacheKey] = data;
263 self.cachedDataSource[cacheKey] = data;
261 query.callback({results: data.results});
264 query.callback({results: data.results});
262 },
265 },
263 error: function(data, textStatus, errorThrown) {
266 error: function(data, textStatus, errorThrown) {
264 alert(
267 alert(
265 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
268 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
266 }
269 }
267 });
270 });
268 }
271 }
269 };
272 };
270
273
271 var queryTargetRefs = function(initialData, query) {
274 var queryTargetRefs = function(initialData, query) {
272 var data = {results: []};
275 var data = {results: []};
273 // filter initialData
276 // filter initialData
274 $.each(initialData, function() {
277 $.each(initialData, function() {
275 var section = this.text;
278 var section = this.text;
276 var children = [];
279 var children = [];
277 $.each(this.children, function() {
280 $.each(this.children, function() {
278 if (query.term.length === 0 ||
281 if (query.term.length === 0 ||
279 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
282 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
280 children.push({'id': this.id, 'text': this.text})
283 children.push({'id': this.id, 'text': this.text})
281 }
284 }
282 });
285 });
283 data.results.push({'text': section, 'children': children})
286 data.results.push({'text': section, 'children': children})
284 });
287 });
285 query.callback({results: data.results});
288 query.callback({results: data.results});
286 };
289 };
287
290
288 var loadRepoRefDiffPreview = function() {
291 var loadRepoRefDiffPreview = function() {
289
292
290 var url_data = {
293 var url_data = {
291 'repo_name': targetRepo(),
294 'repo_name': targetRepo(),
292 'target_repo': sourceRepo(),
295 'target_repo': sourceRepo(),
293 'source_ref': targetRef()[2],
296 'source_ref': targetRef()[2],
294 'source_ref_type': 'rev',
297 'source_ref_type': 'rev',
295 'target_ref': sourceRef()[2],
298 'target_ref': sourceRef()[2],
296 'target_ref_type': 'rev',
299 'target_ref_type': 'rev',
297 'merge': true,
300 'merge': true,
298 '_': Date.now() // bypass browser caching
301 '_': Date.now() // bypass browser caching
299 }; // gather the source/target ref and repo here
302 }; // gather the source/target ref and repo here
300
303
301 if (sourceRef().length !== 3 || targetRef().length !== 3) {
304 if (sourceRef().length !== 3 || targetRef().length !== 3) {
302 prButtonLock(true, "${_('Please select source and target')}");
305 prButtonLock(true, "${_('Please select source and target')}");
303 return;
306 return;
304 }
307 }
305 var url = pyroutes.url('repo_compare', url_data);
308 var url = pyroutes.url('repo_compare', url_data);
306
309
307 // lock PR button, so we cannot send PR before it's calculated
310 // lock PR button, so we cannot send PR before it's calculated
308 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
311 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
309
312
310 if (loadRepoRefDiffPreview._currentRequest) {
313 if (loadRepoRefDiffPreview._currentRequest) {
311 loadRepoRefDiffPreview._currentRequest.abort();
314 loadRepoRefDiffPreview._currentRequest.abort();
312 }
315 }
313
316
314 loadRepoRefDiffPreview._currentRequest = $.get(url)
317 loadRepoRefDiffPreview._currentRequest = $.get(url)
315 .error(function(data, textStatus, errorThrown) {
318 .error(function(data, textStatus, errorThrown) {
316 if (textStatus !== 'abort') {
319 if (textStatus !== 'abort') {
317 alert(
320 alert(
318 "Error while processing request.\nError code {0} ({1}).".format(
321 "Error while processing request.\nError code {0} ({1}).".format(
319 data.status, data.statusText));
322 data.status, data.statusText));
320 }
323 }
321
324
322 })
325 })
323 .done(function(data) {
326 .done(function(data) {
324 loadRepoRefDiffPreview._currentRequest = null;
327 loadRepoRefDiffPreview._currentRequest = null;
325 $('#pull_request_overview').html(data);
328 $('#pull_request_overview').html(data);
326
329
327 var commitElements = $(data).find('tr[commit_id]');
330 var commitElements = $(data).find('tr[commit_id]');
328
331
329 var prTitleAndDesc = getTitleAndDescription(
332 var prTitleAndDesc = getTitleAndDescription(
330 sourceRef()[1], commitElements, 5);
333 sourceRef()[1], commitElements, 5);
331
334
332 var title = prTitleAndDesc[0];
335 var title = prTitleAndDesc[0];
333 var proposedDescription = prTitleAndDesc[1];
336 var proposedDescription = prTitleAndDesc[1];
334
337
335 var useGeneratedTitle = (
338 var useGeneratedTitle = (
336 $('#pullrequest_title').hasClass('autogenerated-title') ||
339 $('#pullrequest_title').hasClass('autogenerated-title') ||
337 $('#pullrequest_title').val() === "");
340 $('#pullrequest_title').val() === "");
338
341
339 if (title && useGeneratedTitle) {
342 if (title && useGeneratedTitle) {
340 // use generated title if we haven't specified our own
343 // use generated title if we haven't specified our own
341 $('#pullrequest_title').val(title);
344 $('#pullrequest_title').val(title);
342 $('#pullrequest_title').addClass('autogenerated-title');
345 $('#pullrequest_title').addClass('autogenerated-title');
343
346
344 }
347 }
345
348
346 var useGeneratedDescription = (
349 var useGeneratedDescription = (
347 !codeMirrorInstance._userDefinedDesc ||
350 !codeMirrorInstance._userDefinedDesc ||
348 codeMirrorInstance.getValue() === "");
351 codeMirrorInstance.getValue() === "");
349
352
350 if (proposedDescription && useGeneratedDescription) {
353 if (proposedDescription && useGeneratedDescription) {
351 // set proposed content, if we haven't defined our own,
354 // set proposed content, if we haven't defined our own,
352 // or we don't have description written
355 // or we don't have description written
353 codeMirrorInstance._userDefinedDesc = false; // reset state
356 codeMirrorInstance._userDefinedDesc = false; // reset state
354 codeMirrorInstance.setValue(proposedDescription);
357 codeMirrorInstance.setValue(proposedDescription);
355 }
358 }
356
359
357 var msg = '';
360 var msg = '';
358 if (commitElements.length === 1) {
361 if (commitElements.length === 1) {
359 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
362 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
360 } else {
363 } else {
361 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
364 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
362 }
365 }
363
366
364 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
367 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
365
368
366 if (commitElements.length) {
369 if (commitElements.length) {
367 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
370 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
368 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
371 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
369 }
372 }
370 else {
373 else {
371 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
374 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
372 }
375 }
373
376
374
377
375 });
378 });
376 };
379 };
377
380
378 var Select2Box = function(element, overrides) {
381 var Select2Box = function(element, overrides) {
379 var globalDefaults = {
382 var globalDefaults = {
380 dropdownAutoWidth: true,
383 dropdownAutoWidth: true,
381 containerCssClass: "drop-menu",
384 containerCssClass: "drop-menu",
382 dropdownCssClass: "drop-menu-dropdown"
385 dropdownCssClass: "drop-menu-dropdown"
383 };
386 };
384
387
385 var initSelect2 = function(defaultOptions) {
388 var initSelect2 = function(defaultOptions) {
386 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
389 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
387 element.select2(options);
390 element.select2(options);
388 };
391 };
389
392
390 return {
393 return {
391 initRef: function() {
394 initRef: function() {
392 var defaultOptions = {
395 var defaultOptions = {
393 minimumResultsForSearch: 5,
396 minimumResultsForSearch: 5,
394 formatSelection: formatRefSelection
397 formatSelection: formatRefSelection
395 };
398 };
396
399
397 initSelect2(defaultOptions);
400 initSelect2(defaultOptions);
398 },
401 },
399
402
400 initRepo: function(defaultValue, readOnly) {
403 initRepo: function(defaultValue, readOnly) {
401 var defaultOptions = {
404 var defaultOptions = {
402 initSelection : function (element, callback) {
405 initSelection : function (element, callback) {
403 var data = {id: defaultValue, text: defaultValue};
406 var data = {id: defaultValue, text: defaultValue};
404 callback(data);
407 callback(data);
405 }
408 }
406 };
409 };
407
410
408 initSelect2(defaultOptions);
411 initSelect2(defaultOptions);
409
412
410 element.select2('val', defaultSourceRepo);
413 element.select2('val', defaultSourceRepo);
411 if (readOnly === true) {
414 if (readOnly === true) {
412 element.select2('readonly', true);
415 element.select2('readonly', true);
413 }
416 }
414 }
417 }
415 };
418 };
416 };
419 };
417
420
418 var initTargetRefs = function(refsData, selectedRef){
421 var initTargetRefs = function(refsData, selectedRef) {
422
419 Select2Box($targetRef, {
423 Select2Box($targetRef, {
424 placeholder: "${_('Select commit reference')}",
420 query: function(query) {
425 query: function(query) {
421 queryTargetRefs(refsData, query);
426 queryTargetRefs(refsData, query);
422 },
427 },
423 initSelection : initRefSelection(selectedRef)
428 initSelection : initRefSelection(selectedRef)
424 }).initRef();
429 }).initRef();
425
430
426 if (!(selectedRef === undefined)) {
431 if (!(selectedRef === undefined)) {
427 $targetRef.select2('val', selectedRef);
432 $targetRef.select2('val', selectedRef);
428 }
433 }
429 };
434 };
430
435
431 var targetRepoChanged = function(repoData) {
436 var targetRepoChanged = function(repoData) {
432 // generate new DESC of target repo displayed next to select
437 // generate new DESC of target repo displayed next to select
433 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
438 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
434 $('#target_repo_desc').html(
439 $('#target_repo_desc').html(
435 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
440 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
436 );
441 );
437
442
438 // generate dynamic select2 for refs.
443 // generate dynamic select2 for refs.
439 initTargetRefs(repoData['refs']['select2_refs'],
444 initTargetRefs(repoData['refs']['select2_refs'],
440 repoData['refs']['selected_ref']);
445 repoData['refs']['selected_ref']);
441
446
442 };
447 };
443
448
444 var sourceRefSelect2 = Select2Box($sourceRef, {
449 var sourceRefSelect2 = Select2Box($sourceRef, {
445 placeholder: "${_('Select commit reference')}",
450 placeholder: "${_('Select commit reference')}",
446 query: function(query) {
451 query: function(query) {
447 var initialData = defaultSourceRepoData['refs']['select2_refs'];
452 var initialData = defaultSourceRepoData['refs']['select2_refs'];
448 queryTargetRefs(initialData, query)
453 queryTargetRefs(initialData, query)
449 },
454 },
450 initSelection: initRefSelection()
455 initSelection: initRefSelection()
451 }
456 }
452 );
457 );
453
458
454 var sourceRepoSelect2 = Select2Box($sourceRepo, {
459 var sourceRepoSelect2 = Select2Box($sourceRepo, {
455 query: function(query) {}
460 query: function(query) {}
456 });
461 });
457
462
458 var targetRepoSelect2 = Select2Box($targetRepo, {
463 var targetRepoSelect2 = Select2Box($targetRepo, {
459 cachedDataSource: {},
464 cachedDataSource: {},
460 query: $.debounce(250, function(query) {
465 query: $.debounce(250, function(query) {
461 queryTargetRepo(this, query);
466 queryTargetRepo(this, query);
462 }),
467 }),
463 formatResult: formatResult
468 formatResult: formatResult
464 });
469 });
465
470
466 sourceRefSelect2.initRef();
471 sourceRefSelect2.initRef();
467
472
468 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
473 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
469
474
470 targetRepoSelect2.initRepo(defaultTargetRepo, false);
475 targetRepoSelect2.initRepo(defaultTargetRepo, false);
471
476
472 $sourceRef.on('change', function(e){
477 $sourceRef.on('change', function(e){
473 loadRepoRefDiffPreview();
478 loadRepoRefDiffPreview();
474 reviewersController.loadDefaultReviewers(
479 reviewersController.loadDefaultReviewers(
475 sourceRepo(), sourceRef(), targetRepo(), targetRef());
480 sourceRepo(), sourceRef(), targetRepo(), targetRef());
476 });
481 });
477
482
478 $targetRef.on('change', function(e){
483 $targetRef.on('change', function(e){
479 loadRepoRefDiffPreview();
484 loadRepoRefDiffPreview();
480 reviewersController.loadDefaultReviewers(
485 reviewersController.loadDefaultReviewers(
481 sourceRepo(), sourceRef(), targetRepo(), targetRef());
486 sourceRepo(), sourceRef(), targetRepo(), targetRef());
482 });
487 });
483
488
484 $targetRepo.on('change', function(e){
489 $targetRepo.on('change', function(e){
485 var repoName = $(this).val();
490 var repoName = $(this).val();
486 calculateContainerWidth();
491 calculateContainerWidth();
487 $targetRef.select2('destroy');
492 $targetRef.select2('destroy');
488 $('#target_ref_loading').show();
493 $('#target_ref_loading').show();
489
494
490 $.ajax({
495 $.ajax({
491 url: pyroutes.url('pullrequest_repo_refs',
496 url: pyroutes.url('pullrequest_repo_refs',
492 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
497 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
493 data: {},
498 data: {},
494 dataType: 'json',
499 dataType: 'json',
495 type: 'GET',
500 type: 'GET',
496 success: function(data) {
501 success: function(data) {
497 $('#target_ref_loading').hide();
502 $('#target_ref_loading').hide();
498 targetRepoChanged(data);
503 targetRepoChanged(data);
499 loadRepoRefDiffPreview();
504 loadRepoRefDiffPreview();
500 },
505 },
501 error: function(data, textStatus, errorThrown) {
506 error: function(data, textStatus, errorThrown) {
502 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
507 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
503 }
508 }
504 })
509 })
505
510
506 });
511 });
507
512
508 prButtonLock(true, "${_('Please select source and target')}", 'all');
513 prButtonLock(true, "${_('Please select source and target')}", 'all');
509
514
510 // auto-load on init, the target refs select2
515 // auto-load on init, the target refs select2
511 calculateContainerWidth();
516 calculateContainerWidth();
512 targetRepoChanged(defaultTargetRepoData);
517 targetRepoChanged(defaultTargetRepoData);
513
518
514 $('#pullrequest_title').on('keyup', function(e){
519 $('#pullrequest_title').on('keyup', function(e){
515 $(this).removeClass('autogenerated-title');
520 $(this).removeClass('autogenerated-title');
516 });
521 });
517
522
518 % if c.default_source_ref:
523 % if c.default_source_ref:
519 // in case we have a pre-selected value, use it now
524 // in case we have a pre-selected value, use it now
520 $sourceRef.select2('val', '${c.default_source_ref}');
525 $sourceRef.select2('val', '${c.default_source_ref}');
526 // diff preview load
521 loadRepoRefDiffPreview();
527 loadRepoRefDiffPreview();
528 // default reviewers
522 reviewersController.loadDefaultReviewers(
529 reviewersController.loadDefaultReviewers(
523 sourceRepo(), sourceRef(), targetRepo(), targetRef());
530 sourceRepo(), sourceRef(), targetRepo(), targetRef());
524 % endif
531 % endif
525
532
526 ReviewerAutoComplete('#user');
533 ReviewerAutoComplete('#user');
527 });
534 });
528 </script>
535 </script>
529
536
530 </%def>
537 </%def>
General Comments 0
You need to be logged in to leave comments. Login now