##// END OF EJS Templates
pull-requests: make auto generated title for pull requests show also source Ref type .eg. branch feature1, instead of just name of the branch.
marcink -
r4433:fa362000 default
parent child Browse files
Show More
@@ -1,657 +1,659 b''
1 // # Copyright (C) 2010-2020 RhodeCode GmbH
1 // # Copyright (C) 2010-2020 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 $('#pr_submit').attr('disabled', 'disabled');
44 $('#pr_submit').attr('disabled', 'disabled');
45 }
45 }
46 else if (checksMeet) {
46 else if (checksMeet) {
47 $('#pr_submit').removeAttr('disabled');
47 $('#pr_submit').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(sourceRefType, 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['message'];
78 var rawMessage = value['message'];
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 var rawMessage = elements[0]['message'];
83 var rawMessage = elements[0]['message'];
84 title = rawMessage.split('\n')[0];
84 title = rawMessage.split('\n')[0];
85 }
85 }
86 else {
86 else {
87 // use reference name
87 // use reference name
88 title = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter();
88 var normalizedRef = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter()
89 var refType = sourceRefType;
90 title = 'Changes from {0}: {1}'.format(refType, normalizedRef);
89 }
91 }
90
92
91 return [title, desc]
93 return [title, desc]
92 };
94 };
93
95
94
96
95 ReviewersController = function () {
97 ReviewersController = function () {
96 var self = this;
98 var self = this;
97 this.$reviewRulesContainer = $('#review_rules');
99 this.$reviewRulesContainer = $('#review_rules');
98 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
100 this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules');
99 this.forbidReviewUsers = undefined;
101 this.forbidReviewUsers = undefined;
100 this.$reviewMembers = $('#review_members');
102 this.$reviewMembers = $('#review_members');
101 this.currentRequest = null;
103 this.currentRequest = null;
102 this.diffData = null;
104 this.diffData = null;
103 //dummy handler, we might register our own later
105 //dummy handler, we might register our own later
104 this.diffDataHandler = function(data){};
106 this.diffDataHandler = function(data){};
105
107
106 this.defaultForbidReviewUsers = function () {
108 this.defaultForbidReviewUsers = function () {
107 return [
109 return [
108 {
110 {
109 'username': 'default',
111 'username': 'default',
110 'user_id': templateContext.default_user.user_id
112 'user_id': templateContext.default_user.user_id
111 }
113 }
112 ];
114 ];
113 };
115 };
114
116
115 this.hideReviewRules = function () {
117 this.hideReviewRules = function () {
116 self.$reviewRulesContainer.hide();
118 self.$reviewRulesContainer.hide();
117 };
119 };
118
120
119 this.showReviewRules = function () {
121 this.showReviewRules = function () {
120 self.$reviewRulesContainer.show();
122 self.$reviewRulesContainer.show();
121 };
123 };
122
124
123 this.addRule = function (ruleText) {
125 this.addRule = function (ruleText) {
124 self.showReviewRules();
126 self.showReviewRules();
125 return '<div>- {0}</div>'.format(ruleText)
127 return '<div>- {0}</div>'.format(ruleText)
126 };
128 };
127
129
128 this.loadReviewRules = function (data) {
130 this.loadReviewRules = function (data) {
129 self.diffData = data;
131 self.diffData = data;
130
132
131 // reset forbidden Users
133 // reset forbidden Users
132 this.forbidReviewUsers = self.defaultForbidReviewUsers();
134 this.forbidReviewUsers = self.defaultForbidReviewUsers();
133
135
134 // reset state of review rules
136 // reset state of review rules
135 self.$rulesList.html('');
137 self.$rulesList.html('');
136
138
137 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
139 if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) {
138 // default rule, case for older repo that don't have any rules stored
140 // default rule, case for older repo that don't have any rules stored
139 self.$rulesList.append(
141 self.$rulesList.append(
140 self.addRule(
142 self.addRule(
141 _gettext('All reviewers must vote.'))
143 _gettext('All reviewers must vote.'))
142 );
144 );
143 return self.forbidReviewUsers
145 return self.forbidReviewUsers
144 }
146 }
145
147
146 if (data.rules.voting !== undefined) {
148 if (data.rules.voting !== undefined) {
147 if (data.rules.voting < 0) {
149 if (data.rules.voting < 0) {
148 self.$rulesList.append(
150 self.$rulesList.append(
149 self.addRule(
151 self.addRule(
150 _gettext('All individual reviewers must vote.'))
152 _gettext('All individual reviewers must vote.'))
151 )
153 )
152 } else if (data.rules.voting === 1) {
154 } else if (data.rules.voting === 1) {
153 self.$rulesList.append(
155 self.$rulesList.append(
154 self.addRule(
156 self.addRule(
155 _gettext('At least {0} reviewer must vote.').format(data.rules.voting))
157 _gettext('At least {0} reviewer must vote.').format(data.rules.voting))
156 )
158 )
157
159
158 } else {
160 } else {
159 self.$rulesList.append(
161 self.$rulesList.append(
160 self.addRule(
162 self.addRule(
161 _gettext('At least {0} reviewers must vote.').format(data.rules.voting))
163 _gettext('At least {0} reviewers must vote.').format(data.rules.voting))
162 )
164 )
163 }
165 }
164 }
166 }
165
167
166 if (data.rules.voting_groups !== undefined) {
168 if (data.rules.voting_groups !== undefined) {
167 $.each(data.rules.voting_groups, function (index, rule_data) {
169 $.each(data.rules.voting_groups, function (index, rule_data) {
168 self.$rulesList.append(
170 self.$rulesList.append(
169 self.addRule(rule_data.text)
171 self.addRule(rule_data.text)
170 )
172 )
171 });
173 });
172 }
174 }
173
175
174 if (data.rules.use_code_authors_for_review) {
176 if (data.rules.use_code_authors_for_review) {
175 self.$rulesList.append(
177 self.$rulesList.append(
176 self.addRule(
178 self.addRule(
177 _gettext('Reviewers picked from source code changes.'))
179 _gettext('Reviewers picked from source code changes.'))
178 )
180 )
179 }
181 }
180 if (data.rules.forbid_adding_reviewers) {
182 if (data.rules.forbid_adding_reviewers) {
181 $('#add_reviewer_input').remove();
183 $('#add_reviewer_input').remove();
182 self.$rulesList.append(
184 self.$rulesList.append(
183 self.addRule(
185 self.addRule(
184 _gettext('Adding new reviewers is forbidden.'))
186 _gettext('Adding new reviewers is forbidden.'))
185 )
187 )
186 }
188 }
187 if (data.rules.forbid_author_to_review) {
189 if (data.rules.forbid_author_to_review) {
188 self.forbidReviewUsers.push(data.rules_data.pr_author);
190 self.forbidReviewUsers.push(data.rules_data.pr_author);
189 self.$rulesList.append(
191 self.$rulesList.append(
190 self.addRule(
192 self.addRule(
191 _gettext('Author is not allowed to be a reviewer.'))
193 _gettext('Author is not allowed to be a reviewer.'))
192 )
194 )
193 }
195 }
194 if (data.rules.forbid_commit_author_to_review) {
196 if (data.rules.forbid_commit_author_to_review) {
195
197
196 if (data.rules_data.forbidden_users) {
198 if (data.rules_data.forbidden_users) {
197 $.each(data.rules_data.forbidden_users, function (index, member_data) {
199 $.each(data.rules_data.forbidden_users, function (index, member_data) {
198 self.forbidReviewUsers.push(member_data)
200 self.forbidReviewUsers.push(member_data)
199 });
201 });
200
202
201 }
203 }
202
204
203 self.$rulesList.append(
205 self.$rulesList.append(
204 self.addRule(
206 self.addRule(
205 _gettext('Commit Authors are not allowed to be a reviewer.'))
207 _gettext('Commit Authors are not allowed to be a reviewer.'))
206 )
208 )
207 }
209 }
208
210
209 return self.forbidReviewUsers
211 return self.forbidReviewUsers
210 };
212 };
211
213
212 this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) {
214 this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) {
213
215
214 if (self.currentRequest) {
216 if (self.currentRequest) {
215 // make sure we cleanup old running requests before triggering this again
217 // make sure we cleanup old running requests before triggering this again
216 self.currentRequest.abort();
218 self.currentRequest.abort();
217 }
219 }
218
220
219 $('.calculate-reviewers').show();
221 $('.calculate-reviewers').show();
220 // reset reviewer members
222 // reset reviewer members
221 self.$reviewMembers.empty();
223 self.$reviewMembers.empty();
222
224
223 prButtonLock(true, null, 'reviewers');
225 prButtonLock(true, null, 'reviewers');
224 $('#user').hide(); // hide user autocomplete before load
226 $('#user').hide(); // hide user autocomplete before load
225
227
226 // lock PR button, so we cannot send PR before it's calculated
228 // lock PR button, so we cannot send PR before it's calculated
227 prButtonLock(true, _gettext('Loading diff ...'), 'compare');
229 prButtonLock(true, _gettext('Loading diff ...'), 'compare');
228
230
229 if (sourceRef.length !== 3 || targetRef.length !== 3) {
231 if (sourceRef.length !== 3 || targetRef.length !== 3) {
230 // don't load defaults in case we're missing some refs...
232 // don't load defaults in case we're missing some refs...
231 $('.calculate-reviewers').hide();
233 $('.calculate-reviewers').hide();
232 return
234 return
233 }
235 }
234
236
235 var url = pyroutes.url('repo_default_reviewers_data',
237 var url = pyroutes.url('repo_default_reviewers_data',
236 {
238 {
237 'repo_name': templateContext.repo_name,
239 'repo_name': templateContext.repo_name,
238 'source_repo': sourceRepo,
240 'source_repo': sourceRepo,
239 'source_ref': sourceRef[2],
241 'source_ref': sourceRef[2],
240 'target_repo': targetRepo,
242 'target_repo': targetRepo,
241 'target_ref': targetRef[2]
243 'target_ref': targetRef[2]
242 });
244 });
243
245
244 self.currentRequest = $.ajax({
246 self.currentRequest = $.ajax({
245 url: url,
247 url: url,
246 headers: {'X-PARTIAL-XHR': true},
248 headers: {'X-PARTIAL-XHR': true},
247 type: 'GET',
249 type: 'GET',
248 success: function (data) {
250 success: function (data) {
249
251
250 self.currentRequest = null;
252 self.currentRequest = null;
251
253
252 // review rules
254 // review rules
253 self.loadReviewRules(data);
255 self.loadReviewRules(data);
254 self.handleDiffData(data["diff_info"]);
256 self.handleDiffData(data["diff_info"]);
255
257
256 for (var i = 0; i < data.reviewers.length; i++) {
258 for (var i = 0; i < data.reviewers.length; i++) {
257 var reviewer = data.reviewers[i];
259 var reviewer = data.reviewers[i];
258 self.addReviewMember(reviewer, reviewer.reasons, reviewer.mandatory);
260 self.addReviewMember(reviewer, reviewer.reasons, reviewer.mandatory);
259 }
261 }
260 $('.calculate-reviewers').hide();
262 $('.calculate-reviewers').hide();
261 prButtonLock(false, null, 'reviewers');
263 prButtonLock(false, null, 'reviewers');
262 $('#user').show(); // show user autocomplete after load
264 $('#user').show(); // show user autocomplete after load
263
265
264 var commitElements = data["diff_info"]['commits'];
266 var commitElements = data["diff_info"]['commits'];
265 if (commitElements.length === 0) {
267 if (commitElements.length === 0) {
266 prButtonLock(true, _gettext('no commits'), 'all');
268 prButtonLock(true, _gettext('no commits'), 'all');
267
269
268 } else {
270 } else {
269 // un-lock PR button, so we cannot send PR before it's calculated
271 // un-lock PR button, so we cannot send PR before it's calculated
270 prButtonLock(false, null, 'compare');
272 prButtonLock(false, null, 'compare');
271 }
273 }
272
274
273 },
275 },
274 error: function (jqXHR, textStatus, errorThrown) {
276 error: function (jqXHR, textStatus, errorThrown) {
275 var prefix = "Loading diff and reviewers failed\n"
277 var prefix = "Loading diff and reviewers failed\n"
276 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
278 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
277 ajaxErrorSwal(message);
279 ajaxErrorSwal(message);
278 }
280 }
279 });
281 });
280
282
281 };
283 };
282
284
283 // check those, refactor
285 // check those, refactor
284 this.removeReviewMember = function (reviewer_id, mark_delete) {
286 this.removeReviewMember = function (reviewer_id, mark_delete) {
285 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
287 var reviewer = $('#reviewer_{0}'.format(reviewer_id));
286
288
287 if (typeof (mark_delete) === undefined) {
289 if (typeof (mark_delete) === undefined) {
288 mark_delete = false;
290 mark_delete = false;
289 }
291 }
290
292
291 if (mark_delete === true) {
293 if (mark_delete === true) {
292 if (reviewer) {
294 if (reviewer) {
293 // now delete the input
295 // now delete the input
294 $('#reviewer_{0} input'.format(reviewer_id)).remove();
296 $('#reviewer_{0} input'.format(reviewer_id)).remove();
295 // mark as to-delete
297 // mark as to-delete
296 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
298 var obj = $('#reviewer_{0}_name'.format(reviewer_id));
297 obj.addClass('to-delete');
299 obj.addClass('to-delete');
298 obj.css({"text-decoration": "line-through", "opacity": 0.5});
300 obj.css({"text-decoration": "line-through", "opacity": 0.5});
299 }
301 }
300 } else {
302 } else {
301 $('#reviewer_{0}'.format(reviewer_id)).remove();
303 $('#reviewer_{0}'.format(reviewer_id)).remove();
302 }
304 }
303 };
305 };
304
306
305 this.reviewMemberEntry = function () {
307 this.reviewMemberEntry = function () {
306
308
307 };
309 };
308
310
309 this.addReviewMember = function (reviewer_obj, reasons, mandatory) {
311 this.addReviewMember = function (reviewer_obj, reasons, mandatory) {
310 var members = self.$reviewMembers.get(0);
312 var members = self.$reviewMembers.get(0);
311 var id = reviewer_obj.user_id;
313 var id = reviewer_obj.user_id;
312 var username = reviewer_obj.username;
314 var username = reviewer_obj.username;
313
315
314 var reasons = reasons || [];
316 var reasons = reasons || [];
315 var mandatory = mandatory || false;
317 var mandatory = mandatory || false;
316
318
317 // register IDS to check if we don't have this ID already in
319 // register IDS to check if we don't have this ID already in
318 var currentIds = [];
320 var currentIds = [];
319 var _els = self.$reviewMembers.find('li').toArray();
321 var _els = self.$reviewMembers.find('li').toArray();
320 for (el in _els) {
322 for (el in _els) {
321 currentIds.push(_els[el].id)
323 currentIds.push(_els[el].id)
322 }
324 }
323
325
324 var userAllowedReview = function (userId) {
326 var userAllowedReview = function (userId) {
325 var allowed = true;
327 var allowed = true;
326 $.each(self.forbidReviewUsers, function (index, member_data) {
328 $.each(self.forbidReviewUsers, function (index, member_data) {
327 if (parseInt(userId) === member_data['user_id']) {
329 if (parseInt(userId) === member_data['user_id']) {
328 allowed = false;
330 allowed = false;
329 return false // breaks the loop
331 return false // breaks the loop
330 }
332 }
331 });
333 });
332 return allowed
334 return allowed
333 };
335 };
334
336
335 var userAllowed = userAllowedReview(id);
337 var userAllowed = userAllowedReview(id);
336 if (!userAllowed) {
338 if (!userAllowed) {
337 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
339 alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
338 } else {
340 } else {
339 // only add if it's not there
341 // only add if it's not there
340 var alreadyReviewer = currentIds.indexOf('reviewer_' + id) != -1;
342 var alreadyReviewer = currentIds.indexOf('reviewer_' + id) != -1;
341
343
342 if (alreadyReviewer) {
344 if (alreadyReviewer) {
343 alert(_gettext('User `{0}` already in reviewers').format(username));
345 alert(_gettext('User `{0}` already in reviewers').format(username));
344 } else {
346 } else {
345 members.innerHTML += renderTemplate('reviewMemberEntry', {
347 members.innerHTML += renderTemplate('reviewMemberEntry', {
346 'member': reviewer_obj,
348 'member': reviewer_obj,
347 'mandatory': mandatory,
349 'mandatory': mandatory,
348 'allowed_to_update': true,
350 'allowed_to_update': true,
349 'review_status': 'not_reviewed',
351 'review_status': 'not_reviewed',
350 'review_status_label': _gettext('Not Reviewed'),
352 'review_status_label': _gettext('Not Reviewed'),
351 'reasons': reasons,
353 'reasons': reasons,
352 'create': true
354 'create': true
353 });
355 });
354 tooltipActivate();
356 tooltipActivate();
355 }
357 }
356 }
358 }
357
359
358 };
360 };
359
361
360 this.updateReviewers = function (repo_name, pull_request_id) {
362 this.updateReviewers = function (repo_name, pull_request_id) {
361 var postData = $('#reviewers input').serialize();
363 var postData = $('#reviewers input').serialize();
362 _updatePullRequest(repo_name, pull_request_id, postData);
364 _updatePullRequest(repo_name, pull_request_id, postData);
363 };
365 };
364
366
365 this.handleDiffData = function (data) {
367 this.handleDiffData = function (data) {
366 self.diffDataHandler(data)
368 self.diffDataHandler(data)
367 }
369 }
368 };
370 };
369
371
370
372
371 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
373 var _updatePullRequest = function(repo_name, pull_request_id, postData) {
372 var url = pyroutes.url(
374 var url = pyroutes.url(
373 'pullrequest_update',
375 'pullrequest_update',
374 {"repo_name": repo_name, "pull_request_id": pull_request_id});
376 {"repo_name": repo_name, "pull_request_id": pull_request_id});
375 if (typeof postData === 'string' ) {
377 if (typeof postData === 'string' ) {
376 postData += '&csrf_token=' + CSRF_TOKEN;
378 postData += '&csrf_token=' + CSRF_TOKEN;
377 } else {
379 } else {
378 postData.csrf_token = CSRF_TOKEN;
380 postData.csrf_token = CSRF_TOKEN;
379 }
381 }
380
382
381 var success = function(o) {
383 var success = function(o) {
382 var redirectUrl = o['redirect_url'];
384 var redirectUrl = o['redirect_url'];
383 if (redirectUrl !== undefined && redirectUrl !== null && redirectUrl !== '') {
385 if (redirectUrl !== undefined && redirectUrl !== null && redirectUrl !== '') {
384 window.location = redirectUrl;
386 window.location = redirectUrl;
385 } else {
387 } else {
386 window.location.reload();
388 window.location.reload();
387 }
389 }
388 };
390 };
389
391
390 ajaxPOST(url, postData, success);
392 ajaxPOST(url, postData, success);
391 };
393 };
392
394
393 /**
395 /**
394 * PULL REQUEST update commits
396 * PULL REQUEST update commits
395 */
397 */
396 var updateCommits = function(repo_name, pull_request_id, force) {
398 var updateCommits = function(repo_name, pull_request_id, force) {
397 var postData = {
399 var postData = {
398 'update_commits': true
400 'update_commits': true
399 };
401 };
400 if (force !== undefined && force === true) {
402 if (force !== undefined && force === true) {
401 postData['force_refresh'] = true
403 postData['force_refresh'] = true
402 }
404 }
403 _updatePullRequest(repo_name, pull_request_id, postData);
405 _updatePullRequest(repo_name, pull_request_id, postData);
404 };
406 };
405
407
406
408
407 /**
409 /**
408 * PULL REQUEST edit info
410 * PULL REQUEST edit info
409 */
411 */
410 var editPullRequest = function(repo_name, pull_request_id, title, description, renderer) {
412 var editPullRequest = function(repo_name, pull_request_id, title, description, renderer) {
411 var url = pyroutes.url(
413 var url = pyroutes.url(
412 'pullrequest_update',
414 'pullrequest_update',
413 {"repo_name": repo_name, "pull_request_id": pull_request_id});
415 {"repo_name": repo_name, "pull_request_id": pull_request_id});
414
416
415 var postData = {
417 var postData = {
416 'title': title,
418 'title': title,
417 'description': description,
419 'description': description,
418 'description_renderer': renderer,
420 'description_renderer': renderer,
419 'edit_pull_request': true,
421 'edit_pull_request': true,
420 'csrf_token': CSRF_TOKEN
422 'csrf_token': CSRF_TOKEN
421 };
423 };
422 var success = function(o) {
424 var success = function(o) {
423 window.location.reload();
425 window.location.reload();
424 };
426 };
425 ajaxPOST(url, postData, success);
427 ajaxPOST(url, postData, success);
426 };
428 };
427
429
428
430
429 /**
431 /**
430 * Reviewer autocomplete
432 * Reviewer autocomplete
431 */
433 */
432 var ReviewerAutoComplete = function(inputId) {
434 var ReviewerAutoComplete = function(inputId) {
433 $(inputId).autocomplete({
435 $(inputId).autocomplete({
434 serviceUrl: pyroutes.url('user_autocomplete_data'),
436 serviceUrl: pyroutes.url('user_autocomplete_data'),
435 minChars:2,
437 minChars:2,
436 maxHeight:400,
438 maxHeight:400,
437 deferRequestBy: 300, //miliseconds
439 deferRequestBy: 300, //miliseconds
438 showNoSuggestionNotice: true,
440 showNoSuggestionNotice: true,
439 tabDisabled: true,
441 tabDisabled: true,
440 autoSelectFirst: true,
442 autoSelectFirst: true,
441 params: { user_id: templateContext.rhodecode_user.user_id, user_groups:true, user_groups_expand:true, skip_default_user:true },
443 params: { user_id: templateContext.rhodecode_user.user_id, user_groups:true, user_groups_expand:true, skip_default_user:true },
442 formatResult: autocompleteFormatResult,
444 formatResult: autocompleteFormatResult,
443 lookupFilter: autocompleteFilterResult,
445 lookupFilter: autocompleteFilterResult,
444 onSelect: function(element, data) {
446 onSelect: function(element, data) {
445 var mandatory = false;
447 var mandatory = false;
446 var reasons = [_gettext('added manually by "{0}"').format(templateContext.rhodecode_user.username)];
448 var reasons = [_gettext('added manually by "{0}"').format(templateContext.rhodecode_user.username)];
447
449
448 // add whole user groups
450 // add whole user groups
449 if (data.value_type == 'user_group') {
451 if (data.value_type == 'user_group') {
450 reasons.push(_gettext('member of "{0}"').format(data.value_display));
452 reasons.push(_gettext('member of "{0}"').format(data.value_display));
451
453
452 $.each(data.members, function(index, member_data) {
454 $.each(data.members, function(index, member_data) {
453 var reviewer = member_data;
455 var reviewer = member_data;
454 reviewer['user_id'] = member_data['id'];
456 reviewer['user_id'] = member_data['id'];
455 reviewer['gravatar_link'] = member_data['icon_link'];
457 reviewer['gravatar_link'] = member_data['icon_link'];
456 reviewer['user_link'] = member_data['profile_link'];
458 reviewer['user_link'] = member_data['profile_link'];
457 reviewer['rules'] = [];
459 reviewer['rules'] = [];
458 reviewersController.addReviewMember(reviewer, reasons, mandatory);
460 reviewersController.addReviewMember(reviewer, reasons, mandatory);
459 })
461 })
460 }
462 }
461 // add single user
463 // add single user
462 else {
464 else {
463 var reviewer = data;
465 var reviewer = data;
464 reviewer['user_id'] = data['id'];
466 reviewer['user_id'] = data['id'];
465 reviewer['gravatar_link'] = data['icon_link'];
467 reviewer['gravatar_link'] = data['icon_link'];
466 reviewer['user_link'] = data['profile_link'];
468 reviewer['user_link'] = data['profile_link'];
467 reviewer['rules'] = [];
469 reviewer['rules'] = [];
468 reviewersController.addReviewMember(reviewer, reasons, mandatory);
470 reviewersController.addReviewMember(reviewer, reasons, mandatory);
469 }
471 }
470
472
471 $(inputId).val('');
473 $(inputId).val('');
472 }
474 }
473 });
475 });
474 };
476 };
475
477
476
478
477 VersionController = function () {
479 VersionController = function () {
478 var self = this;
480 var self = this;
479 this.$verSource = $('input[name=ver_source]');
481 this.$verSource = $('input[name=ver_source]');
480 this.$verTarget = $('input[name=ver_target]');
482 this.$verTarget = $('input[name=ver_target]');
481 this.$showVersionDiff = $('#show-version-diff');
483 this.$showVersionDiff = $('#show-version-diff');
482
484
483 this.adjustRadioSelectors = function (curNode) {
485 this.adjustRadioSelectors = function (curNode) {
484 var getVal = function (item) {
486 var getVal = function (item) {
485 if (item == 'latest') {
487 if (item == 'latest') {
486 return Number.MAX_SAFE_INTEGER
488 return Number.MAX_SAFE_INTEGER
487 }
489 }
488 else {
490 else {
489 return parseInt(item)
491 return parseInt(item)
490 }
492 }
491 };
493 };
492
494
493 var curVal = getVal($(curNode).val());
495 var curVal = getVal($(curNode).val());
494 var cleared = false;
496 var cleared = false;
495
497
496 $.each(self.$verSource, function (index, value) {
498 $.each(self.$verSource, function (index, value) {
497 var elVal = getVal($(value).val());
499 var elVal = getVal($(value).val());
498
500
499 if (elVal > curVal) {
501 if (elVal > curVal) {
500 if ($(value).is(':checked')) {
502 if ($(value).is(':checked')) {
501 cleared = true;
503 cleared = true;
502 }
504 }
503 $(value).attr('disabled', 'disabled');
505 $(value).attr('disabled', 'disabled');
504 $(value).removeAttr('checked');
506 $(value).removeAttr('checked');
505 $(value).css({'opacity': 0.1});
507 $(value).css({'opacity': 0.1});
506 }
508 }
507 else {
509 else {
508 $(value).css({'opacity': 1});
510 $(value).css({'opacity': 1});
509 $(value).removeAttr('disabled');
511 $(value).removeAttr('disabled');
510 }
512 }
511 });
513 });
512
514
513 if (cleared) {
515 if (cleared) {
514 // if we unchecked an active, set the next one to same loc.
516 // if we unchecked an active, set the next one to same loc.
515 $(this.$verSource).filter('[value={0}]'.format(
517 $(this.$verSource).filter('[value={0}]'.format(
516 curVal)).attr('checked', 'checked');
518 curVal)).attr('checked', 'checked');
517 }
519 }
518
520
519 self.setLockAction(false,
521 self.setLockAction(false,
520 $(curNode).data('verPos'),
522 $(curNode).data('verPos'),
521 $(this.$verSource).filter(':checked').data('verPos')
523 $(this.$verSource).filter(':checked').data('verPos')
522 );
524 );
523 };
525 };
524
526
525
527
526 this.attachVersionListener = function () {
528 this.attachVersionListener = function () {
527 self.$verTarget.change(function (e) {
529 self.$verTarget.change(function (e) {
528 self.adjustRadioSelectors(this)
530 self.adjustRadioSelectors(this)
529 });
531 });
530 self.$verSource.change(function (e) {
532 self.$verSource.change(function (e) {
531 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
533 self.adjustRadioSelectors(self.$verTarget.filter(':checked'))
532 });
534 });
533 };
535 };
534
536
535 this.init = function () {
537 this.init = function () {
536
538
537 var curNode = self.$verTarget.filter(':checked');
539 var curNode = self.$verTarget.filter(':checked');
538 self.adjustRadioSelectors(curNode);
540 self.adjustRadioSelectors(curNode);
539 self.setLockAction(true);
541 self.setLockAction(true);
540 self.attachVersionListener();
542 self.attachVersionListener();
541
543
542 };
544 };
543
545
544 this.setLockAction = function (state, selectedVersion, otherVersion) {
546 this.setLockAction = function (state, selectedVersion, otherVersion) {
545 var $showVersionDiff = this.$showVersionDiff;
547 var $showVersionDiff = this.$showVersionDiff;
546
548
547 if (state) {
549 if (state) {
548 $showVersionDiff.attr('disabled', 'disabled');
550 $showVersionDiff.attr('disabled', 'disabled');
549 $showVersionDiff.addClass('disabled');
551 $showVersionDiff.addClass('disabled');
550 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
552 $showVersionDiff.html($showVersionDiff.data('labelTextLocked'));
551 }
553 }
552 else {
554 else {
553 $showVersionDiff.removeAttr('disabled');
555 $showVersionDiff.removeAttr('disabled');
554 $showVersionDiff.removeClass('disabled');
556 $showVersionDiff.removeClass('disabled');
555
557
556 if (selectedVersion == otherVersion) {
558 if (selectedVersion == otherVersion) {
557 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
559 $showVersionDiff.html($showVersionDiff.data('labelTextShow'));
558 } else {
560 } else {
559 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
561 $showVersionDiff.html($showVersionDiff.data('labelTextDiff'));
560 }
562 }
561 }
563 }
562
564
563 };
565 };
564
566
565 this.showVersionDiff = function () {
567 this.showVersionDiff = function () {
566 var target = self.$verTarget.filter(':checked');
568 var target = self.$verTarget.filter(':checked');
567 var source = self.$verSource.filter(':checked');
569 var source = self.$verSource.filter(':checked');
568
570
569 if (target.val() && source.val()) {
571 if (target.val() && source.val()) {
570 var params = {
572 var params = {
571 'pull_request_id': templateContext.pull_request_data.pull_request_id,
573 'pull_request_id': templateContext.pull_request_data.pull_request_id,
572 'repo_name': templateContext.repo_name,
574 'repo_name': templateContext.repo_name,
573 'version': target.val(),
575 'version': target.val(),
574 'from_version': source.val()
576 'from_version': source.val()
575 };
577 };
576 window.location = pyroutes.url('pullrequest_show', params)
578 window.location = pyroutes.url('pullrequest_show', params)
577 }
579 }
578
580
579 return false;
581 return false;
580 };
582 };
581
583
582 this.toggleVersionView = function (elem) {
584 this.toggleVersionView = function (elem) {
583
585
584 if (this.$showVersionDiff.is(':visible')) {
586 if (this.$showVersionDiff.is(':visible')) {
585 $('.version-pr').hide();
587 $('.version-pr').hide();
586 this.$showVersionDiff.hide();
588 this.$showVersionDiff.hide();
587 $(elem).html($(elem).data('toggleOn'))
589 $(elem).html($(elem).data('toggleOn'))
588 } else {
590 } else {
589 $('.version-pr').show();
591 $('.version-pr').show();
590 this.$showVersionDiff.show();
592 this.$showVersionDiff.show();
591 $(elem).html($(elem).data('toggleOff'))
593 $(elem).html($(elem).data('toggleOff'))
592 }
594 }
593
595
594 return false
596 return false
595 };
597 };
596
598
597 this.toggleElement = function (elem, target) {
599 this.toggleElement = function (elem, target) {
598 var $elem = $(elem);
600 var $elem = $(elem);
599 var $target = $(target);
601 var $target = $(target);
600
602
601 if ($target.is(':visible')) {
603 if ($target.is(':visible')) {
602 $target.hide();
604 $target.hide();
603 $elem.html($elem.data('toggleOn'))
605 $elem.html($elem.data('toggleOn'))
604 } else {
606 } else {
605 $target.show();
607 $target.show();
606 $elem.html($elem.data('toggleOff'))
608 $elem.html($elem.data('toggleOff'))
607 }
609 }
608
610
609 return false
611 return false
610 }
612 }
611
613
612 };
614 };
613
615
614
616
615 UpdatePrController = function () {
617 UpdatePrController = function () {
616 var self = this;
618 var self = this;
617 this.$updateCommits = $('#update_commits');
619 this.$updateCommits = $('#update_commits');
618 this.$updateCommitsSwitcher = $('#update_commits_switcher');
620 this.$updateCommitsSwitcher = $('#update_commits_switcher');
619
621
620 this.lockUpdateButton = function (label) {
622 this.lockUpdateButton = function (label) {
621 self.$updateCommits.attr('disabled', 'disabled');
623 self.$updateCommits.attr('disabled', 'disabled');
622 self.$updateCommitsSwitcher.attr('disabled', 'disabled');
624 self.$updateCommitsSwitcher.attr('disabled', 'disabled');
623
625
624 self.$updateCommits.addClass('disabled');
626 self.$updateCommits.addClass('disabled');
625 self.$updateCommitsSwitcher.addClass('disabled');
627 self.$updateCommitsSwitcher.addClass('disabled');
626
628
627 self.$updateCommits.removeClass('btn-primary');
629 self.$updateCommits.removeClass('btn-primary');
628 self.$updateCommitsSwitcher.removeClass('btn-primary');
630 self.$updateCommitsSwitcher.removeClass('btn-primary');
629
631
630 self.$updateCommits.text(_gettext(label));
632 self.$updateCommits.text(_gettext(label));
631 };
633 };
632
634
633 this.isUpdateLocked = function () {
635 this.isUpdateLocked = function () {
634 return self.$updateCommits.attr('disabled') !== undefined;
636 return self.$updateCommits.attr('disabled') !== undefined;
635 };
637 };
636
638
637 this.updateCommits = function (curNode) {
639 this.updateCommits = function (curNode) {
638 if (self.isUpdateLocked()) {
640 if (self.isUpdateLocked()) {
639 return
641 return
640 }
642 }
641 self.lockUpdateButton(_gettext('Updating...'));
643 self.lockUpdateButton(_gettext('Updating...'));
642 updateCommits(
644 updateCommits(
643 templateContext.repo_name,
645 templateContext.repo_name,
644 templateContext.pull_request_data.pull_request_id);
646 templateContext.pull_request_data.pull_request_id);
645 };
647 };
646
648
647 this.forceUpdateCommits = function () {
649 this.forceUpdateCommits = function () {
648 if (self.isUpdateLocked()) {
650 if (self.isUpdateLocked()) {
649 return
651 return
650 }
652 }
651 self.lockUpdateButton(_gettext('Force updating...'));
653 self.lockUpdateButton(_gettext('Force updating...'));
652 var force = true;
654 var force = true;
653 updateCommits(
655 updateCommits(
654 templateContext.repo_name,
656 templateContext.repo_name,
655 templateContext.pull_request_data.pull_request_id, force);
657 templateContext.pull_request_data.pull_request_id, force);
656 };
658 };
657 }; No newline at end of file
659 };
@@ -1,1208 +1,1208 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%!
3 <%!
4 from rhodecode.lib import html_filters
4 from rhodecode.lib import html_filters
5 %>
5 %>
6
6
7 <%inherit file="root.mako"/>
7 <%inherit file="root.mako"/>
8
8
9 <%include file="/ejs_templates/templates.html"/>
9 <%include file="/ejs_templates/templates.html"/>
10
10
11 <div class="outerwrapper">
11 <div class="outerwrapper">
12 <!-- HEADER -->
12 <!-- HEADER -->
13 <div class="header">
13 <div class="header">
14 <div id="header-inner" class="wrapper">
14 <div id="header-inner" class="wrapper">
15 <div id="logo">
15 <div id="logo">
16 <div class="logo-wrapper">
16 <div class="logo-wrapper">
17 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
17 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
18 </div>
18 </div>
19 % if c.rhodecode_name:
19 % if c.rhodecode_name:
20 <div class="branding">
20 <div class="branding">
21 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
21 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
22 </div>
22 </div>
23 % endif
23 % endif
24 </div>
24 </div>
25 <!-- MENU BAR NAV -->
25 <!-- MENU BAR NAV -->
26 ${self.menu_bar_nav()}
26 ${self.menu_bar_nav()}
27 <!-- END MENU BAR NAV -->
27 <!-- END MENU BAR NAV -->
28 </div>
28 </div>
29 </div>
29 </div>
30 ${self.menu_bar_subnav()}
30 ${self.menu_bar_subnav()}
31 <!-- END HEADER -->
31 <!-- END HEADER -->
32
32
33 <!-- CONTENT -->
33 <!-- CONTENT -->
34 <div id="content" class="wrapper">
34 <div id="content" class="wrapper">
35
35
36 <rhodecode-toast id="notifications"></rhodecode-toast>
36 <rhodecode-toast id="notifications"></rhodecode-toast>
37
37
38 <div class="main">
38 <div class="main">
39 ${next.main()}
39 ${next.main()}
40 </div>
40 </div>
41 </div>
41 </div>
42 <!-- END CONTENT -->
42 <!-- END CONTENT -->
43
43
44 </div>
44 </div>
45 <!-- FOOTER -->
45 <!-- FOOTER -->
46 <div id="footer">
46 <div id="footer">
47 <div id="footer-inner" class="title wrapper">
47 <div id="footer-inner" class="title wrapper">
48 <div>
48 <div>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50
50
51 <p class="footer-link-right">
51 <p class="footer-link-right">
52 <a class="grey-link-action" href="${h.route_path('home', _query={'showrcid': 1})}">
52 <a class="grey-link-action" href="${h.route_path('home', _query={'showrcid': 1})}">
53 RhodeCode
53 RhodeCode
54 % if c.visual.show_version:
54 % if c.visual.show_version:
55 ${c.rhodecode_version}
55 ${c.rhodecode_version}
56 % endif
56 % endif
57 ${c.rhodecode_edition}
57 ${c.rhodecode_edition}
58 </a> |
58 </a> |
59
59
60 % if c.visual.rhodecode_support_url:
60 % if c.visual.rhodecode_support_url:
61 <a class="grey-link-action" href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> |
61 <a class="grey-link-action" href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> |
62 <a class="grey-link-action" href="https://docs.rhodecode.com" target="_blank">${_('Documentation')}</a>
62 <a class="grey-link-action" href="https://docs.rhodecode.com" target="_blank">${_('Documentation')}</a>
63 % endif
63 % endif
64
64
65 </p>
65 </p>
66
66
67 <p class="server-instance" style="display:${sid}">
67 <p class="server-instance" style="display:${sid}">
68 ## display hidden instance ID if specially defined
68 ## display hidden instance ID if specially defined
69 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
69 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
70 % if c.rhodecode_instanceid:
70 % if c.rhodecode_instanceid:
71 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
71 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
72 % endif
72 % endif
73 </p>
73 </p>
74 </div>
74 </div>
75 </div>
75 </div>
76 </div>
76 </div>
77
77
78 <!-- END FOOTER -->
78 <!-- END FOOTER -->
79
79
80 ### MAKO DEFS ###
80 ### MAKO DEFS ###
81
81
82 <%def name="menu_bar_subnav()">
82 <%def name="menu_bar_subnav()">
83 </%def>
83 </%def>
84
84
85 <%def name="breadcrumbs(class_='breadcrumbs')">
85 <%def name="breadcrumbs(class_='breadcrumbs')">
86 <div class="${class_}">
86 <div class="${class_}">
87 ${self.breadcrumbs_links()}
87 ${self.breadcrumbs_links()}
88 </div>
88 </div>
89 </%def>
89 </%def>
90
90
91 <%def name="admin_menu(active=None)">
91 <%def name="admin_menu(active=None)">
92
92
93 <div id="context-bar">
93 <div id="context-bar">
94 <div class="wrapper">
94 <div class="wrapper">
95 <div class="title">
95 <div class="title">
96 <div class="title-content">
96 <div class="title-content">
97 <div class="title-main">
97 <div class="title-main">
98 % if c.is_super_admin:
98 % if c.is_super_admin:
99 ${_('Super-admin Panel')}
99 ${_('Super-admin Panel')}
100 % else:
100 % else:
101 ${_('Delegated Admin Panel')}
101 ${_('Delegated Admin Panel')}
102 % endif
102 % endif
103 </div>
103 </div>
104 </div>
104 </div>
105 </div>
105 </div>
106
106
107 <ul id="context-pages" class="navigation horizontal-list">
107 <ul id="context-pages" class="navigation horizontal-list">
108
108
109 ## super-admin case
109 ## super-admin case
110 % if c.is_super_admin:
110 % if c.is_super_admin:
111 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
111 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
112 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
112 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
113 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
113 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
114 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
114 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
115 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
115 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
116 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
116 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
117 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
117 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
118 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
118 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
119 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
119 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
120 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
120 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
121
121
122 ## delegated admin
122 ## delegated admin
123 % elif c.is_delegated_admin:
123 % elif c.is_delegated_admin:
124 <%
124 <%
125 repositories=c.auth_user.repositories_admin or c.can_create_repo
125 repositories=c.auth_user.repositories_admin or c.can_create_repo
126 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
126 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
127 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
127 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
128 %>
128 %>
129
129
130 %if repositories:
130 %if repositories:
131 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
131 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
132 %endif
132 %endif
133 %if repository_groups:
133 %if repository_groups:
134 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
134 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
135 %endif
135 %endif
136 %if user_groups:
136 %if user_groups:
137 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
137 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
138 %endif
138 %endif
139 % endif
139 % endif
140 </ul>
140 </ul>
141
141
142 </div>
142 </div>
143 <div class="clear"></div>
143 <div class="clear"></div>
144 </div>
144 </div>
145 </%def>
145 </%def>
146
146
147 <%def name="dt_info_panel(elements)">
147 <%def name="dt_info_panel(elements)">
148 <dl class="dl-horizontal">
148 <dl class="dl-horizontal">
149 %for dt, dd, title, show_items in elements:
149 %for dt, dd, title, show_items in elements:
150 <dt>${dt}:</dt>
150 <dt>${dt}:</dt>
151 <dd title="${h.tooltip(title)}">
151 <dd title="${h.tooltip(title)}">
152 %if callable(dd):
152 %if callable(dd):
153 ## allow lazy evaluation of elements
153 ## allow lazy evaluation of elements
154 ${dd()}
154 ${dd()}
155 %else:
155 %else:
156 ${dd}
156 ${dd}
157 %endif
157 %endif
158 %if show_items:
158 %if show_items:
159 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
159 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
160 %endif
160 %endif
161 </dd>
161 </dd>
162
162
163 %if show_items:
163 %if show_items:
164 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
164 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
165 %for item in show_items:
165 %for item in show_items:
166 <dt></dt>
166 <dt></dt>
167 <dd>${item}</dd>
167 <dd>${item}</dd>
168 %endfor
168 %endfor
169 </div>
169 </div>
170 %endif
170 %endif
171
171
172 %endfor
172 %endfor
173 </dl>
173 </dl>
174 </%def>
174 </%def>
175
175
176 <%def name="tr_info_entry(element)">
176 <%def name="tr_info_entry(element)">
177 <% key, val, title, show_items = element %>
177 <% key, val, title, show_items = element %>
178
178
179 <tr>
179 <tr>
180 <td style="vertical-align: top">${key}</td>
180 <td style="vertical-align: top">${key}</td>
181 <td title="${h.tooltip(title)}">
181 <td title="${h.tooltip(title)}">
182 %if callable(val):
182 %if callable(val):
183 ## allow lazy evaluation of elements
183 ## allow lazy evaluation of elements
184 ${val()}
184 ${val()}
185 %else:
185 %else:
186 ${val}
186 ${val}
187 %endif
187 %endif
188 %if show_items:
188 %if show_items:
189 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
189 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
190 % for item in show_items:
190 % for item in show_items:
191 <dt></dt>
191 <dt></dt>
192 <dd>${item}</dd>
192 <dd>${item}</dd>
193 % endfor
193 % endfor
194 </div>
194 </div>
195 %endif
195 %endif
196 </td>
196 </td>
197 <td style="vertical-align: top">
197 <td style="vertical-align: top">
198 %if show_items:
198 %if show_items:
199 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
199 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
200 %endif
200 %endif
201 </td>
201 </td>
202 </tr>
202 </tr>
203
203
204 </%def>
204 </%def>
205
205
206 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None, extra_class=None)">
206 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None, extra_class=None)">
207 <%
207 <%
208 if size > 16:
208 if size > 16:
209 gravatar_class = ['gravatar','gravatar-large']
209 gravatar_class = ['gravatar','gravatar-large']
210 else:
210 else:
211 gravatar_class = ['gravatar']
211 gravatar_class = ['gravatar']
212
212
213 data_hovercard_url = ''
213 data_hovercard_url = ''
214 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
214 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
215
215
216 if tooltip:
216 if tooltip:
217 gravatar_class += ['tooltip-hovercard']
217 gravatar_class += ['tooltip-hovercard']
218 if extra_class:
218 if extra_class:
219 gravatar_class += extra_class
219 gravatar_class += extra_class
220 if tooltip and user:
220 if tooltip and user:
221 if user.username == h.DEFAULT_USER:
221 if user.username == h.DEFAULT_USER:
222 gravatar_class.pop(-1)
222 gravatar_class.pop(-1)
223 else:
223 else:
224 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
224 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
225 gravatar_class = ' '.join(gravatar_class)
225 gravatar_class = ' '.join(gravatar_class)
226
226
227 %>
227 %>
228 <%doc>
228 <%doc>
229 TODO: johbo: For now we serve double size images to make it smooth
229 TODO: johbo: For now we serve double size images to make it smooth
230 for retina. This is how it worked until now. Should be replaced
230 for retina. This is how it worked until now. Should be replaced
231 with a better solution at some point.
231 with a better solution at some point.
232 </%doc>
232 </%doc>
233
233
234 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
234 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
235 </%def>
235 </%def>
236
236
237
237
238 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False, _class='rc-user')">
238 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False, _class='rc-user')">
239 <%
239 <%
240 email = h.email_or_none(contact)
240 email = h.email_or_none(contact)
241 rc_user = h.discover_user(contact)
241 rc_user = h.discover_user(contact)
242 %>
242 %>
243
243
244 <div class="${_class}">
244 <div class="${_class}">
245 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
245 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
246 <span class="${('user user-disabled' if show_disabled else 'user')}">
246 <span class="${('user user-disabled' if show_disabled else 'user')}">
247 ${h.link_to_user(rc_user or contact)}
247 ${h.link_to_user(rc_user or contact)}
248 </span>
248 </span>
249 </div>
249 </div>
250 </%def>
250 </%def>
251
251
252
252
253 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
253 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
254 <%
254 <%
255 if (size > 16):
255 if (size > 16):
256 gravatar_class = 'icon-user-group-alt'
256 gravatar_class = 'icon-user-group-alt'
257 else:
257 else:
258 gravatar_class = 'icon-user-group-alt'
258 gravatar_class = 'icon-user-group-alt'
259
259
260 if tooltip:
260 if tooltip:
261 gravatar_class += ' tooltip-hovercard'
261 gravatar_class += ' tooltip-hovercard'
262
262
263 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
263 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
264 %>
264 %>
265 <%doc>
265 <%doc>
266 TODO: johbo: For now we serve double size images to make it smooth
266 TODO: johbo: For now we serve double size images to make it smooth
267 for retina. This is how it worked until now. Should be replaced
267 for retina. This is how it worked until now. Should be replaced
268 with a better solution at some point.
268 with a better solution at some point.
269 </%doc>
269 </%doc>
270
270
271 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
271 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
272 </%def>
272 </%def>
273
273
274 <%def name="repo_page_title(repo_instance)">
274 <%def name="repo_page_title(repo_instance)">
275 <div class="title-content repo-title">
275 <div class="title-content repo-title">
276
276
277 <div class="title-main">
277 <div class="title-main">
278 ## SVN/HG/GIT icons
278 ## SVN/HG/GIT icons
279 %if h.is_hg(repo_instance):
279 %if h.is_hg(repo_instance):
280 <i class="icon-hg"></i>
280 <i class="icon-hg"></i>
281 %endif
281 %endif
282 %if h.is_git(repo_instance):
282 %if h.is_git(repo_instance):
283 <i class="icon-git"></i>
283 <i class="icon-git"></i>
284 %endif
284 %endif
285 %if h.is_svn(repo_instance):
285 %if h.is_svn(repo_instance):
286 <i class="icon-svn"></i>
286 <i class="icon-svn"></i>
287 %endif
287 %endif
288
288
289 ## public/private
289 ## public/private
290 %if repo_instance.private:
290 %if repo_instance.private:
291 <i class="icon-repo-private"></i>
291 <i class="icon-repo-private"></i>
292 %else:
292 %else:
293 <i class="icon-repo-public"></i>
293 <i class="icon-repo-public"></i>
294 %endif
294 %endif
295
295
296 ## repo name with group name
296 ## repo name with group name
297 ${h.breadcrumb_repo_link(repo_instance)}
297 ${h.breadcrumb_repo_link(repo_instance)}
298
298
299 ## Context Actions
299 ## Context Actions
300 <div class="pull-right">
300 <div class="pull-right">
301 %if c.rhodecode_user.username != h.DEFAULT_USER:
301 %if c.rhodecode_user.username != h.DEFAULT_USER:
302 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
302 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
303
303
304 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
304 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
305 % if c.repository_is_user_following:
305 % if c.repository_is_user_following:
306 <i class="icon-eye-off"></i>${_('Unwatch')}
306 <i class="icon-eye-off"></i>${_('Unwatch')}
307 % else:
307 % else:
308 <i class="icon-eye"></i>${_('Watch')}
308 <i class="icon-eye"></i>${_('Watch')}
309 % endif
309 % endif
310
310
311 </a>
311 </a>
312 %else:
312 %else:
313 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
313 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
314 %endif
314 %endif
315 </div>
315 </div>
316
316
317 </div>
317 </div>
318
318
319 ## FORKED
319 ## FORKED
320 %if repo_instance.fork:
320 %if repo_instance.fork:
321 <p class="discreet">
321 <p class="discreet">
322 <i class="icon-code-fork"></i> ${_('Fork of')}
322 <i class="icon-code-fork"></i> ${_('Fork of')}
323 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
323 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
324 </p>
324 </p>
325 %endif
325 %endif
326
326
327 ## IMPORTED FROM REMOTE
327 ## IMPORTED FROM REMOTE
328 %if repo_instance.clone_uri:
328 %if repo_instance.clone_uri:
329 <p class="discreet">
329 <p class="discreet">
330 <i class="icon-code-fork"></i> ${_('Clone from')}
330 <i class="icon-code-fork"></i> ${_('Clone from')}
331 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
331 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
332 </p>
332 </p>
333 %endif
333 %endif
334
334
335 ## LOCKING STATUS
335 ## LOCKING STATUS
336 %if repo_instance.locked[0]:
336 %if repo_instance.locked[0]:
337 <p class="locking_locked discreet">
337 <p class="locking_locked discreet">
338 <i class="icon-repo-lock"></i>
338 <i class="icon-repo-lock"></i>
339 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
339 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
340 </p>
340 </p>
341 %elif repo_instance.enable_locking:
341 %elif repo_instance.enable_locking:
342 <p class="locking_unlocked discreet">
342 <p class="locking_unlocked discreet">
343 <i class="icon-repo-unlock"></i>
343 <i class="icon-repo-unlock"></i>
344 ${_('Repository not locked. Pull repository to lock it.')}
344 ${_('Repository not locked. Pull repository to lock it.')}
345 </p>
345 </p>
346 %endif
346 %endif
347
347
348 </div>
348 </div>
349 </%def>
349 </%def>
350
350
351 <%def name="repo_menu(active=None)">
351 <%def name="repo_menu(active=None)">
352 <%
352 <%
353 ## determine if we have "any" option available
353 ## determine if we have "any" option available
354 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
354 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
355 has_actions = can_lock
355 has_actions = can_lock
356
356
357 %>
357 %>
358 % if c.rhodecode_db_repo.archived:
358 % if c.rhodecode_db_repo.archived:
359 <div class="alert alert-warning text-center">
359 <div class="alert alert-warning text-center">
360 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
360 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
361 </div>
361 </div>
362 % endif
362 % endif
363
363
364 <!--- REPO CONTEXT BAR -->
364 <!--- REPO CONTEXT BAR -->
365 <div id="context-bar">
365 <div id="context-bar">
366 <div class="wrapper">
366 <div class="wrapper">
367
367
368 <div class="title">
368 <div class="title">
369 ${self.repo_page_title(c.rhodecode_db_repo)}
369 ${self.repo_page_title(c.rhodecode_db_repo)}
370 </div>
370 </div>
371
371
372 <ul id="context-pages" class="navigation horizontal-list">
372 <ul id="context-pages" class="navigation horizontal-list">
373 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary_explicit', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
373 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary_explicit', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
374 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
374 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
375 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.repo_files_by_ref_url(c.repo_name, c.rhodecode_db_repo.repo_type, f_path='', ref_name=c.rhodecode_db_repo.landing_ref_name, commit_id='tip', query={'at':c.rhodecode_db_repo.landing_ref_name})}"><div class="menulabel">${_('Files')}</div></a></li>
375 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.repo_files_by_ref_url(c.repo_name, c.rhodecode_db_repo.repo_type, f_path='', ref_name=c.rhodecode_db_repo.landing_ref_name, commit_id='tip', query={'at':c.rhodecode_db_repo.landing_ref_name})}"><div class="menulabel">${_('Files')}</div></a></li>
376 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
376 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
377
377
378 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
378 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
379 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
379 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
380 <li class="${h.is_active('showpullrequest', active)}">
380 <li class="${h.is_active('showpullrequest', active)}">
381 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
381 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
382 <div class="menulabel">
382 <div class="menulabel">
383 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
383 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
384 </div>
384 </div>
385 </a>
385 </a>
386 </li>
386 </li>
387 %endif
387 %endif
388
388
389 <li class="${h.is_active('artifacts', active)}">
389 <li class="${h.is_active('artifacts', active)}">
390 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
390 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
391 <div class="menulabel">
391 <div class="menulabel">
392 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
392 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
393 </div>
393 </div>
394 </a>
394 </a>
395 </li>
395 </li>
396
396
397 %if not c.rhodecode_db_repo.archived and h.HasRepoPermissionAll('repository.admin')(c.repo_name):
397 %if not c.rhodecode_db_repo.archived and h.HasRepoPermissionAll('repository.admin')(c.repo_name):
398 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
398 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
399 %endif
399 %endif
400
400
401 <li class="${h.is_active('options', active)}">
401 <li class="${h.is_active('options', active)}">
402 % if has_actions:
402 % if has_actions:
403 <a class="menulink dropdown">
403 <a class="menulink dropdown">
404 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
404 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
405 </a>
405 </a>
406 <ul class="submenu">
406 <ul class="submenu">
407 %if can_lock:
407 %if can_lock:
408 %if c.rhodecode_db_repo.locked[0]:
408 %if c.rhodecode_db_repo.locked[0]:
409 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
409 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
410 %else:
410 %else:
411 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
411 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
412 %endif
412 %endif
413 %endif
413 %endif
414 </ul>
414 </ul>
415 % endif
415 % endif
416 </li>
416 </li>
417
417
418 </ul>
418 </ul>
419 </div>
419 </div>
420 <div class="clear"></div>
420 <div class="clear"></div>
421 </div>
421 </div>
422
422
423 <!--- REPO END CONTEXT BAR -->
423 <!--- REPO END CONTEXT BAR -->
424
424
425 </%def>
425 </%def>
426
426
427 <%def name="repo_group_page_title(repo_group_instance)">
427 <%def name="repo_group_page_title(repo_group_instance)">
428 <div class="title-content">
428 <div class="title-content">
429 <div class="title-main">
429 <div class="title-main">
430 ## Repository Group icon
430 ## Repository Group icon
431 <i class="icon-repo-group"></i>
431 <i class="icon-repo-group"></i>
432
432
433 ## repo name with group name
433 ## repo name with group name
434 ${h.breadcrumb_repo_group_link(repo_group_instance)}
434 ${h.breadcrumb_repo_group_link(repo_group_instance)}
435 </div>
435 </div>
436
436
437 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
437 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
438 <div class="repo-group-desc discreet">
438 <div class="repo-group-desc discreet">
439 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
439 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
440 </div>
440 </div>
441
441
442 </div>
442 </div>
443 </%def>
443 </%def>
444
444
445
445
446 <%def name="repo_group_menu(active=None)">
446 <%def name="repo_group_menu(active=None)">
447 <%
447 <%
448 gr_name = c.repo_group.group_name if c.repo_group else None
448 gr_name = c.repo_group.group_name if c.repo_group else None
449 # create repositories with write permission on group is set to true
449 # create repositories with write permission on group is set to true
450 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
450 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
451
451
452 %>
452 %>
453
453
454
454
455 <!--- REPO GROUP CONTEXT BAR -->
455 <!--- REPO GROUP CONTEXT BAR -->
456 <div id="context-bar">
456 <div id="context-bar">
457 <div class="wrapper">
457 <div class="wrapper">
458 <div class="title">
458 <div class="title">
459 ${self.repo_group_page_title(c.repo_group)}
459 ${self.repo_group_page_title(c.repo_group)}
460 </div>
460 </div>
461
461
462 <ul id="context-pages" class="navigation horizontal-list">
462 <ul id="context-pages" class="navigation horizontal-list">
463 <li class="${h.is_active('home', active)}">
463 <li class="${h.is_active('home', active)}">
464 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
464 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
465 </li>
465 </li>
466 % if c.is_super_admin or group_admin:
466 % if c.is_super_admin or group_admin:
467 <li class="${h.is_active('settings', active)}">
467 <li class="${h.is_active('settings', active)}">
468 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
468 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
469 </li>
469 </li>
470 % endif
470 % endif
471
471
472 </ul>
472 </ul>
473 </div>
473 </div>
474 <div class="clear"></div>
474 <div class="clear"></div>
475 </div>
475 </div>
476
476
477 <!--- REPO GROUP CONTEXT BAR -->
477 <!--- REPO GROUP CONTEXT BAR -->
478
478
479 </%def>
479 </%def>
480
480
481
481
482 <%def name="usermenu(active=False)">
482 <%def name="usermenu(active=False)">
483 <%
483 <%
484 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
484 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
485
485
486 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
486 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
487 # create repositories with write permission on group is set to true
487 # create repositories with write permission on group is set to true
488
488
489 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
489 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
490 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
490 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
491 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
491 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
492 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
492 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
493
493
494 can_create_repos = c.is_super_admin or c.can_create_repo
494 can_create_repos = c.is_super_admin or c.can_create_repo
495 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
495 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
496
496
497 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
497 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
498 can_create_repo_groups_in_group = c.is_super_admin or group_admin
498 can_create_repo_groups_in_group = c.is_super_admin or group_admin
499 %>
499 %>
500
500
501 % if not_anonymous:
501 % if not_anonymous:
502 <%
502 <%
503 default_target_group = dict()
503 default_target_group = dict()
504 if c.rhodecode_user.personal_repo_group:
504 if c.rhodecode_user.personal_repo_group:
505 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
505 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
506 %>
506 %>
507
507
508 ## create action
508 ## create action
509 <li>
509 <li>
510 <a href="#create-actions" onclick="return false;" class="menulink childs">
510 <a href="#create-actions" onclick="return false;" class="menulink childs">
511 <i class="tooltip icon-plus-circled" title="${_('Create')}"></i>
511 <i class="icon-plus-circled"></i>
512 </a>
512 </a>
513
513
514 <div class="action-menu submenu">
514 <div class="action-menu submenu">
515
515
516 <ol>
516 <ol>
517 ## scope of within a repository
517 ## scope of within a repository
518 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
518 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
519 <li class="submenu-title">${_('This Repository')}</li>
519 <li class="submenu-title">${_('This Repository')}</li>
520 <li>
520 <li>
521 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
521 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
522 </li>
522 </li>
523 % if can_fork:
523 % if can_fork:
524 <li>
524 <li>
525 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
525 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
526 </li>
526 </li>
527 % endif
527 % endif
528 % endif
528 % endif
529
529
530 ## scope of within repository groups
530 ## scope of within repository groups
531 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
531 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
532 <li class="submenu-title">${_('This Repository Group')}</li>
532 <li class="submenu-title">${_('This Repository Group')}</li>
533
533
534 % if can_create_repos_in_group:
534 % if can_create_repos_in_group:
535 <li>
535 <li>
536 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('New Repository')}</a>
536 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('New Repository')}</a>
537 </li>
537 </li>
538 % endif
538 % endif
539
539
540 % if can_create_repo_groups_in_group:
540 % if can_create_repo_groups_in_group:
541 <li>
541 <li>
542 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'New Repository Group')}</a>
542 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'New Repository Group')}</a>
543 </li>
543 </li>
544 % endif
544 % endif
545 % endif
545 % endif
546
546
547 ## personal group
547 ## personal group
548 % if c.rhodecode_user.personal_repo_group:
548 % if c.rhodecode_user.personal_repo_group:
549 <li class="submenu-title">Personal Group</li>
549 <li class="submenu-title">Personal Group</li>
550
550
551 <li>
551 <li>
552 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
552 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
553 </li>
553 </li>
554
554
555 <li>
555 <li>
556 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
556 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
557 </li>
557 </li>
558 % endif
558 % endif
559
559
560 ## Global actions
560 ## Global actions
561 <li class="submenu-title">RhodeCode</li>
561 <li class="submenu-title">RhodeCode</li>
562 % if can_create_repos:
562 % if can_create_repos:
563 <li>
563 <li>
564 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
564 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
565 </li>
565 </li>
566 % endif
566 % endif
567
567
568 % if can_create_repo_groups:
568 % if can_create_repo_groups:
569 <li>
569 <li>
570 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
570 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
571 </li>
571 </li>
572 % endif
572 % endif
573
573
574 <li>
574 <li>
575 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
575 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
576 </li>
576 </li>
577
577
578 </ol>
578 </ol>
579
579
580 </div>
580 </div>
581 </li>
581 </li>
582
582
583 ## notifications
583 ## notifications
584 <li>
584 <li>
585 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
585 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
586 ${c.unread_notifications}
586 ${c.unread_notifications}
587 </a>
587 </a>
588 </li>
588 </li>
589 % endif
589 % endif
590
590
591 ## USER MENU
591 ## USER MENU
592 <li id="quick_login_li" class="${'active' if active else ''}">
592 <li id="quick_login_li" class="${'active' if active else ''}">
593 % if c.rhodecode_user.username == h.DEFAULT_USER:
593 % if c.rhodecode_user.username == h.DEFAULT_USER:
594 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
594 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
595 ${gravatar(c.rhodecode_user.email, 20)}
595 ${gravatar(c.rhodecode_user.email, 20)}
596 <span class="user">
596 <span class="user">
597 <span>${_('Sign in')}</span>
597 <span>${_('Sign in')}</span>
598 </span>
598 </span>
599 </a>
599 </a>
600 % else:
600 % else:
601 ## logged in user
601 ## logged in user
602 <a id="quick_login_link" class="menulink childs">
602 <a id="quick_login_link" class="menulink childs">
603 ${gravatar(c.rhodecode_user.email, 20)}
603 ${gravatar(c.rhodecode_user.email, 20)}
604 <span class="user">
604 <span class="user">
605 <span class="menu_link_user">${c.rhodecode_user.username}</span>
605 <span class="menu_link_user">${c.rhodecode_user.username}</span>
606 <div class="show_more"></div>
606 <div class="show_more"></div>
607 </span>
607 </span>
608 </a>
608 </a>
609 ## subnav with menu for logged in user
609 ## subnav with menu for logged in user
610 <div class="user-menu submenu">
610 <div class="user-menu submenu">
611 <div id="quick_login">
611 <div id="quick_login">
612 %if c.rhodecode_user.username != h.DEFAULT_USER:
612 %if c.rhodecode_user.username != h.DEFAULT_USER:
613 <div class="">
613 <div class="">
614 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
614 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
615 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
615 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
616 <div class="email">${c.rhodecode_user.email}</div>
616 <div class="email">${c.rhodecode_user.email}</div>
617 </div>
617 </div>
618 <div class="">
618 <div class="">
619 <ol class="links">
619 <ol class="links">
620 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
620 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
621 % if c.rhodecode_user.personal_repo_group:
621 % if c.rhodecode_user.personal_repo_group:
622 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
622 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
623 % endif
623 % endif
624 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
624 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
625
625
626 % if c.debug_style:
626 % if c.debug_style:
627 <li>
627 <li>
628 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
628 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
629 <div class="menulabel">${_('[Style]')}</div>
629 <div class="menulabel">${_('[Style]')}</div>
630 </a>
630 </a>
631 </li>
631 </li>
632 % endif
632 % endif
633
633
634 ## bookmark-items
634 ## bookmark-items
635 <li class="bookmark-items">
635 <li class="bookmark-items">
636 ${_('Bookmarks')}
636 ${_('Bookmarks')}
637 <div class="pull-right">
637 <div class="pull-right">
638 <a href="${h.route_path('my_account_bookmarks')}">
638 <a href="${h.route_path('my_account_bookmarks')}">
639
639
640 <i class="icon-cog"></i>
640 <i class="icon-cog"></i>
641 </a>
641 </a>
642 </div>
642 </div>
643 </li>
643 </li>
644 % if not c.bookmark_items:
644 % if not c.bookmark_items:
645 <li>
645 <li>
646 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
646 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
647 </li>
647 </li>
648 % endif
648 % endif
649 % for item in c.bookmark_items:
649 % for item in c.bookmark_items:
650 <li>
650 <li>
651 % if item.repository:
651 % if item.repository:
652 <div>
652 <div>
653 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
653 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
654 <code>${item.position}</code>
654 <code>${item.position}</code>
655 % if item.repository.repo_type == 'hg':
655 % if item.repository.repo_type == 'hg':
656 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
656 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
657 % elif item.repository.repo_type == 'git':
657 % elif item.repository.repo_type == 'git':
658 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
658 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
659 % elif item.repository.repo_type == 'svn':
659 % elif item.repository.repo_type == 'svn':
660 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
660 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
661 % endif
661 % endif
662 ${(item.title or h.shorter(item.repository.repo_name, 30))}
662 ${(item.title or h.shorter(item.repository.repo_name, 30))}
663 </a>
663 </a>
664 </div>
664 </div>
665 % elif item.repository_group:
665 % elif item.repository_group:
666 <div>
666 <div>
667 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
667 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
668 <code>${item.position}</code>
668 <code>${item.position}</code>
669 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
669 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
670 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
670 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
671 </a>
671 </a>
672 </div>
672 </div>
673 % else:
673 % else:
674 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
674 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
675 <code>${item.position}</code>
675 <code>${item.position}</code>
676 ${item.title}
676 ${item.title}
677 </a>
677 </a>
678 % endif
678 % endif
679 </li>
679 </li>
680 % endfor
680 % endfor
681
681
682 <li class="logout">
682 <li class="logout">
683 ${h.secure_form(h.route_path('logout'), request=request)}
683 ${h.secure_form(h.route_path('logout'), request=request)}
684 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
684 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
685 ${h.end_form()}
685 ${h.end_form()}
686 </li>
686 </li>
687 </ol>
687 </ol>
688 </div>
688 </div>
689 %endif
689 %endif
690 </div>
690 </div>
691 </div>
691 </div>
692
692
693 % endif
693 % endif
694 </li>
694 </li>
695 </%def>
695 </%def>
696
696
697 <%def name="menu_items(active=None)">
697 <%def name="menu_items(active=None)">
698 <%
698 <%
699 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
699 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
700 notice_display = 'none' if len(notice_messages) == 0 else ''
700 notice_display = 'none' if len(notice_messages) == 0 else ''
701 %>
701 %>
702 <style>
702 <style>
703
703
704 </style>
704 </style>
705
705
706 <ul id="quick" class="main_nav navigation horizontal-list">
706 <ul id="quick" class="main_nav navigation horizontal-list">
707 ## notice box for important system messages
707 ## notice box for important system messages
708 <li style="display: ${notice_display}">
708 <li style="display: ${notice_display}">
709 <a class="notice-box" href="#openNotice" onclick="$('.notice-messages-container').toggle(); return false">
709 <a class="notice-box" href="#openNotice" onclick="$('.notice-messages-container').toggle(); return false">
710 <div class="menulabel-notice ${notice_level}" >
710 <div class="menulabel-notice ${notice_level}" >
711 ${len(notice_messages)}
711 ${len(notice_messages)}
712 </div>
712 </div>
713 </a>
713 </a>
714 </li>
714 </li>
715 <div class="notice-messages-container" style="display: none">
715 <div class="notice-messages-container" style="display: none">
716 <div class="notice-messages">
716 <div class="notice-messages">
717 <table class="rctable">
717 <table class="rctable">
718 % for notice in notice_messages:
718 % for notice in notice_messages:
719 <tr id="notice-message-${notice['msg_id']}" class="notice-message-${notice['level']}">
719 <tr id="notice-message-${notice['msg_id']}" class="notice-message-${notice['level']}">
720 <td style="vertical-align: text-top; width: 20px">
720 <td style="vertical-align: text-top; width: 20px">
721 <i class="tooltip icon-info notice-color-${notice['level']}" title="${notice['level']}"></i>
721 <i class="tooltip icon-info notice-color-${notice['level']}" title="${notice['level']}"></i>
722 </td>
722 </td>
723 <td>
723 <td>
724 <span><i class="icon-plus-squared cursor-pointer" onclick="$('#notice-${notice['msg_id']}').toggle()"></i> </span>
724 <span><i class="icon-plus-squared cursor-pointer" onclick="$('#notice-${notice['msg_id']}').toggle()"></i> </span>
725 ${notice['subject']}
725 ${notice['subject']}
726
726
727 <div id="notice-${notice['msg_id']}" style="display: none">
727 <div id="notice-${notice['msg_id']}" style="display: none">
728 ${h.render(notice['body'], renderer='markdown')}
728 ${h.render(notice['body'], renderer='markdown')}
729 </div>
729 </div>
730 </td>
730 </td>
731 <td style="vertical-align: text-top; width: 35px;">
731 <td style="vertical-align: text-top; width: 35px;">
732 <a class="tooltip" title="${_('dismiss')}" href="#dismiss" onclick="dismissNotice(${notice['msg_id']});return false">
732 <a class="tooltip" title="${_('dismiss')}" href="#dismiss" onclick="dismissNotice(${notice['msg_id']});return false">
733 <i class="icon-remove icon-filled-red"></i>
733 <i class="icon-remove icon-filled-red"></i>
734 </a>
734 </a>
735 </td>
735 </td>
736 </tr>
736 </tr>
737
737
738 % endfor
738 % endfor
739 </table>
739 </table>
740 </div>
740 </div>
741 </div>
741 </div>
742 ## Main filter
742 ## Main filter
743 <li>
743 <li>
744 <div class="menulabel main_filter_box">
744 <div class="menulabel main_filter_box">
745 <div class="main_filter_input_box">
745 <div class="main_filter_input_box">
746 <ul class="searchItems">
746 <ul class="searchItems">
747
747
748 <li class="searchTag searchTagIcon">
748 <li class="searchTag searchTagIcon">
749 <i class="icon-search"></i>
749 <i class="icon-search"></i>
750 </li>
750 </li>
751
751
752 % if c.template_context['search_context']['repo_id']:
752 % if c.template_context['search_context']['repo_id']:
753 <li class="searchTag searchTagFilter searchTagHidable" >
753 <li class="searchTag searchTagFilter searchTagHidable" >
754 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
754 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
755 <span class="tag">
755 <span class="tag">
756 This repo
756 This repo
757 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
757 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
758 </span>
758 </span>
759 ##</a>
759 ##</a>
760 </li>
760 </li>
761 % elif c.template_context['search_context']['repo_group_id']:
761 % elif c.template_context['search_context']['repo_group_id']:
762 <li class="searchTag searchTagFilter searchTagHidable">
762 <li class="searchTag searchTagFilter searchTagHidable">
763 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
763 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
764 <span class="tag">
764 <span class="tag">
765 This group
765 This group
766 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
766 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
767 </span>
767 </span>
768 ##</a>
768 ##</a>
769 </li>
769 </li>
770 % endif
770 % endif
771
771
772 <li class="searchTagInput">
772 <li class="searchTagInput">
773 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
773 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
774 </li>
774 </li>
775 <li class="searchTag searchTagHelp">
775 <li class="searchTag searchTagHelp">
776 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
776 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
777 </li>
777 </li>
778 </ul>
778 </ul>
779 </div>
779 </div>
780 </div>
780 </div>
781
781
782 <div id="main_filter_help" style="display: none">
782 <div id="main_filter_help" style="display: none">
783 - Use '/' key to quickly access this field.
783 - Use '/' key to quickly access this field.
784
784
785 - Enter a name of repository, or repository group for quick search.
785 - Enter a name of repository, or repository group for quick search.
786
786
787 - Prefix query to allow special search:
787 - Prefix query to allow special search:
788
788
789 user:admin, to search for usernames, always global
789 user:admin, to search for usernames, always global
790
790
791 user_group:devops, to search for user groups, always global
791 user_group:devops, to search for user groups, always global
792
792
793 pr:303, to search for pull request number, title, or description, always global
793 pr:303, to search for pull request number, title, or description, always global
794
794
795 commit:efced4, to search for commits, scoped to repositories or groups
795 commit:efced4, to search for commits, scoped to repositories or groups
796
796
797 file:models.py, to search for file paths, scoped to repositories or groups
797 file:models.py, to search for file paths, scoped to repositories or groups
798
798
799 % if c.template_context['search_context']['repo_id']:
799 % if c.template_context['search_context']['repo_id']:
800 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
800 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
801 % elif c.template_context['search_context']['repo_group_id']:
801 % elif c.template_context['search_context']['repo_group_id']:
802 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
802 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
803 % else:
803 % else:
804 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
804 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
805 % endif
805 % endif
806 </div>
806 </div>
807 </li>
807 </li>
808
808
809 ## ROOT MENU
809 ## ROOT MENU
810 <li class="${h.is_active('home', active)}">
810 <li class="${h.is_active('home', active)}">
811 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
811 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
812 <div class="menulabel">${_('Home')}</div>
812 <div class="menulabel">${_('Home')}</div>
813 </a>
813 </a>
814 </li>
814 </li>
815
815
816 %if c.rhodecode_user.username != h.DEFAULT_USER:
816 %if c.rhodecode_user.username != h.DEFAULT_USER:
817 <li class="${h.is_active('journal', active)}">
817 <li class="${h.is_active('journal', active)}">
818 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
818 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
819 <div class="menulabel">${_('Journal')}</div>
819 <div class="menulabel">${_('Journal')}</div>
820 </a>
820 </a>
821 </li>
821 </li>
822 %else:
822 %else:
823 <li class="${h.is_active('journal', active)}">
823 <li class="${h.is_active('journal', active)}">
824 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
824 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
825 <div class="menulabel">${_('Public journal')}</div>
825 <div class="menulabel">${_('Public journal')}</div>
826 </a>
826 </a>
827 </li>
827 </li>
828 %endif
828 %endif
829
829
830 <li class="${h.is_active('gists', active)}">
830 <li class="${h.is_active('gists', active)}">
831 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
831 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
832 <div class="menulabel">${_('Gists')}</div>
832 <div class="menulabel">${_('Gists')}</div>
833 </a>
833 </a>
834 </li>
834 </li>
835
835
836 % if c.is_super_admin or c.is_delegated_admin:
836 % if c.is_super_admin or c.is_delegated_admin:
837 <li class="${h.is_active('admin', active)}">
837 <li class="${h.is_active('admin', active)}">
838 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
838 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
839 <div class="menulabel">${_('Admin')} </div>
839 <div class="menulabel">${_('Admin')} </div>
840 </a>
840 </a>
841 </li>
841 </li>
842 % endif
842 % endif
843
843
844 ## render extra user menu
844 ## render extra user menu
845 ${usermenu(active=(active=='my_account'))}
845 ${usermenu(active=(active=='my_account'))}
846
846
847 </ul>
847 </ul>
848
848
849 <script type="text/javascript">
849 <script type="text/javascript">
850 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
850 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
851
851
852 var formatRepoResult = function(result, container, query, escapeMarkup) {
852 var formatRepoResult = function(result, container, query, escapeMarkup) {
853 return function(data, escapeMarkup) {
853 return function(data, escapeMarkup) {
854 if (!data.repo_id){
854 if (!data.repo_id){
855 return data.text; // optgroup text Repositories
855 return data.text; // optgroup text Repositories
856 }
856 }
857
857
858 var tmpl = '';
858 var tmpl = '';
859 var repoType = data['repo_type'];
859 var repoType = data['repo_type'];
860 var repoName = data['text'];
860 var repoName = data['text'];
861
861
862 if(data && data.type == 'repo'){
862 if(data && data.type == 'repo'){
863 if(repoType === 'hg'){
863 if(repoType === 'hg'){
864 tmpl += '<i class="icon-hg"></i> ';
864 tmpl += '<i class="icon-hg"></i> ';
865 }
865 }
866 else if(repoType === 'git'){
866 else if(repoType === 'git'){
867 tmpl += '<i class="icon-git"></i> ';
867 tmpl += '<i class="icon-git"></i> ';
868 }
868 }
869 else if(repoType === 'svn'){
869 else if(repoType === 'svn'){
870 tmpl += '<i class="icon-svn"></i> ';
870 tmpl += '<i class="icon-svn"></i> ';
871 }
871 }
872 if(data['private']){
872 if(data['private']){
873 tmpl += '<i class="icon-lock" ></i> ';
873 tmpl += '<i class="icon-lock" ></i> ';
874 }
874 }
875 else if(visualShowPublicIcon){
875 else if(visualShowPublicIcon){
876 tmpl += '<i class="icon-unlock-alt"></i> ';
876 tmpl += '<i class="icon-unlock-alt"></i> ';
877 }
877 }
878 }
878 }
879 tmpl += escapeMarkup(repoName);
879 tmpl += escapeMarkup(repoName);
880 return tmpl;
880 return tmpl;
881
881
882 }(result, escapeMarkup);
882 }(result, escapeMarkup);
883 };
883 };
884
884
885 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
885 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
886 return function(data, escapeMarkup) {
886 return function(data, escapeMarkup) {
887 if (!data.repo_group_id){
887 if (!data.repo_group_id){
888 return data.text; // optgroup text Repositories
888 return data.text; // optgroup text Repositories
889 }
889 }
890
890
891 var tmpl = '';
891 var tmpl = '';
892 var repoGroupName = data['text'];
892 var repoGroupName = data['text'];
893
893
894 if(data){
894 if(data){
895
895
896 tmpl += '<i class="icon-repo-group"></i> ';
896 tmpl += '<i class="icon-repo-group"></i> ';
897
897
898 }
898 }
899 tmpl += escapeMarkup(repoGroupName);
899 tmpl += escapeMarkup(repoGroupName);
900 return tmpl;
900 return tmpl;
901
901
902 }(result, escapeMarkup);
902 }(result, escapeMarkup);
903 };
903 };
904
904
905 var escapeRegExChars = function (value) {
905 var escapeRegExChars = function (value) {
906 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
906 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
907 };
907 };
908
908
909 var getRepoIcon = function(repo_type) {
909 var getRepoIcon = function(repo_type) {
910 if (repo_type === 'hg') {
910 if (repo_type === 'hg') {
911 return '<i class="icon-hg"></i> ';
911 return '<i class="icon-hg"></i> ';
912 }
912 }
913 else if (repo_type === 'git') {
913 else if (repo_type === 'git') {
914 return '<i class="icon-git"></i> ';
914 return '<i class="icon-git"></i> ';
915 }
915 }
916 else if (repo_type === 'svn') {
916 else if (repo_type === 'svn') {
917 return '<i class="icon-svn"></i> ';
917 return '<i class="icon-svn"></i> ';
918 }
918 }
919 return ''
919 return ''
920 };
920 };
921
921
922 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
922 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
923
923
924 if (value.split(':').length === 2) {
924 if (value.split(':').length === 2) {
925 value = value.split(':')[1]
925 value = value.split(':')[1]
926 }
926 }
927
927
928 var searchType = data['type'];
928 var searchType = data['type'];
929 var searchSubType = data['subtype'];
929 var searchSubType = data['subtype'];
930 var valueDisplay = data['value_display'];
930 var valueDisplay = data['value_display'];
931 var valueIcon = data['value_icon'];
931 var valueIcon = data['value_icon'];
932
932
933 var pattern = '(' + escapeRegExChars(value) + ')';
933 var pattern = '(' + escapeRegExChars(value) + ')';
934
934
935 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
935 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
936
936
937 // highlight match
937 // highlight match
938 if (searchType != 'text') {
938 if (searchType != 'text') {
939 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
939 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
940 }
940 }
941
941
942 var icon = '';
942 var icon = '';
943
943
944 if (searchType === 'hint') {
944 if (searchType === 'hint') {
945 icon += '<i class="icon-repo-group"></i> ';
945 icon += '<i class="icon-repo-group"></i> ';
946 }
946 }
947 // full text search/hints
947 // full text search/hints
948 else if (searchType === 'search') {
948 else if (searchType === 'search') {
949 if (valueIcon === undefined) {
949 if (valueIcon === undefined) {
950 icon += '<i class="icon-more"></i> ';
950 icon += '<i class="icon-more"></i> ';
951 } else {
951 } else {
952 icon += valueIcon + ' ';
952 icon += valueIcon + ' ';
953 }
953 }
954
954
955 if (searchSubType !== undefined && searchSubType == 'repo') {
955 if (searchSubType !== undefined && searchSubType == 'repo') {
956 valueDisplay += '<div class="pull-right tag">repository</div>';
956 valueDisplay += '<div class="pull-right tag">repository</div>';
957 }
957 }
958 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
958 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
959 valueDisplay += '<div class="pull-right tag">repo group</div>';
959 valueDisplay += '<div class="pull-right tag">repo group</div>';
960 }
960 }
961 }
961 }
962 // repository
962 // repository
963 else if (searchType === 'repo') {
963 else if (searchType === 'repo') {
964
964
965 var repoIcon = getRepoIcon(data['repo_type']);
965 var repoIcon = getRepoIcon(data['repo_type']);
966 icon += repoIcon;
966 icon += repoIcon;
967
967
968 if (data['private']) {
968 if (data['private']) {
969 icon += '<i class="icon-lock" ></i> ';
969 icon += '<i class="icon-lock" ></i> ';
970 }
970 }
971 else if (visualShowPublicIcon) {
971 else if (visualShowPublicIcon) {
972 icon += '<i class="icon-unlock-alt"></i> ';
972 icon += '<i class="icon-unlock-alt"></i> ';
973 }
973 }
974 }
974 }
975 // repository groups
975 // repository groups
976 else if (searchType === 'repo_group') {
976 else if (searchType === 'repo_group') {
977 icon += '<i class="icon-repo-group"></i> ';
977 icon += '<i class="icon-repo-group"></i> ';
978 }
978 }
979 // user group
979 // user group
980 else if (searchType === 'user_group') {
980 else if (searchType === 'user_group') {
981 icon += '<i class="icon-group"></i> ';
981 icon += '<i class="icon-group"></i> ';
982 }
982 }
983 // user
983 // user
984 else if (searchType === 'user') {
984 else if (searchType === 'user') {
985 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
985 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
986 }
986 }
987 // pull request
987 // pull request
988 else if (searchType === 'pull_request') {
988 else if (searchType === 'pull_request') {
989 icon += '<i class="icon-merge"></i> ';
989 icon += '<i class="icon-merge"></i> ';
990 }
990 }
991 // commit
991 // commit
992 else if (searchType === 'commit') {
992 else if (searchType === 'commit') {
993 var repo_data = data['repo_data'];
993 var repo_data = data['repo_data'];
994 var repoIcon = getRepoIcon(repo_data['repository_type']);
994 var repoIcon = getRepoIcon(repo_data['repository_type']);
995 if (repoIcon) {
995 if (repoIcon) {
996 icon += repoIcon;
996 icon += repoIcon;
997 } else {
997 } else {
998 icon += '<i class="icon-tag"></i>';
998 icon += '<i class="icon-tag"></i>';
999 }
999 }
1000 }
1000 }
1001 // file
1001 // file
1002 else if (searchType === 'file') {
1002 else if (searchType === 'file') {
1003 var repo_data = data['repo_data'];
1003 var repo_data = data['repo_data'];
1004 var repoIcon = getRepoIcon(repo_data['repository_type']);
1004 var repoIcon = getRepoIcon(repo_data['repository_type']);
1005 if (repoIcon) {
1005 if (repoIcon) {
1006 icon += repoIcon;
1006 icon += repoIcon;
1007 } else {
1007 } else {
1008 icon += '<i class="icon-tag"></i>';
1008 icon += '<i class="icon-tag"></i>';
1009 }
1009 }
1010 }
1010 }
1011 // generic text
1011 // generic text
1012 else if (searchType === 'text') {
1012 else if (searchType === 'text') {
1013 icon = '';
1013 icon = '';
1014 }
1014 }
1015
1015
1016 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
1016 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
1017 return tmpl.format(icon, valueDisplay);
1017 return tmpl.format(icon, valueDisplay);
1018 };
1018 };
1019
1019
1020 var handleSelect = function(element, suggestion) {
1020 var handleSelect = function(element, suggestion) {
1021 if (suggestion.type === "hint") {
1021 if (suggestion.type === "hint") {
1022 // we skip action
1022 // we skip action
1023 $('#main_filter').focus();
1023 $('#main_filter').focus();
1024 }
1024 }
1025 else if (suggestion.type === "text") {
1025 else if (suggestion.type === "text") {
1026 // we skip action
1026 // we skip action
1027 $('#main_filter').focus();
1027 $('#main_filter').focus();
1028
1028
1029 } else {
1029 } else {
1030 window.location = suggestion['url'];
1030 window.location = suggestion['url'];
1031 }
1031 }
1032 };
1032 };
1033
1033
1034 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
1034 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
1035 if (queryLowerCase.split(':').length === 2) {
1035 if (queryLowerCase.split(':').length === 2) {
1036 queryLowerCase = queryLowerCase.split(':')[1]
1036 queryLowerCase = queryLowerCase.split(':')[1]
1037 }
1037 }
1038 if (suggestion.type === "text") {
1038 if (suggestion.type === "text") {
1039 // special case we don't want to "skip" display for
1039 // special case we don't want to "skip" display for
1040 return true
1040 return true
1041 }
1041 }
1042 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1042 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1043 };
1043 };
1044
1044
1045 var cleanContext = {
1045 var cleanContext = {
1046 repo_view_type: null,
1046 repo_view_type: null,
1047
1047
1048 repo_id: null,
1048 repo_id: null,
1049 repo_name: "",
1049 repo_name: "",
1050
1050
1051 repo_group_id: null,
1051 repo_group_id: null,
1052 repo_group_name: null
1052 repo_group_name: null
1053 };
1053 };
1054 var removeGoToFilter = function () {
1054 var removeGoToFilter = function () {
1055 $('.searchTagHidable').hide();
1055 $('.searchTagHidable').hide();
1056 $('#main_filter').autocomplete(
1056 $('#main_filter').autocomplete(
1057 'setOptions', {params:{search_context: cleanContext}});
1057 'setOptions', {params:{search_context: cleanContext}});
1058 };
1058 };
1059
1059
1060 $('#main_filter').autocomplete({
1060 $('#main_filter').autocomplete({
1061 serviceUrl: pyroutes.url('goto_switcher_data'),
1061 serviceUrl: pyroutes.url('goto_switcher_data'),
1062 params: {
1062 params: {
1063 "search_context": templateContext.search_context
1063 "search_context": templateContext.search_context
1064 },
1064 },
1065 minChars:2,
1065 minChars:2,
1066 maxHeight:400,
1066 maxHeight:400,
1067 deferRequestBy: 300, //miliseconds
1067 deferRequestBy: 300, //miliseconds
1068 tabDisabled: true,
1068 tabDisabled: true,
1069 autoSelectFirst: false,
1069 autoSelectFirst: false,
1070 containerClass: 'autocomplete-qfilter-suggestions',
1070 containerClass: 'autocomplete-qfilter-suggestions',
1071 formatResult: autocompleteMainFilterFormatResult,
1071 formatResult: autocompleteMainFilterFormatResult,
1072 lookupFilter: autocompleteMainFilterResult,
1072 lookupFilter: autocompleteMainFilterResult,
1073 onSelect: function (element, suggestion) {
1073 onSelect: function (element, suggestion) {
1074 handleSelect(element, suggestion);
1074 handleSelect(element, suggestion);
1075 return false;
1075 return false;
1076 },
1076 },
1077 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1077 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1078 if (jqXHR !== 'abort') {
1078 if (jqXHR !== 'abort') {
1079 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
1079 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
1080 SwalNoAnimation.fire({
1080 SwalNoAnimation.fire({
1081 icon: 'error',
1081 icon: 'error',
1082 title: _gettext('Error during search operation'),
1082 title: _gettext('Error during search operation'),
1083 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
1083 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
1084 }).then(function(result) {
1084 }).then(function(result) {
1085 window.location.reload();
1085 window.location.reload();
1086 })
1086 })
1087 }
1087 }
1088 },
1088 },
1089 onSearchStart: function (params) {
1089 onSearchStart: function (params) {
1090 $('.searchTag.searchTagIcon').html('<i class="icon-spin animate-spin"></i>')
1090 $('.searchTag.searchTagIcon').html('<i class="icon-spin animate-spin"></i>')
1091 },
1091 },
1092 onSearchComplete: function (query, suggestions) {
1092 onSearchComplete: function (query, suggestions) {
1093 $('.searchTag.searchTagIcon').html('<i class="icon-search"></i>')
1093 $('.searchTag.searchTagIcon').html('<i class="icon-search"></i>')
1094 },
1094 },
1095 });
1095 });
1096
1096
1097 showMainFilterBox = function () {
1097 showMainFilterBox = function () {
1098 $('#main_filter_help').toggle();
1098 $('#main_filter_help').toggle();
1099 };
1099 };
1100
1100
1101 $('#main_filter').on('keydown.autocomplete', function (e) {
1101 $('#main_filter').on('keydown.autocomplete', function (e) {
1102
1102
1103 var BACKSPACE = 8;
1103 var BACKSPACE = 8;
1104 var el = $(e.currentTarget);
1104 var el = $(e.currentTarget);
1105 if(e.which === BACKSPACE){
1105 if(e.which === BACKSPACE){
1106 var inputVal = el.val();
1106 var inputVal = el.val();
1107 if (inputVal === ""){
1107 if (inputVal === ""){
1108 removeGoToFilter()
1108 removeGoToFilter()
1109 }
1109 }
1110 }
1110 }
1111 });
1111 });
1112
1112
1113 var dismissNotice = function(noticeId) {
1113 var dismissNotice = function(noticeId) {
1114
1114
1115 var url = pyroutes.url('user_notice_dismiss',
1115 var url = pyroutes.url('user_notice_dismiss',
1116 {"user_id": templateContext.rhodecode_user.user_id});
1116 {"user_id": templateContext.rhodecode_user.user_id});
1117
1117
1118 var postData = {
1118 var postData = {
1119 'csrf_token': CSRF_TOKEN,
1119 'csrf_token': CSRF_TOKEN,
1120 'notice_id': noticeId,
1120 'notice_id': noticeId,
1121 };
1121 };
1122
1122
1123 var success = function(response) {
1123 var success = function(response) {
1124 $('#notice-message-' + noticeId).remove();
1124 $('#notice-message-' + noticeId).remove();
1125 return false;
1125 return false;
1126 };
1126 };
1127 var failure = function(data, textStatus, xhr) {
1127 var failure = function(data, textStatus, xhr) {
1128 alert("error processing request: " + textStatus);
1128 alert("error processing request: " + textStatus);
1129 return false;
1129 return false;
1130 };
1130 };
1131 ajaxPOST(url, postData, success, failure);
1131 ajaxPOST(url, postData, success, failure);
1132 }
1132 }
1133 </script>
1133 </script>
1134 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1134 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1135 </%def>
1135 </%def>
1136
1136
1137 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1137 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1138 <div class="modal-dialog">
1138 <div class="modal-dialog">
1139 <div class="modal-content">
1139 <div class="modal-content">
1140 <div class="modal-header">
1140 <div class="modal-header">
1141 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1141 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1142 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1142 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1143 </div>
1143 </div>
1144 <div class="modal-body">
1144 <div class="modal-body">
1145 <div class="block-left">
1145 <div class="block-left">
1146 <table class="keyboard-mappings">
1146 <table class="keyboard-mappings">
1147 <tbody>
1147 <tbody>
1148 <tr>
1148 <tr>
1149 <th></th>
1149 <th></th>
1150 <th>${_('Site-wide shortcuts')}</th>
1150 <th>${_('Site-wide shortcuts')}</th>
1151 </tr>
1151 </tr>
1152 <%
1152 <%
1153 elems = [
1153 elems = [
1154 ('/', 'Use quick search box'),
1154 ('/', 'Use quick search box'),
1155 ('g h', 'Goto home page'),
1155 ('g h', 'Goto home page'),
1156 ('g g', 'Goto my private gists page'),
1156 ('g g', 'Goto my private gists page'),
1157 ('g G', 'Goto my public gists page'),
1157 ('g G', 'Goto my public gists page'),
1158 ('g 0-9', 'Goto bookmarked items from 0-9'),
1158 ('g 0-9', 'Goto bookmarked items from 0-9'),
1159 ('n r', 'New repository page'),
1159 ('n r', 'New repository page'),
1160 ('n g', 'New gist page'),
1160 ('n g', 'New gist page'),
1161 ]
1161 ]
1162 %>
1162 %>
1163 %for key, desc in elems:
1163 %for key, desc in elems:
1164 <tr>
1164 <tr>
1165 <td class="keys">
1165 <td class="keys">
1166 <span class="key tag">${key}</span>
1166 <span class="key tag">${key}</span>
1167 </td>
1167 </td>
1168 <td>${desc}</td>
1168 <td>${desc}</td>
1169 </tr>
1169 </tr>
1170 %endfor
1170 %endfor
1171 </tbody>
1171 </tbody>
1172 </table>
1172 </table>
1173 </div>
1173 </div>
1174 <div class="block-left">
1174 <div class="block-left">
1175 <table class="keyboard-mappings">
1175 <table class="keyboard-mappings">
1176 <tbody>
1176 <tbody>
1177 <tr>
1177 <tr>
1178 <th></th>
1178 <th></th>
1179 <th>${_('Repositories')}</th>
1179 <th>${_('Repositories')}</th>
1180 </tr>
1180 </tr>
1181 <%
1181 <%
1182 elems = [
1182 elems = [
1183 ('g s', 'Goto summary page'),
1183 ('g s', 'Goto summary page'),
1184 ('g c', 'Goto changelog page'),
1184 ('g c', 'Goto changelog page'),
1185 ('g f', 'Goto files page'),
1185 ('g f', 'Goto files page'),
1186 ('g F', 'Goto files page with file search activated'),
1186 ('g F', 'Goto files page with file search activated'),
1187 ('g p', 'Goto pull requests page'),
1187 ('g p', 'Goto pull requests page'),
1188 ('g o', 'Goto repository settings'),
1188 ('g o', 'Goto repository settings'),
1189 ('g O', 'Goto repository access permissions settings'),
1189 ('g O', 'Goto repository access permissions settings'),
1190 ]
1190 ]
1191 %>
1191 %>
1192 %for key, desc in elems:
1192 %for key, desc in elems:
1193 <tr>
1193 <tr>
1194 <td class="keys">
1194 <td class="keys">
1195 <span class="key tag">${key}</span>
1195 <span class="key tag">${key}</span>
1196 </td>
1196 </td>
1197 <td>${desc}</td>
1197 <td>${desc}</td>
1198 </tr>
1198 </tr>
1199 %endfor
1199 %endfor
1200 </tbody>
1200 </tbody>
1201 </table>
1201 </table>
1202 </div>
1202 </div>
1203 </div>
1203 </div>
1204 <div class="modal-footer">
1204 <div class="modal-footer">
1205 </div>
1205 </div>
1206 </div><!-- /.modal-content -->
1206 </div><!-- /.modal-content -->
1207 </div><!-- /.modal-dialog -->
1207 </div><!-- /.modal-dialog -->
1208 </div><!-- /.modal -->
1208 </div><!-- /.modal -->
@@ -1,537 +1,537 b''
1 <%inherit file="/base/base.mako"/>
1 <%inherit file="/base/base.mako"/>
2 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
2 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${c.repo_name} ${_('New pull request')}
5 ${c.repo_name} ${_('New pull request')}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()"></%def>
8 <%def name="breadcrumbs_links()"></%def>
9
9
10 <%def name="menu_bar_nav()">
10 <%def name="menu_bar_nav()">
11 ${self.menu_items(active='repositories')}
11 ${self.menu_items(active='repositories')}
12 </%def>
12 </%def>
13
13
14 <%def name="menu_bar_subnav()">
14 <%def name="menu_bar_subnav()">
15 ${self.repo_menu(active='showpullrequest')}
15 ${self.repo_menu(active='showpullrequest')}
16 </%def>
16 </%def>
17
17
18 <%def name="main()">
18 <%def name="main()">
19 <div class="box">
19 <div class="box">
20 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
20 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
21
21
22 <div class="box pr-summary">
22 <div class="box pr-summary">
23
23
24 <div class="summary-details block-left">
24 <div class="summary-details block-left">
25
25
26
26
27 <div class="pr-details-title">
27 <div class="pr-details-title">
28 ${_('New pull request')}
28 ${_('New pull request')}
29 </div>
29 </div>
30
30
31 <div class="form" style="padding-top: 10px">
31 <div class="form" style="padding-top: 10px">
32 <!-- fields -->
32 <!-- fields -->
33
33
34 <div class="fields" >
34 <div class="fields" >
35
35
36 <div class="field">
36 <div class="field">
37 <div class="label">
37 <div class="label">
38 <label for="pullrequest_title">${_('Title')}:</label>
38 <label for="pullrequest_title">${_('Title')}:</label>
39 </div>
39 </div>
40 <div class="input">
40 <div class="input">
41 ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")}
41 ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")}
42 </div>
42 </div>
43 <p class="help-block">
43 <p class="help-block">
44 Start the title with WIP: to prevent accidental merge of Work In Progress pull request before it's ready.
44 Start the title with WIP: to prevent accidental merge of Work In Progress pull request before it's ready.
45 </p>
45 </p>
46 </div>
46 </div>
47
47
48 <div class="field">
48 <div class="field">
49 <div class="label label-textarea">
49 <div class="label label-textarea">
50 <label for="pullrequest_desc">${_('Description')}:</label>
50 <label for="pullrequest_desc">${_('Description')}:</label>
51 </div>
51 </div>
52 <div class="textarea text-area">
52 <div class="textarea text-area">
53 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
53 <input id="pr-renderer-input" type="hidden" name="description_renderer" value="${c.visual.default_renderer}">
54 ${dt.markup_form('pullrequest_desc')}
54 ${dt.markup_form('pullrequest_desc')}
55 </div>
55 </div>
56 </div>
56 </div>
57
57
58 <div class="field">
58 <div class="field">
59 <div class="label label-textarea">
59 <div class="label label-textarea">
60 <label for="commit_flow">${_('Commit flow')}:</label>
60 <label for="commit_flow">${_('Commit flow')}:</label>
61 </div>
61 </div>
62
62
63 ## TODO: johbo: Abusing the "content" class here to get the
63 ## TODO: johbo: Abusing the "content" class here to get the
64 ## desired effect. Should be replaced by a proper solution.
64 ## desired effect. Should be replaced by a proper solution.
65
65
66 ##ORG
66 ##ORG
67 <div class="content">
67 <div class="content">
68 <strong>${_('Source repository')}:</strong>
68 <strong>${_('Source repository')}:</strong>
69 ${c.rhodecode_db_repo.description}
69 ${c.rhodecode_db_repo.description}
70 </div>
70 </div>
71 <div class="content">
71 <div class="content">
72 ${h.hidden('source_repo')}
72 ${h.hidden('source_repo')}
73 ${h.hidden('source_ref')}
73 ${h.hidden('source_ref')}
74 </div>
74 </div>
75
75
76 ##OTHER, most Probably the PARENT OF THIS FORK
76 ##OTHER, most Probably the PARENT OF THIS FORK
77 <div class="content">
77 <div class="content">
78 ## filled with JS
78 ## filled with JS
79 <div id="target_repo_desc"></div>
79 <div id="target_repo_desc"></div>
80 </div>
80 </div>
81
81
82 <div class="content">
82 <div class="content">
83 ${h.hidden('target_repo')}
83 ${h.hidden('target_repo')}
84 ${h.hidden('target_ref')}
84 ${h.hidden('target_ref')}
85 <span id="target_ref_loading" style="display: none">
85 <span id="target_ref_loading" style="display: none">
86 ${_('Loading refs...')}
86 ${_('Loading refs...')}
87 </span>
87 </span>
88 </div>
88 </div>
89 </div>
89 </div>
90
90
91 <div class="field">
91 <div class="field">
92 <div class="label label-textarea">
92 <div class="label label-textarea">
93 <label for="pullrequest_submit"></label>
93 <label for="pullrequest_submit"></label>
94 </div>
94 </div>
95 <div class="input">
95 <div class="input">
96 <div class="pr-submit-button">
96 <div class="pr-submit-button">
97 <input id="pr_submit" class="btn" name="save" type="submit" value="${_('Submit Pull Request')}">
97 <input id="pr_submit" class="btn" name="save" type="submit" value="${_('Submit Pull Request')}">
98 </div>
98 </div>
99 <div id="pr_open_message"></div>
99 <div id="pr_open_message"></div>
100 </div>
100 </div>
101 </div>
101 </div>
102
102
103 <div class="pr-spacing-container"></div>
103 <div class="pr-spacing-container"></div>
104 </div>
104 </div>
105 </div>
105 </div>
106 </div>
106 </div>
107 <div>
107 <div>
108 ## AUTHOR
108 ## AUTHOR
109 <div class="reviewers-title block-right">
109 <div class="reviewers-title block-right">
110 <div class="pr-details-title">
110 <div class="pr-details-title">
111 ${_('Author of this pull request')}
111 ${_('Author of this pull request')}
112 </div>
112 </div>
113 </div>
113 </div>
114 <div class="block-right pr-details-content reviewers">
114 <div class="block-right pr-details-content reviewers">
115 <ul class="group_members">
115 <ul class="group_members">
116 <li>
116 <li>
117 ${self.gravatar_with_user(c.rhodecode_user.email, 16, tooltip=True)}
117 ${self.gravatar_with_user(c.rhodecode_user.email, 16, tooltip=True)}
118 </li>
118 </li>
119 </ul>
119 </ul>
120 </div>
120 </div>
121
121
122 ## REVIEW RULES
122 ## REVIEW RULES
123 <div id="review_rules" style="display: none" class="reviewers-title block-right">
123 <div id="review_rules" style="display: none" class="reviewers-title block-right">
124 <div class="pr-details-title">
124 <div class="pr-details-title">
125 ${_('Reviewer rules')}
125 ${_('Reviewer rules')}
126 </div>
126 </div>
127 <div class="pr-reviewer-rules">
127 <div class="pr-reviewer-rules">
128 ## review rules will be appended here, by default reviewers logic
128 ## review rules will be appended here, by default reviewers logic
129 </div>
129 </div>
130 </div>
130 </div>
131
131
132 ## REVIEWERS
132 ## REVIEWERS
133 <div class="reviewers-title block-right">
133 <div class="reviewers-title block-right">
134 <div class="pr-details-title">
134 <div class="pr-details-title">
135 ${_('Pull request reviewers')}
135 ${_('Pull request reviewers')}
136 <span class="calculate-reviewers"> - ${_('loading...')}</span>
136 <span class="calculate-reviewers"> - ${_('loading...')}</span>
137 </div>
137 </div>
138 </div>
138 </div>
139 <div id="reviewers" class="block-right pr-details-content reviewers">
139 <div id="reviewers" class="block-right pr-details-content reviewers">
140 ## members goes here, filled via JS based on initial selection !
140 ## members goes here, filled via JS based on initial selection !
141 <input type="hidden" name="__start__" value="review_members:sequence">
141 <input type="hidden" name="__start__" value="review_members:sequence">
142 <ul id="review_members" class="group_members"></ul>
142 <ul id="review_members" class="group_members"></ul>
143 <input type="hidden" name="__end__" value="review_members:sequence">
143 <input type="hidden" name="__end__" value="review_members:sequence">
144 <div id="add_reviewer_input" class='ac'>
144 <div id="add_reviewer_input" class='ac'>
145 <div class="reviewer_ac">
145 <div class="reviewer_ac">
146 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
146 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
147 <div id="reviewers_container"></div>
147 <div id="reviewers_container"></div>
148 </div>
148 </div>
149 </div>
149 </div>
150 </div>
150 </div>
151 </div>
151 </div>
152 </div>
152 </div>
153 <div class="box">
153 <div class="box">
154 <div>
154 <div>
155 ## overview pulled by ajax
155 ## overview pulled by ajax
156 <div id="pull_request_overview"></div>
156 <div id="pull_request_overview"></div>
157 </div>
157 </div>
158 </div>
158 </div>
159 ${h.end_form()}
159 ${h.end_form()}
160 </div>
160 </div>
161
161
162 <script type="text/javascript">
162 <script type="text/javascript">
163 $(function(){
163 $(function(){
164 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
164 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
165 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
165 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
166 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
166 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
167 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
167 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
168
168
169 var $pullRequestForm = $('#pull_request_form');
169 var $pullRequestForm = $('#pull_request_form');
170 var $pullRequestSubmit = $('#pr_submit', $pullRequestForm);
170 var $pullRequestSubmit = $('#pr_submit', $pullRequestForm);
171 var $sourceRepo = $('#source_repo', $pullRequestForm);
171 var $sourceRepo = $('#source_repo', $pullRequestForm);
172 var $targetRepo = $('#target_repo', $pullRequestForm);
172 var $targetRepo = $('#target_repo', $pullRequestForm);
173 var $sourceRef = $('#source_ref', $pullRequestForm);
173 var $sourceRef = $('#source_ref', $pullRequestForm);
174 var $targetRef = $('#target_ref', $pullRequestForm);
174 var $targetRef = $('#target_ref', $pullRequestForm);
175
175
176 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
176 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
177 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
177 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
178
178
179 var targetRepo = function() { return $targetRepo.eq(0).val() };
179 var targetRepo = function() { return $targetRepo.eq(0).val() };
180 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
180 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
181
181
182 var calculateContainerWidth = function() {
182 var calculateContainerWidth = function() {
183 var maxWidth = 0;
183 var maxWidth = 0;
184 var repoSelect2Containers = ['#source_repo', '#target_repo'];
184 var repoSelect2Containers = ['#source_repo', '#target_repo'];
185 $.each(repoSelect2Containers, function(idx, value) {
185 $.each(repoSelect2Containers, function(idx, value) {
186 $(value).select2('container').width('auto');
186 $(value).select2('container').width('auto');
187 var curWidth = $(value).select2('container').width();
187 var curWidth = $(value).select2('container').width();
188 if (maxWidth <= curWidth) {
188 if (maxWidth <= curWidth) {
189 maxWidth = curWidth;
189 maxWidth = curWidth;
190 }
190 }
191 $.each(repoSelect2Containers, function(idx, value) {
191 $.each(repoSelect2Containers, function(idx, value) {
192 $(value).select2('container').width(maxWidth + 10);
192 $(value).select2('container').width(maxWidth + 10);
193 });
193 });
194 });
194 });
195 };
195 };
196
196
197 var initRefSelection = function(selectedRef) {
197 var initRefSelection = function(selectedRef) {
198 return function(element, callback) {
198 return function(element, callback) {
199 // translate our select2 id into a text, it's a mapping to show
199 // translate our select2 id into a text, it's a mapping to show
200 // simple label when selecting by internal ID.
200 // simple label when selecting by internal ID.
201 var id, refData;
201 var id, refData;
202 if (selectedRef === undefined || selectedRef === null) {
202 if (selectedRef === undefined || selectedRef === null) {
203 id = element.val();
203 id = element.val();
204 refData = element.val().split(':');
204 refData = element.val().split(':');
205
205
206 if (refData.length !== 3){
206 if (refData.length !== 3){
207 refData = ["", "", ""]
207 refData = ["", "", ""]
208 }
208 }
209 } else {
209 } else {
210 id = selectedRef;
210 id = selectedRef;
211 refData = selectedRef.split(':');
211 refData = selectedRef.split(':');
212 }
212 }
213
213
214 var text = refData[1];
214 var text = refData[1];
215 if (refData[0] === 'rev') {
215 if (refData[0] === 'rev') {
216 text = text.substring(0, 12);
216 text = text.substring(0, 12);
217 }
217 }
218
218
219 var data = {id: id, text: text};
219 var data = {id: id, text: text};
220 callback(data);
220 callback(data);
221 };
221 };
222 };
222 };
223
223
224 var formatRefSelection = function(data, container, escapeMarkup) {
224 var formatRefSelection = function(data, container, escapeMarkup) {
225 var prefix = '';
225 var prefix = '';
226 var refData = data.id.split(':');
226 var refData = data.id.split(':');
227 if (refData[0] === 'branch') {
227 if (refData[0] === 'branch') {
228 prefix = '<i class="icon-branch"></i>';
228 prefix = '<i class="icon-branch"></i>';
229 }
229 }
230 else if (refData[0] === 'book') {
230 else if (refData[0] === 'book') {
231 prefix = '<i class="icon-bookmark"></i>';
231 prefix = '<i class="icon-bookmark"></i>';
232 }
232 }
233 else if (refData[0] === 'tag') {
233 else if (refData[0] === 'tag') {
234 prefix = '<i class="icon-tag"></i>';
234 prefix = '<i class="icon-tag"></i>';
235 }
235 }
236
236
237 var originalOption = data.element;
237 var originalOption = data.element;
238 return prefix + escapeMarkup(data.text);
238 return prefix + escapeMarkup(data.text);
239 };formatSelection:
239 };formatSelection:
240
240
241 // custom code mirror
241 // custom code mirror
242 var codeMirrorInstance = $('#pullrequest_desc').get(0).MarkupForm.cm;
242 var codeMirrorInstance = $('#pullrequest_desc').get(0).MarkupForm.cm;
243
243
244 var diffDataHandler = function(data) {
244 var diffDataHandler = function(data) {
245
245
246 $('#pull_request_overview').html(data);
246 $('#pull_request_overview').html(data);
247
247
248 var commitElements = data['commits'];
248 var commitElements = data['commits'];
249 var files = data['files'];
249 var files = data['files'];
250 var added = data['stats'][0]
250 var added = data['stats'][0]
251 var deleted = data['stats'][1]
251 var deleted = data['stats'][1]
252 var commonAncestorId = data['ancestor'];
252 var commonAncestorId = data['ancestor'];
253
253 var _sourceRefType = sourceRef()[0];
254 var prTitleAndDesc = getTitleAndDescription(
254 var _sourceRefName = sourceRef()[1];
255 sourceRef()[1], commitElements, 5);
255 var prTitleAndDesc = getTitleAndDescription(_sourceRefType, _sourceRefName, commitElements, 5);
256
256
257 var title = prTitleAndDesc[0];
257 var title = prTitleAndDesc[0];
258 var proposedDescription = prTitleAndDesc[1];
258 var proposedDescription = prTitleAndDesc[1];
259
259
260 var useGeneratedTitle = (
260 var useGeneratedTitle = (
261 $('#pullrequest_title').hasClass('autogenerated-title') ||
261 $('#pullrequest_title').hasClass('autogenerated-title') ||
262 $('#pullrequest_title').val() === "");
262 $('#pullrequest_title').val() === "");
263
263
264 if (title && useGeneratedTitle) {
264 if (title && useGeneratedTitle) {
265 // use generated title if we haven't specified our own
265 // use generated title if we haven't specified our own
266 $('#pullrequest_title').val(title);
266 $('#pullrequest_title').val(title);
267 $('#pullrequest_title').addClass('autogenerated-title');
267 $('#pullrequest_title').addClass('autogenerated-title');
268
268
269 }
269 }
270
270
271 var useGeneratedDescription = (
271 var useGeneratedDescription = (
272 !codeMirrorInstance._userDefinedValue ||
272 !codeMirrorInstance._userDefinedValue ||
273 codeMirrorInstance.getValue() === "");
273 codeMirrorInstance.getValue() === "");
274
274
275 if (proposedDescription && useGeneratedDescription) {
275 if (proposedDescription && useGeneratedDescription) {
276 // set proposed content, if we haven't defined our own,
276 // set proposed content, if we haven't defined our own,
277 // or we don't have description written
277 // or we don't have description written
278 codeMirrorInstance._userDefinedValue = false; // reset state
278 codeMirrorInstance._userDefinedValue = false; // reset state
279 codeMirrorInstance.setValue(proposedDescription);
279 codeMirrorInstance.setValue(proposedDescription);
280 }
280 }
281
281
282 // refresh our codeMirror so events kicks in and it's change aware
282 // refresh our codeMirror so events kicks in and it's change aware
283 codeMirrorInstance.refresh();
283 codeMirrorInstance.refresh();
284
284
285 var url_data = {
285 var url_data = {
286 'repo_name': targetRepo(),
286 'repo_name': targetRepo(),
287 'target_repo': sourceRepo(),
287 'target_repo': sourceRepo(),
288 'source_ref': targetRef()[2],
288 'source_ref': targetRef()[2],
289 'source_ref_type': 'rev',
289 'source_ref_type': 'rev',
290 'target_ref': sourceRef()[2],
290 'target_ref': sourceRef()[2],
291 'target_ref_type': 'rev',
291 'target_ref_type': 'rev',
292 'merge': true,
292 'merge': true,
293 '_': Date.now() // bypass browser caching
293 '_': Date.now() // bypass browser caching
294 }; // gather the source/target ref and repo here
294 }; // gather the source/target ref and repo here
295 var url = pyroutes.url('repo_compare', url_data);
295 var url = pyroutes.url('repo_compare', url_data);
296
296
297 var msg = '<input id="common_ancestor" type="hidden" name="common_ancestor" value="{0}">'.format(commonAncestorId);
297 var msg = '<input id="common_ancestor" type="hidden" name="common_ancestor" value="{0}">'.format(commonAncestorId);
298 msg += '<input type="hidden" name="__start__" value="revisions:sequence">'
298 msg += '<input type="hidden" name="__start__" value="revisions:sequence">'
299
299
300 $.each(commitElements, function(idx, value) {
300 $.each(commitElements, function(idx, value) {
301 msg += '<input type="hidden" name="revisions" value="{0}">'.format(value["raw_id"]);
301 msg += '<input type="hidden" name="revisions" value="{0}">'.format(value["raw_id"]);
302 });
302 });
303
303
304 msg += '<input type="hidden" name="__end__" value="revisions:sequence">'
304 msg += '<input type="hidden" name="__end__" value="revisions:sequence">'
305 msg += _ngettext(
305 msg += _ngettext(
306 'This pull requests will consist of <strong>{0} commit</strong>.',
306 'This pull requests will consist of <strong>{0} commit</strong>.',
307 'This pull requests will consist of <strong>{0} commits</strong>.',
307 'This pull requests will consist of <strong>{0} commits</strong>.',
308 commitElements.length).format(commitElements.length)
308 commitElements.length).format(commitElements.length)
309
309
310 msg += '\n';
310 msg += '\n';
311 msg += _ngettext(
311 msg += _ngettext(
312 '<strong>{0} file</strong> changed, ',
312 '<strong>{0} file</strong> changed, ',
313 '<strong>{0} files</strong> changed, ',
313 '<strong>{0} files</strong> changed, ',
314 files.length).format(files.length)
314 files.length).format(files.length)
315 msg += '<span class="op-added">{0} lines inserted</span>, <span class="op-deleted">{1} lines deleted</span>.'.format(added, deleted)
315 msg += '<span class="op-added">{0} lines inserted</span>, <span class="op-deleted">{1} lines deleted</span>.'.format(added, deleted)
316
316
317 msg += '\n\n <a class="" id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
317 msg += '\n\n <a class="" id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
318
318
319 if (commitElements.length) {
319 if (commitElements.length) {
320 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
320 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
321 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
321 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
322 }
322 }
323 else {
323 else {
324 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
324 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
325 }
325 }
326
326
327 };
327 };
328
328
329 reviewersController = new ReviewersController();
329 reviewersController = new ReviewersController();
330 reviewersController.diffDataHandler = diffDataHandler;
330 reviewersController.diffDataHandler = diffDataHandler;
331
331
332 var queryTargetRepo = function(self, query) {
332 var queryTargetRepo = function(self, query) {
333 // cache ALL results if query is empty
333 // cache ALL results if query is empty
334 var cacheKey = query.term || '__';
334 var cacheKey = query.term || '__';
335 var cachedData = self.cachedDataSource[cacheKey];
335 var cachedData = self.cachedDataSource[cacheKey];
336
336
337 if (cachedData) {
337 if (cachedData) {
338 query.callback({results: cachedData.results});
338 query.callback({results: cachedData.results});
339 } else {
339 } else {
340 $.ajax({
340 $.ajax({
341 url: pyroutes.url('pullrequest_repo_targets', {'repo_name': templateContext.repo_name}),
341 url: pyroutes.url('pullrequest_repo_targets', {'repo_name': templateContext.repo_name}),
342 data: {query: query.term},
342 data: {query: query.term},
343 dataType: 'json',
343 dataType: 'json',
344 type: 'GET',
344 type: 'GET',
345 success: function(data) {
345 success: function(data) {
346 self.cachedDataSource[cacheKey] = data;
346 self.cachedDataSource[cacheKey] = data;
347 query.callback({results: data.results});
347 query.callback({results: data.results});
348 },
348 },
349 error: function(jqXHR, textStatus, errorThrown) {
349 error: function(jqXHR, textStatus, errorThrown) {
350 var prefix = "Error while fetching entries.\n"
350 var prefix = "Error while fetching entries.\n"
351 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
351 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
352 ajaxErrorSwal(message);
352 ajaxErrorSwal(message);
353 }
353 }
354 });
354 });
355 }
355 }
356 };
356 };
357
357
358 var queryTargetRefs = function(initialData, query) {
358 var queryTargetRefs = function(initialData, query) {
359 var data = {results: []};
359 var data = {results: []};
360 // filter initialData
360 // filter initialData
361 $.each(initialData, function() {
361 $.each(initialData, function() {
362 var section = this.text;
362 var section = this.text;
363 var children = [];
363 var children = [];
364 $.each(this.children, function() {
364 $.each(this.children, function() {
365 if (query.term.length === 0 ||
365 if (query.term.length === 0 ||
366 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
366 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
367 children.push({'id': this.id, 'text': this.text})
367 children.push({'id': this.id, 'text': this.text})
368 }
368 }
369 });
369 });
370 data.results.push({'text': section, 'children': children})
370 data.results.push({'text': section, 'children': children})
371 });
371 });
372 query.callback({results: data.results});
372 query.callback({results: data.results});
373 };
373 };
374
374
375 var Select2Box = function(element, overrides) {
375 var Select2Box = function(element, overrides) {
376 var globalDefaults = {
376 var globalDefaults = {
377 dropdownAutoWidth: true,
377 dropdownAutoWidth: true,
378 containerCssClass: "drop-menu",
378 containerCssClass: "drop-menu",
379 dropdownCssClass: "drop-menu-dropdown"
379 dropdownCssClass: "drop-menu-dropdown"
380 };
380 };
381
381
382 var initSelect2 = function(defaultOptions) {
382 var initSelect2 = function(defaultOptions) {
383 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
383 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
384 element.select2(options);
384 element.select2(options);
385 };
385 };
386
386
387 return {
387 return {
388 initRef: function() {
388 initRef: function() {
389 var defaultOptions = {
389 var defaultOptions = {
390 minimumResultsForSearch: 5,
390 minimumResultsForSearch: 5,
391 formatSelection: formatRefSelection
391 formatSelection: formatRefSelection
392 };
392 };
393
393
394 initSelect2(defaultOptions);
394 initSelect2(defaultOptions);
395 },
395 },
396
396
397 initRepo: function(defaultValue, readOnly) {
397 initRepo: function(defaultValue, readOnly) {
398 var defaultOptions = {
398 var defaultOptions = {
399 initSelection : function (element, callback) {
399 initSelection : function (element, callback) {
400 var data = {id: defaultValue, text: defaultValue};
400 var data = {id: defaultValue, text: defaultValue};
401 callback(data);
401 callback(data);
402 }
402 }
403 };
403 };
404
404
405 initSelect2(defaultOptions);
405 initSelect2(defaultOptions);
406
406
407 element.select2('val', defaultSourceRepo);
407 element.select2('val', defaultSourceRepo);
408 if (readOnly === true) {
408 if (readOnly === true) {
409 element.select2('readonly', true);
409 element.select2('readonly', true);
410 }
410 }
411 }
411 }
412 };
412 };
413 };
413 };
414
414
415 var initTargetRefs = function(refsData, selectedRef) {
415 var initTargetRefs = function(refsData, selectedRef) {
416
416
417 Select2Box($targetRef, {
417 Select2Box($targetRef, {
418 placeholder: "${_('Select commit reference')}",
418 placeholder: "${_('Select commit reference')}",
419 query: function(query) {
419 query: function(query) {
420 queryTargetRefs(refsData, query);
420 queryTargetRefs(refsData, query);
421 },
421 },
422 initSelection : initRefSelection(selectedRef)
422 initSelection : initRefSelection(selectedRef)
423 }).initRef();
423 }).initRef();
424
424
425 if (!(selectedRef === undefined)) {
425 if (!(selectedRef === undefined)) {
426 $targetRef.select2('val', selectedRef);
426 $targetRef.select2('val', selectedRef);
427 }
427 }
428 };
428 };
429
429
430 var targetRepoChanged = function(repoData) {
430 var targetRepoChanged = function(repoData) {
431 // generate new DESC of target repo displayed next to select
431 // generate new DESC of target repo displayed next to select
432 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
432 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
433 $('#target_repo_desc').html(
433 $('#target_repo_desc').html(
434 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
434 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
435 );
435 );
436
436
437 // generate dynamic select2 for refs.
437 // generate dynamic select2 for refs.
438 initTargetRefs(repoData['refs']['select2_refs'],
438 initTargetRefs(repoData['refs']['select2_refs'],
439 repoData['refs']['selected_ref']);
439 repoData['refs']['selected_ref']);
440
440
441 };
441 };
442
442
443 var sourceRefSelect2 = Select2Box($sourceRef, {
443 var sourceRefSelect2 = Select2Box($sourceRef, {
444 placeholder: "${_('Select commit reference')}",
444 placeholder: "${_('Select commit reference')}",
445 query: function(query) {
445 query: function(query) {
446 var initialData = defaultSourceRepoData['refs']['select2_refs'];
446 var initialData = defaultSourceRepoData['refs']['select2_refs'];
447 queryTargetRefs(initialData, query)
447 queryTargetRefs(initialData, query)
448 },
448 },
449 initSelection: initRefSelection()
449 initSelection: initRefSelection()
450 }
450 }
451 );
451 );
452
452
453 var sourceRepoSelect2 = Select2Box($sourceRepo, {
453 var sourceRepoSelect2 = Select2Box($sourceRepo, {
454 query: function(query) {}
454 query: function(query) {}
455 });
455 });
456
456
457 var targetRepoSelect2 = Select2Box($targetRepo, {
457 var targetRepoSelect2 = Select2Box($targetRepo, {
458 cachedDataSource: {},
458 cachedDataSource: {},
459 query: $.debounce(250, function(query) {
459 query: $.debounce(250, function(query) {
460 queryTargetRepo(this, query);
460 queryTargetRepo(this, query);
461 }),
461 }),
462 formatResult: formatRepoResult
462 formatResult: formatRepoResult
463 });
463 });
464
464
465 sourceRefSelect2.initRef();
465 sourceRefSelect2.initRef();
466
466
467 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
467 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
468
468
469 targetRepoSelect2.initRepo(defaultTargetRepo, false);
469 targetRepoSelect2.initRepo(defaultTargetRepo, false);
470
470
471 $sourceRef.on('change', function(e){
471 $sourceRef.on('change', function(e){
472 reviewersController.loadDefaultReviewers(
472 reviewersController.loadDefaultReviewers(
473 sourceRepo(), sourceRef(), targetRepo(), targetRef());
473 sourceRepo(), sourceRef(), targetRepo(), targetRef());
474 });
474 });
475
475
476 $targetRef.on('change', function(e){
476 $targetRef.on('change', function(e){
477 reviewersController.loadDefaultReviewers(
477 reviewersController.loadDefaultReviewers(
478 sourceRepo(), sourceRef(), targetRepo(), targetRef());
478 sourceRepo(), sourceRef(), targetRepo(), targetRef());
479 });
479 });
480
480
481 $targetRepo.on('change', function(e){
481 $targetRepo.on('change', function(e){
482 var repoName = $(this).val();
482 var repoName = $(this).val();
483 calculateContainerWidth();
483 calculateContainerWidth();
484 $targetRef.select2('destroy');
484 $targetRef.select2('destroy');
485 $('#target_ref_loading').show();
485 $('#target_ref_loading').show();
486
486
487 $.ajax({
487 $.ajax({
488 url: pyroutes.url('pullrequest_repo_refs',
488 url: pyroutes.url('pullrequest_repo_refs',
489 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
489 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
490 data: {},
490 data: {},
491 dataType: 'json',
491 dataType: 'json',
492 type: 'GET',
492 type: 'GET',
493 success: function(data) {
493 success: function(data) {
494 $('#target_ref_loading').hide();
494 $('#target_ref_loading').hide();
495 targetRepoChanged(data);
495 targetRepoChanged(data);
496 },
496 },
497 error: function(jqXHR, textStatus, errorThrown) {
497 error: function(jqXHR, textStatus, errorThrown) {
498 var prefix = "Error while fetching entries.\n"
498 var prefix = "Error while fetching entries.\n"
499 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
499 var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix);
500 ajaxErrorSwal(message);
500 ajaxErrorSwal(message);
501 }
501 }
502 })
502 })
503
503
504 });
504 });
505
505
506 $pullRequestForm.on('submit', function(e){
506 $pullRequestForm.on('submit', function(e){
507 // Flush changes into textarea
507 // Flush changes into textarea
508 codeMirrorInstance.save();
508 codeMirrorInstance.save();
509 prButtonLock(true, null, 'all');
509 prButtonLock(true, null, 'all');
510 $pullRequestSubmit.val(_gettext('Please wait creating pull request...'));
510 $pullRequestSubmit.val(_gettext('Please wait creating pull request...'));
511 });
511 });
512
512
513 prButtonLock(true, "${_('Please select source and target')}", 'all');
513 prButtonLock(true, "${_('Please select source and target')}", 'all');
514
514
515 // auto-load on init, the target refs select2
515 // auto-load on init, the target refs select2
516 calculateContainerWidth();
516 calculateContainerWidth();
517 targetRepoChanged(defaultTargetRepoData);
517 targetRepoChanged(defaultTargetRepoData);
518
518
519 $('#pullrequest_title').on('keyup', function(e){
519 $('#pullrequest_title').on('keyup', function(e){
520 $(this).removeClass('autogenerated-title');
520 $(this).removeClass('autogenerated-title');
521 });
521 });
522
522
523 % if c.default_source_ref:
523 % if c.default_source_ref:
524 // in case we have a pre-selected value, use it now
524 // in case we have a pre-selected value, use it now
525 $sourceRef.select2('val', '${c.default_source_ref}');
525 $sourceRef.select2('val', '${c.default_source_ref}');
526
526
527
527
528 // default reviewers
528 // default reviewers
529 reviewersController.loadDefaultReviewers(
529 reviewersController.loadDefaultReviewers(
530 sourceRepo(), sourceRef(), targetRepo(), targetRef());
530 sourceRepo(), sourceRef(), targetRepo(), targetRef());
531 % endif
531 % endif
532
532
533 ReviewerAutoComplete('#user');
533 ReviewerAutoComplete('#user');
534 });
534 });
535 </script>
535 </script>
536
536
537 </%def>
537 </%def>
General Comments 0
You need to be logged in to leave comments. Login now