pullrequests.js
1173 lines
| 36.1 KiB
| application/javascript
|
JavascriptLexer
r4306 | // # Copyright (C) 2010-2020 RhodeCode GmbH | |||
r1 | // # | |||
// # This program is free software: you can redistribute it and/or modify | ||||
// # it under the terms of the GNU Affero General Public License, version 3 | ||||
// # (only), as published by the Free Software Foundation. | ||||
// # | ||||
// # This program is distributed in the hope that it will be useful, | ||||
// # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
// # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||||
// # GNU General Public License for more details. | ||||
// # | ||||
// # You should have received a copy of the GNU Affero General Public License | ||||
// # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
// # | ||||
// # This program is dual-licensed. If you wish to learn more about the | ||||
// # RhodeCode Enterprise Edition, including its added features, Support services, | ||||
// # and proprietary license terms, please see https://rhodecode.com/licenses/ | ||||
r1769 | ||||
var prButtonLockChecks = { | ||||
'compare': false, | ||||
'reviewers': false | ||||
}; | ||||
r1 | /** | |||
r1769 | * lock button until all checks and loads are made. E.g reviewer calculation | |||
* should prevent from submitting a PR | ||||
* @param lockEnabled | ||||
* @param msg | ||||
* @param scope | ||||
r1 | */ | |||
r1769 | var prButtonLock = function(lockEnabled, msg, scope) { | |||
scope = scope || 'all'; | ||||
if (scope == 'all'){ | ||||
prButtonLockChecks['compare'] = !lockEnabled; | ||||
prButtonLockChecks['reviewers'] = !lockEnabled; | ||||
} else if (scope == 'compare') { | ||||
prButtonLockChecks['compare'] = !lockEnabled; | ||||
} else if (scope == 'reviewers'){ | ||||
prButtonLockChecks['reviewers'] = !lockEnabled; | ||||
} | ||||
var checksMeet = prButtonLockChecks.compare && prButtonLockChecks.reviewers; | ||||
if (lockEnabled) { | ||||
r2806 | $('#pr_submit').attr('disabled', 'disabled'); | |||
r1769 | } | |||
else if (checksMeet) { | ||||
r2806 | $('#pr_submit').removeAttr('disabled'); | |||
r1769 | } | |||
r1 | ||||
r1769 | if (msg) { | |||
$('#pr_open_message').html(msg); | ||||
} | ||||
}; | ||||
r1 | ||||
r1769 | ||||
/** | ||||
Generate Title and Description for a PullRequest. | ||||
In case of 1 commits, the title and description is that one commit | ||||
in case of multiple commits, we iterate on them with max N number of commits, | ||||
and build description in a form | ||||
- commitN | ||||
- commitN+1 | ||||
... | ||||
Title is then constructed from branch names, or other references, | ||||
replacing '-' and '_' into spaces | ||||
* @param sourceRef | ||||
* @param elements | ||||
* @param limit | ||||
* @returns {*[]} | ||||
*/ | ||||
r4433 | var getTitleAndDescription = function(sourceRefType, sourceRef, elements, limit) { | |||
r1769 | var title = ''; | |||
var desc = ''; | ||||
$.each($(elements).get().reverse().slice(0, limit), function(idx, value) { | ||||
r4346 | var rawMessage = value['message']; | |||
r1769 | desc += '- ' + rawMessage.split('\n')[0].replace(/\n+$/, "") + '\n'; | |||
}); | ||||
// only 1 commit, use commit message as title | ||||
if (elements.length === 1) { | ||||
r4346 | var rawMessage = elements[0]['message']; | |||
r4163 | title = rawMessage.split('\n')[0]; | |||
r1769 | } | |||
else { | ||||
// use reference name | ||||
r4433 | var normalizedRef = sourceRef.replace(/-/g, ' ').replace(/_/g, ' ').capitalizeFirstLetter() | |||
var refType = sourceRefType; | ||||
title = 'Changes from {0}: {1}'.format(refType, normalizedRef); | ||||
r1769 | } | |||
return [title, desc] | ||||
r1 | }; | |||
r1769 | ||||
r4500 | window.ReviewersController = function () { | |||
r1769 | var self = this; | |||
r4500 | this.$loadingIndicator = $('.calculate-reviewers'); | |||
r1769 | this.$reviewRulesContainer = $('#review_rules'); | |||
this.$rulesList = this.$reviewRulesContainer.find('.pr-reviewer-rules'); | ||||
r4482 | this.$userRule = $('.pr-user-rule-container'); | |||
r1769 | this.$reviewMembers = $('#review_members'); | |||
r4500 | this.$observerMembers = $('#observer_members'); | |||
r1769 | this.currentRequest = null; | |||
r4346 | this.diffData = null; | |||
r4482 | this.enabledRules = []; | |||
r4500 | // sync with db.py entries | |||
this.ROLE_REVIEWER = 'reviewer'; | ||||
this.ROLE_OBSERVER = 'observer' | ||||
r4482 | ||||
r4346 | //dummy handler, we might register our own later | |||
r4500 | this.diffDataHandler = function (data) {}; | |||
r1769 | ||||
r4500 | this.defaultForbidUsers = function () { | |||
r1769 | return [ | |||
r4346 | { | |||
'username': 'default', | ||||
'user_id': templateContext.default_user.user_id | ||||
} | ||||
r1769 | ]; | |||
}; | ||||
r4500 | // init default forbidden users | |||
this.forbidUsers = this.defaultForbidUsers(); | ||||
r4346 | this.hideReviewRules = function () { | |||
r1769 | self.$reviewRulesContainer.hide(); | |||
r4482 | $(self.$userRule.selector).hide(); | |||
r1769 | }; | |||
r4346 | this.showReviewRules = function () { | |||
r1769 | self.$reviewRulesContainer.show(); | |||
r4482 | $(self.$userRule.selector).show(); | |||
r1769 | }; | |||
r4346 | this.addRule = function (ruleText) { | |||
r1769 | self.showReviewRules(); | |||
r4482 | self.enabledRules.push(ruleText); | |||
r1769 | return '<div>- {0}</div>'.format(ruleText) | |||
}; | ||||
r4500 | this.increaseCounter = function(role) { | |||
if (role === self.ROLE_REVIEWER) { | ||||
var $elem = $('#reviewers-cnt') | ||||
var cnt = parseInt($elem.data('count') || 0) | ||||
cnt +=1 | ||||
$elem.html(cnt); | ||||
$elem.data('count', cnt); | ||||
} | ||||
else if (role === self.ROLE_OBSERVER) { | ||||
var $elem = $('#observers-cnt'); | ||||
var cnt = parseInt($elem.data('count') || 0) | ||||
cnt +=1 | ||||
$elem.html(cnt); | ||||
$elem.data('count', cnt); | ||||
} | ||||
} | ||||
this.resetCounter = function () { | ||||
var $elem = $('#reviewers-cnt'); | ||||
$elem.data('count', 0); | ||||
$elem.html(0); | ||||
var $elem = $('#observers-cnt'); | ||||
$elem.data('count', 0); | ||||
$elem.html(0); | ||||
} | ||||
r4346 | this.loadReviewRules = function (data) { | |||
self.diffData = data; | ||||
r1769 | // reset forbidden Users | |||
r4500 | this.forbidUsers = self.defaultForbidUsers(); | |||
r1769 | ||||
// reset state of review rules | ||||
self.$rulesList.html(''); | ||||
if (!data || data.rules === undefined || $.isEmptyObject(data.rules)) { | ||||
// default rule, case for older repo that don't have any rules stored | ||||
self.$rulesList.append( | ||||
r4559 | self.addRule(_gettext('All reviewers must vote.')) | |||
r1769 | ); | |||
r4500 | return self.forbidUsers | |||
r1769 | } | |||
r4559 | if (data.rules.forbid_adding_reviewers) { | |||
$('#add_reviewer_input').remove(); | ||||
r1769 | } | |||
r2484 | ||||
r4559 | if (data.rules_data !== undefined && data.rules_data.forbidden_users !== undefined) { | |||
$.each(data.rules_data.forbidden_users, function(idx, val){ | ||||
self.forbidUsers.push(val) | ||||
}) | ||||
r1769 | } | |||
r4482 | ||||
r4559 | if (data.rules_humanized !== undefined && data.rules_humanized.length > 0) { | |||
$.each(data.rules_humanized, function(idx, val) { | ||||
self.$rulesList.append( | ||||
self.addRule(val) | ||||
) | ||||
}) | ||||
} else { | ||||
// we don't have any rules set, so we inform users about it | ||||
r1769 | self.$rulesList.append( | |||
r4559 | self.addRule(_gettext('No additional review rules set.')) | |||
r1769 | ) | |||
} | ||||
r4482 | ||||
r4500 | return self.forbidUsers | |||
r1769 | }; | |||
r4500 | this.emptyTables = function () { | |||
self.emptyReviewersTable(); | ||||
self.emptyObserversTable(); | ||||
// Also reset counters. | ||||
self.resetCounter(); | ||||
} | ||||
this.emptyReviewersTable = function (withText) { | ||||
self.$reviewMembers.empty(); | ||||
if (withText !== undefined) { | ||||
self.$reviewMembers.html(withText) | ||||
} | ||||
}; | ||||
this.emptyObserversTable = function (withText) { | ||||
self.$observerMembers.empty(); | ||||
if (withText !== undefined) { | ||||
self.$observerMembers.html(withText) | ||||
} | ||||
} | ||||
r4346 | this.loadDefaultReviewers = function (sourceRepo, sourceRef, targetRepo, targetRef) { | |||
r1769 | ||||
if (self.currentRequest) { | ||||
r4346 | // make sure we cleanup old running requests before triggering this again | |||
r1769 | self.currentRequest.abort(); | |||
r821 | } | |||
r1769 | ||||
r4500 | self.$loadingIndicator.show(); | |||
// reset reviewer/observe members | ||||
self.emptyTables(); | ||||
r1769 | ||||
prButtonLock(true, null, 'reviewers'); | ||||
$('#user').hide(); // hide user autocomplete before load | ||||
r4500 | $('#observer').hide(); //hide observer autocomplete before load | |||
r1769 | ||||
r4346 | // lock PR button, so we cannot send PR before it's calculated | |||
prButtonLock(true, _gettext('Loading diff ...'), 'compare'); | ||||
r2555 | if (sourceRef.length !== 3 || targetRef.length !== 3) { | |||
// don't load defaults in case we're missing some refs... | ||||
r4500 | self.$loadingIndicator.hide(); | |||
r2555 | return | |||
} | ||||
r1769 | var url = pyroutes.url('repo_default_reviewers_data', | |||
r4346 | { | |||
'repo_name': templateContext.repo_name, | ||||
'source_repo': sourceRepo, | ||||
r4509 | 'source_ref_type': sourceRef[0], | |||
'source_ref_name': sourceRef[1], | ||||
r4346 | 'source_ref': sourceRef[2], | |||
'target_repo': targetRepo, | ||||
r4509 | 'target_ref': targetRef[2], | |||
r4584 | 'target_ref_type': targetRef[0], | |||
'target_ref_name': targetRef[1] | ||||
r4346 | }); | |||
r1769 | ||||
r4346 | self.currentRequest = $.ajax({ | |||
url: url, | ||||
headers: {'X-PARTIAL-XHR': true}, | ||||
type: 'GET', | ||||
success: function (data) { | ||||
r1769 | self.currentRequest = null; | |||
// review rules | ||||
self.loadReviewRules(data); | ||||
r4520 | var diffHandled = self.handleDiffData(data["diff_info"]); | |||
if (diffHandled === false) { | ||||
return | ||||
} | ||||
r1769 | ||||
for (var i = 0; i < data.reviewers.length; i++) { | ||||
r4346 | var reviewer = data.reviewers[i]; | |||
r4500 | // load reviewer rules from the repo data | |||
self.addMember(reviewer, reviewer.reasons, reviewer.mandatory, reviewer.role); | ||||
r1769 | } | |||
r4500 | ||||
self.$loadingIndicator.hide(); | ||||
r1769 | prButtonLock(false, null, 'reviewers'); | |||
r4500 | ||||
$('#user').show(); // show user autocomplete before load | ||||
$('#observer').show(); // show observer autocomplete before load | ||||
r4346 | ||||
var commitElements = data["diff_info"]['commits']; | ||||
r4485 | ||||
r4346 | if (commitElements.length === 0) { | |||
r4485 | var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format( | |||
_gettext('There are no commits to merge.')); | ||||
prButtonLock(true, noCommitsMsg, 'all'); | ||||
r4346 | ||||
} else { | ||||
// un-lock PR button, so we cannot send PR before it's calculated | ||||
prButtonLock(false, null, 'compare'); | ||||
} | ||||
}, | ||||
error: function (jqXHR, textStatus, errorThrown) { | ||||
r4500 | var prefix = "Loading diff and reviewers/observers failed\n" | |||
r4346 | var message = formatErrorMessage(jqXHR, textStatus, errorThrown, prefix); | |||
ajaxErrorSwal(message); | ||||
} | ||||
}); | ||||
r1769 | }; | |||
// check those, refactor | ||||
r4500 | this.removeMember = function (reviewer_id, mark_delete) { | |||
r1769 | var reviewer = $('#reviewer_{0}'.format(reviewer_id)); | |||
r4346 | if (typeof (mark_delete) === undefined) { | |||
r1769 | mark_delete = false; | |||
} | ||||
r4346 | if (mark_delete === true) { | |||
if (reviewer) { | ||||
r1769 | // now delete the input | |||
$('#reviewer_{0} input'.format(reviewer_id)).remove(); | ||||
r4500 | $('#reviewer_{0}_rules input'.format(reviewer_id)).remove(); | |||
r1769 | // mark as to-delete | |||
var obj = $('#reviewer_{0}_name'.format(reviewer_id)); | ||||
obj.addClass('to-delete'); | ||||
r4346 | obj.css({"text-decoration": "line-through", "opacity": 0.5}); | |||
r1769 | } | |||
r4346 | } else { | |||
r1769 | $('#reviewer_{0}'.format(reviewer_id)).remove(); | |||
} | ||||
}; | ||||
r4346 | ||||
r4500 | this.addMember = function (reviewer_obj, reasons, mandatory, role) { | |||
r1769 | ||||
r2484 | var id = reviewer_obj.user_id; | |||
var username = reviewer_obj.username; | ||||
r4500 | reasons = reasons || []; | |||
mandatory = mandatory || false; | ||||
role = role || self.ROLE_REVIEWER | ||||
r873 | ||||
r4500 | // register current set IDS to check if we don't have this ID already in | |||
// and prevent duplicates | ||||
r2484 | var currentIds = []; | |||
r4485 | ||||
r4500 | $.each($('.reviewer_entry'), function (index, value) { | |||
r4485 | currentIds.push($(value).data('reviewerUserId')) | |||
}) | ||||
r1769 | ||||
r4346 | var userAllowedReview = function (userId) { | |||
r1769 | var allowed = true; | |||
r4500 | $.each(self.forbidUsers, function (index, member_data) { | |||
r1769 | if (parseInt(userId) === member_data['user_id']) { | |||
allowed = false; | ||||
return false // breaks the loop | ||||
} | ||||
}); | ||||
return allowed | ||||
}; | ||||
var userAllowed = userAllowedReview(id); | ||||
r4500 | ||||
r4346 | if (!userAllowed) { | |||
alert(_gettext('User `{0}` not allowed to be a reviewer').format(username)); | ||||
r2484 | } else { | |||
// only add if it's not there | ||||
r4485 | var alreadyReviewer = currentIds.indexOf(id) != -1; | |||
r1769 | ||||
r2484 | if (alreadyReviewer) { | |||
r4500 | alert(_gettext('User `{0}` already in reviewers/observers').format(username)); | |||
r2484 | } else { | |||
r4500 | ||||
r4485 | var reviewerEntry = renderTemplate('reviewMemberEntry', { | |||
r4346 | 'member': reviewer_obj, | |||
'mandatory': mandatory, | ||||
r4500 | 'role': role, | |||
r4482 | 'reasons': reasons, | |||
r4346 | 'allowed_to_update': true, | |||
'review_status': 'not_reviewed', | ||||
'review_status_label': _gettext('Not Reviewed'), | ||||
r4482 | 'user_group': reviewer_obj.user_group, | |||
'create': true, | ||||
r4485 | 'rule_show': true, | |||
r4595 | 'rhodecode_user': templateContext.rhodecode_user | |||
r4485 | }) | |||
r4500 | ||||
if (role === self.ROLE_REVIEWER) { | ||||
$(self.$reviewMembers.selector).append(reviewerEntry); | ||||
self.increaseCounter(self.ROLE_REVIEWER); | ||||
$('#reviewer-empty-msg').remove() | ||||
} | ||||
else if (role === self.ROLE_OBSERVER) { | ||||
$(self.$observerMembers.selector).append(reviewerEntry); | ||||
self.increaseCounter(self.ROLE_OBSERVER); | ||||
$('#observer-empty-msg').remove(); | ||||
} | ||||
r4026 | tooltipActivate(); | |||
r2484 | } | |||
r1769 | } | |||
}; | ||||
r4500 | this.updateReviewers = function (repo_name, pull_request_id, role) { | |||
if (role === 'reviewer') { | ||||
var postData = $('#reviewers input').serialize(); | ||||
_updatePullRequest(repo_name, pull_request_id, postData); | ||||
} else if (role === 'observer') { | ||||
var postData = $('#observers input').serialize(); | ||||
_updatePullRequest(repo_name, pull_request_id, postData); | ||||
} | ||||
r1769 | }; | |||
r1 | ||||
r4346 | this.handleDiffData = function (data) { | |||
r4520 | return self.diffDataHandler(data) | |||
r4346 | } | |||
r1 | }; | |||
r1769 | ||||
r1 | var _updatePullRequest = function(repo_name, pull_request_id, postData) { | |||
var url = pyroutes.url( | ||||
'pullrequest_update', | ||||
{"repo_name": repo_name, "pull_request_id": pull_request_id}); | ||||
r873 | if (typeof postData === 'string' ) { | |||
postData += '&csrf_token=' + CSRF_TOKEN; | ||||
} else { | ||||
postData.csrf_token = CSRF_TOKEN; | ||||
} | ||||
r4101 | ||||
r1 | var success = function(o) { | |||
r4101 | var redirectUrl = o['redirect_url']; | |||
if (redirectUrl !== undefined && redirectUrl !== null && redirectUrl !== '') { | ||||
window.location = redirectUrl; | ||||
} else { | ||||
window.location.reload(); | ||||
} | ||||
r1 | }; | |||
r4101 | ||||
r1 | ajaxPOST(url, postData, success); | |||
}; | ||||
/** | ||||
* PULL REQUEST update commits | ||||
*/ | ||||
r4101 | var updateCommits = function(repo_name, pull_request_id, force) { | |||
r1 | var postData = { | |||
r4101 | 'update_commits': true | |||
}; | ||||
if (force !== undefined && force === true) { | ||||
postData['force_refresh'] = true | ||||
} | ||||
r1 | _updatePullRequest(repo_name, pull_request_id, postData); | |||
}; | ||||
/** | ||||
* PULL REQUEST edit info | ||||
*/ | ||||
r2903 | var editPullRequest = function(repo_name, pull_request_id, title, description, renderer) { | |||
r1 | var url = pyroutes.url( | |||
'pullrequest_update', | ||||
{"repo_name": repo_name, "pull_request_id": pull_request_id}); | ||||
var postData = { | ||||
'title': title, | ||||
'description': description, | ||||
r2903 | 'description_renderer': renderer, | |||
r1 | 'edit_pull_request': true, | |||
'csrf_token': CSRF_TOKEN | ||||
}; | ||||
var success = function(o) { | ||||
window.location.reload(); | ||||
}; | ||||
ajaxPOST(url, postData, success); | ||||
}; | ||||
/** | ||||
r4500 | * autocomplete handler for reviewers/observers | |||
r1 | */ | |||
r4500 | var autoCompleteHandler = function (inputId, controller, role) { | |||
return function (element, data) { | ||||
r2484 | var mandatory = false; | |||
r4500 | var reasons = [_gettext('added manually by "{0}"').format( | |||
templateContext.rhodecode_user.username)]; | ||||
r1678 | ||||
r2484 | // add whole user groups | |||
r1678 | if (data.value_type == 'user_group') { | |||
reasons.push(_gettext('member of "{0}"').format(data.value_display)); | ||||
r4500 | $.each(data.members, function (index, member_data) { | |||
r2484 | var reviewer = member_data; | |||
reviewer['user_id'] = member_data['id']; | ||||
reviewer['gravatar_link'] = member_data['icon_link']; | ||||
reviewer['user_link'] = member_data['profile_link']; | ||||
reviewer['rules'] = []; | ||||
r4500 | controller.addMember(reviewer, reasons, mandatory, role); | |||
r1678 | }) | |||
r2484 | } | |||
// add single user | ||||
else { | ||||
var reviewer = data; | ||||
reviewer['user_id'] = data['id']; | ||||
reviewer['gravatar_link'] = data['icon_link']; | ||||
reviewer['user_link'] = data['profile_link']; | ||||
reviewer['rules'] = []; | ||||
r4500 | controller.addMember(reviewer, reasons, mandatory, role); | |||
r1678 | } | |||
r4500 | $(inputId).val(''); | |||
r1 | } | |||
r4500 | } | |||
/** | ||||
* Reviewer autocomplete | ||||
*/ | ||||
var ReviewerAutoComplete = function (inputId, controller) { | ||||
var self = this; | ||||
self.controller = controller; | ||||
self.inputId = inputId; | ||||
var handler = autoCompleteHandler(inputId, controller, controller.ROLE_REVIEWER); | ||||
$(inputId).autocomplete({ | ||||
serviceUrl: pyroutes.url('user_autocomplete_data'), | ||||
minChars: 2, | ||||
maxHeight: 400, | ||||
deferRequestBy: 300, //miliseconds | ||||
showNoSuggestionNotice: true, | ||||
tabDisabled: true, | ||||
autoSelectFirst: true, | ||||
params: { | ||||
user_id: templateContext.rhodecode_user.user_id, | ||||
user_groups: true, | ||||
user_groups_expand: true, | ||||
skip_default_user: true | ||||
}, | ||||
formatResult: autocompleteFormatResult, | ||||
lookupFilter: autocompleteFilterResult, | ||||
onSelect: handler | ||||
}); | ||||
r1 | }; | |||
r1371 | ||||
r4500 | /** | |||
* Observers autocomplete | ||||
*/ | ||||
var ObserverAutoComplete = function(inputId, controller) { | ||||
var self = this; | ||||
self.controller = controller; | ||||
self.inputId = inputId; | ||||
var handler = autoCompleteHandler(inputId, controller, controller.ROLE_OBSERVER); | ||||
$(inputId).autocomplete({ | ||||
serviceUrl: pyroutes.url('user_autocomplete_data'), | ||||
minChars: 2, | ||||
maxHeight: 400, | ||||
deferRequestBy: 300, //miliseconds | ||||
showNoSuggestionNotice: true, | ||||
tabDisabled: true, | ||||
autoSelectFirst: true, | ||||
params: { | ||||
user_id: templateContext.rhodecode_user.user_id, | ||||
user_groups: true, | ||||
user_groups_expand: true, | ||||
skip_default_user: true | ||||
}, | ||||
formatResult: autocompleteFormatResult, | ||||
lookupFilter: autocompleteFilterResult, | ||||
onSelect: handler | ||||
}); | ||||
} | ||||
r1371 | ||||
r4485 | window.VersionController = function () { | |||
r1371 | var self = this; | |||
this.$verSource = $('input[name=ver_source]'); | ||||
this.$verTarget = $('input[name=ver_target]'); | ||||
this.$showVersionDiff = $('#show-version-diff'); | ||||
this.adjustRadioSelectors = function (curNode) { | ||||
var getVal = function (item) { | ||||
r4500 | if (item === 'latest') { | |||
r1371 | return Number.MAX_SAFE_INTEGER | |||
} | ||||
else { | ||||
return parseInt(item) | ||||
} | ||||
}; | ||||
var curVal = getVal($(curNode).val()); | ||||
var cleared = false; | ||||
$.each(self.$verSource, function (index, value) { | ||||
var elVal = getVal($(value).val()); | ||||
if (elVal > curVal) { | ||||
if ($(value).is(':checked')) { | ||||
cleared = true; | ||||
} | ||||
$(value).attr('disabled', 'disabled'); | ||||
$(value).removeAttr('checked'); | ||||
$(value).css({'opacity': 0.1}); | ||||
} | ||||
else { | ||||
$(value).css({'opacity': 1}); | ||||
$(value).removeAttr('disabled'); | ||||
} | ||||
}); | ||||
if (cleared) { | ||||
// if we unchecked an active, set the next one to same loc. | ||||
$(this.$verSource).filter('[value={0}]'.format( | ||||
curVal)).attr('checked', 'checked'); | ||||
} | ||||
self.setLockAction(false, | ||||
$(curNode).data('verPos'), | ||||
$(this.$verSource).filter(':checked').data('verPos') | ||||
); | ||||
}; | ||||
this.attachVersionListener = function () { | ||||
self.$verTarget.change(function (e) { | ||||
self.adjustRadioSelectors(this) | ||||
}); | ||||
self.$verSource.change(function (e) { | ||||
self.adjustRadioSelectors(self.$verTarget.filter(':checked')) | ||||
}); | ||||
}; | ||||
this.init = function () { | ||||
var curNode = self.$verTarget.filter(':checked'); | ||||
self.adjustRadioSelectors(curNode); | ||||
self.setLockAction(true); | ||||
self.attachVersionListener(); | ||||
}; | ||||
this.setLockAction = function (state, selectedVersion, otherVersion) { | ||||
var $showVersionDiff = this.$showVersionDiff; | ||||
if (state) { | ||||
$showVersionDiff.attr('disabled', 'disabled'); | ||||
$showVersionDiff.addClass('disabled'); | ||||
$showVersionDiff.html($showVersionDiff.data('labelTextLocked')); | ||||
} | ||||
else { | ||||
$showVersionDiff.removeAttr('disabled'); | ||||
$showVersionDiff.removeClass('disabled'); | ||||
if (selectedVersion == otherVersion) { | ||||
$showVersionDiff.html($showVersionDiff.data('labelTextShow')); | ||||
} else { | ||||
$showVersionDiff.html($showVersionDiff.data('labelTextDiff')); | ||||
} | ||||
} | ||||
}; | ||||
this.showVersionDiff = function () { | ||||
var target = self.$verTarget.filter(':checked'); | ||||
var source = self.$verSource.filter(':checked'); | ||||
if (target.val() && source.val()) { | ||||
var params = { | ||||
'pull_request_id': templateContext.pull_request_data.pull_request_id, | ||||
'repo_name': templateContext.repo_name, | ||||
'version': target.val(), | ||||
'from_version': source.val() | ||||
}; | ||||
window.location = pyroutes.url('pullrequest_show', params) | ||||
} | ||||
return false; | ||||
}; | ||||
this.toggleVersionView = function (elem) { | ||||
if (this.$showVersionDiff.is(':visible')) { | ||||
$('.version-pr').hide(); | ||||
this.$showVersionDiff.hide(); | ||||
$(elem).html($(elem).data('toggleOn')) | ||||
} else { | ||||
$('.version-pr').show(); | ||||
this.$showVersionDiff.show(); | ||||
$(elem).html($(elem).data('toggleOff')) | ||||
} | ||||
return false | ||||
r4136 | }; | |||
r4101 | }; | |||
r4485 | window.UpdatePrController = function () { | |||
r4101 | var self = this; | |||
this.$updateCommits = $('#update_commits'); | ||||
this.$updateCommitsSwitcher = $('#update_commits_switcher'); | ||||
this.lockUpdateButton = function (label) { | ||||
self.$updateCommits.attr('disabled', 'disabled'); | ||||
self.$updateCommitsSwitcher.attr('disabled', 'disabled'); | ||||
self.$updateCommits.addClass('disabled'); | ||||
self.$updateCommitsSwitcher.addClass('disabled'); | ||||
self.$updateCommits.removeClass('btn-primary'); | ||||
self.$updateCommitsSwitcher.removeClass('btn-primary'); | ||||
self.$updateCommits.text(_gettext(label)); | ||||
}; | ||||
this.isUpdateLocked = function () { | ||||
return self.$updateCommits.attr('disabled') !== undefined; | ||||
}; | ||||
this.updateCommits = function (curNode) { | ||||
if (self.isUpdateLocked()) { | ||||
return | ||||
} | ||||
self.lockUpdateButton(_gettext('Updating...')); | ||||
updateCommits( | ||||
templateContext.repo_name, | ||||
templateContext.pull_request_data.pull_request_id); | ||||
}; | ||||
this.forceUpdateCommits = function () { | ||||
if (self.isUpdateLocked()) { | ||||
return | ||||
} | ||||
self.lockUpdateButton(_gettext('Force updating...')); | ||||
var force = true; | ||||
updateCommits( | ||||
templateContext.repo_name, | ||||
templateContext.pull_request_data.pull_request_id, force); | ||||
}; | ||||
r4485 | }; | |||
r4500 | ||||
r4485 | /** | |||
* Reviewer display panel | ||||
*/ | ||||
window.ReviewersPanel = { | ||||
editButton: null, | ||||
closeButton: null, | ||||
addButton: null, | ||||
removeButtons: null, | ||||
reviewRules: null, | ||||
setReviewers: null, | ||||
r4503 | controller: null, | |||
r4485 | ||||
setSelectors: function () { | ||||
var self = this; | ||||
self.editButton = $('#open_edit_reviewers'); | ||||
self.closeButton =$('#close_edit_reviewers'); | ||||
self.addButton = $('#add_reviewer'); | ||||
self.removeButtons = $('.reviewer_member_remove,.reviewer_member_mandatory_remove'); | ||||
}, | ||||
r4503 | init: function (controller, reviewRules, setReviewers) { | |||
r4485 | var self = this; | |||
self.setSelectors(); | ||||
r4503 | self.controller = controller; | |||
self.reviewRules = reviewRules; | ||||
self.setReviewers = setReviewers; | ||||
r4485 | ||||
r4503 | self.editButton.on('click', function (e) { | |||
r4485 | self.edit(); | |||
}); | ||||
r4503 | self.closeButton.on('click', function (e) { | |||
r4485 | self.close(); | |||
self.renderReviewers(); | ||||
}); | ||||
self.renderReviewers(); | ||||
}, | ||||
renderReviewers: function () { | ||||
r4503 | var self = this; | |||
if (self.setReviewers.reviewers === undefined) { | ||||
r4500 | return | |||
} | ||||
r4503 | if (self.setReviewers.reviewers.length === 0) { | |||
self.controller.emptyReviewersTable('<tr id="reviewer-empty-msg"><td colspan="6">No reviewers</td></tr>'); | ||||
r4500 | return | |||
} | ||||
r4485 | ||||
r4503 | self.controller.emptyReviewersTable(); | |||
r4500 | ||||
r4503 | $.each(self.setReviewers.reviewers, function (key, val) { | |||
r4485 | ||||
r4500 | var member = val; | |||
r4503 | if (member.role === self.controller.ROLE_REVIEWER) { | |||
r4500 | var entry = renderTemplate('reviewMemberEntry', { | |||
'member': member, | ||||
'mandatory': member.mandatory, | ||||
'role': member.role, | ||||
'reasons': member.reasons, | ||||
'allowed_to_update': member.allowed_to_update, | ||||
'review_status': member.review_status, | ||||
'review_status_label': member.review_status_label, | ||||
'user_group': member.user_group, | ||||
r4595 | 'create': false, | |||
'rhodecode_user': templateContext.rhodecode_user | ||||
r4500 | }); | |||
r4485 | ||||
r4503 | $(self.controller.$reviewMembers.selector).append(entry) | |||
r4500 | } | |||
r4485 | }); | |||
r4500 | ||||
r4485 | tooltipActivate(); | |||
}, | ||||
edit: function (event) { | ||||
r4503 | var self = this; | |||
self.editButton.hide(); | ||||
self.closeButton.show(); | ||||
self.addButton.show(); | ||||
$(self.removeButtons.selector).css('visibility', 'visible'); | ||||
r4485 | // review rules | |||
r4503 | self.controller.loadReviewRules(this.reviewRules); | |||
r4485 | }, | |||
close: function (event) { | ||||
r4503 | var self = this; | |||
r4485 | this.editButton.show(); | |||
this.closeButton.hide(); | ||||
this.addButton.hide(); | ||||
$(this.removeButtons.selector).css('visibility', 'hidden'); | ||||
// hide review rules | ||||
r4503 | self.controller.hideReviewRules(); | |||
r4485 | } | |||
}; | ||||
r4500 | /** | |||
* Reviewer display panel | ||||
*/ | ||||
window.ObserversPanel = { | ||||
editButton: null, | ||||
closeButton: null, | ||||
addButton: null, | ||||
removeButtons: null, | ||||
reviewRules: null, | ||||
setReviewers: null, | ||||
r4503 | controller: null, | |||
r4500 | ||||
setSelectors: function () { | ||||
var self = this; | ||||
self.editButton = $('#open_edit_observers'); | ||||
self.closeButton =$('#close_edit_observers'); | ||||
self.addButton = $('#add_observer'); | ||||
self.removeButtons = $('.observer_member_remove,.observer_member_mandatory_remove'); | ||||
}, | ||||
r4503 | init: function (controller, reviewRules, setReviewers) { | |||
r4500 | var self = this; | |||
self.setSelectors(); | ||||
r4503 | self.controller = controller; | |||
self.reviewRules = reviewRules; | ||||
self.setReviewers = setReviewers; | ||||
r4500 | ||||
r4503 | self.editButton.on('click', function (e) { | |||
r4500 | self.edit(); | |||
}); | ||||
r4503 | self.closeButton.on('click', function (e) { | |||
r4500 | self.close(); | |||
self.renderObservers(); | ||||
}); | ||||
self.renderObservers(); | ||||
}, | ||||
renderObservers: function () { | ||||
r4503 | var self = this; | |||
if (self.setReviewers.observers === undefined) { | ||||
r4500 | return | |||
} | ||||
r4503 | if (self.setReviewers.observers.length === 0) { | |||
self.controller.emptyObserversTable('<tr id="observer-empty-msg"><td colspan="6">No observers</td></tr>'); | ||||
r4500 | return | |||
} | ||||
r4503 | self.controller.emptyObserversTable(); | |||
r4500 | ||||
r4503 | $.each(self.setReviewers.observers, function (key, val) { | |||
r4500 | var member = val; | |||
r4503 | if (member.role === self.controller.ROLE_OBSERVER) { | |||
r4500 | var entry = renderTemplate('reviewMemberEntry', { | |||
'member': member, | ||||
'mandatory': member.mandatory, | ||||
'role': member.role, | ||||
'reasons': member.reasons, | ||||
'allowed_to_update': member.allowed_to_update, | ||||
'review_status': member.review_status, | ||||
'review_status_label': member.review_status_label, | ||||
'user_group': member.user_group, | ||||
r4595 | 'create': false, | |||
'rhodecode_user': templateContext.rhodecode_user | ||||
r4500 | }); | |||
r4503 | $(self.controller.$observerMembers.selector).append(entry) | |||
r4500 | } | |||
}); | ||||
tooltipActivate(); | ||||
}, | ||||
edit: function (event) { | ||||
this.editButton.hide(); | ||||
this.closeButton.show(); | ||||
this.addButton.show(); | ||||
$(this.removeButtons.selector).css('visibility', 'visible'); | ||||
}, | ||||
close: function (event) { | ||||
this.editButton.show(); | ||||
this.closeButton.hide(); | ||||
this.addButton.hide(); | ||||
$(this.removeButtons.selector).css('visibility', 'hidden'); | ||||
} | ||||
}; | ||||
window.PRDetails = { | ||||
editButton: null, | ||||
closeButton: null, | ||||
deleteButton: null, | ||||
viewFields: null, | ||||
editFields: null, | ||||
setSelectors: function () { | ||||
var self = this; | ||||
self.editButton = $('#open_edit_pullrequest') | ||||
self.closeButton = $('#close_edit_pullrequest') | ||||
self.deleteButton = $('#delete_pullrequest') | ||||
self.viewFields = $('#pr-desc, #pr-title') | ||||
self.editFields = $('#pr-desc-edit, #pr-title-edit, .pr-save') | ||||
}, | ||||
init: function () { | ||||
var self = this; | ||||
self.setSelectors(); | ||||
self.editButton.on('click', function (e) { | ||||
self.edit(); | ||||
}); | ||||
self.closeButton.on('click', function (e) { | ||||
self.view(); | ||||
}); | ||||
}, | ||||
edit: function (event) { | ||||
var cmInstance = $('#pr-description-input').get(0).MarkupForm.cm; | ||||
this.viewFields.hide(); | ||||
this.editButton.hide(); | ||||
this.deleteButton.hide(); | ||||
this.closeButton.show(); | ||||
this.editFields.show(); | ||||
cmInstance.refresh(); | ||||
}, | ||||
view: function (event) { | ||||
this.editButton.show(); | ||||
this.deleteButton.show(); | ||||
this.editFields.hide(); | ||||
this.closeButton.hide(); | ||||
this.viewFields.show(); | ||||
} | ||||
}; | ||||
r4485 | ||||
/** | ||||
* OnLine presence using channelstream | ||||
*/ | ||||
window.ReviewerPresenceController = function (channel) { | ||||
var self = this; | ||||
this.channel = channel; | ||||
this.users = {}; | ||||
this.storeUsers = function (users) { | ||||
self.users = {} | ||||
$.each(users, function (index, value) { | ||||
var userId = value.state.id; | ||||
self.users[userId] = value.state; | ||||
}) | ||||
} | ||||
this.render = function () { | ||||
$.each($('.reviewer_entry'), function (index, value) { | ||||
var userData = $(value).data(); | ||||
if (self.users[userData.reviewerUserId] !== undefined) { | ||||
$(value).find('.presence-state').show(); | ||||
} else { | ||||
$(value).find('.presence-state').hide(); | ||||
} | ||||
}) | ||||
}; | ||||
this.handlePresence = function (data) { | ||||
if (data.type == 'presence' && data.channel === self.channel) { | ||||
this.storeUsers(data.users); | ||||
r4549 | this.render(); | |||
r4485 | } | |||
}; | ||||
this.handleChannelUpdate = function (data) { | ||||
if (data.channel === this.channel) { | ||||
this.storeUsers(data.state.users); | ||||
r4549 | this.render(); | |||
r4485 | } | |||
}; | ||||
/* subscribe to the current presence */ | ||||
$.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this)); | ||||
/* subscribe to updates e.g connect/disconnect */ | ||||
$.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this)); | ||||
}; | ||||
r4562 | window.refreshCommentsSuccess = function(targetNode, counterNode, extraCallback) { | |||
var $targetElem = targetNode; | ||||
var $counterElem = counterNode; | ||||
return function (data) { | ||||
var newCount = $(data).data('counter'); | ||||
if (newCount !== undefined) { | ||||
var callback = function () { | ||||
$counterElem.animate({'opacity': 1.00}, 200) | ||||
$counterElem.html(newCount); | ||||
}; | ||||
$counterElem.animate({'opacity': 0.15}, 200, callback); | ||||
} | ||||
$targetElem.css('opacity', 1); | ||||
$targetElem.html(data); | ||||
tooltipActivate(); | ||||
if (extraCallback !== undefined) { | ||||
extraCallback(data) | ||||
} | ||||
} | ||||
} | ||||
r4485 | window.refreshComments = function (version) { | |||
version = version || templateContext.pull_request_data.pull_request_version || ''; | ||||
// Pull request case | ||||
if (templateContext.pull_request_data.pull_request_id !== null) { | ||||
var params = { | ||||
'pull_request_id': templateContext.pull_request_data.pull_request_id, | ||||
'repo_name': templateContext.repo_name, | ||||
'version': version, | ||||
}; | ||||
var loadUrl = pyroutes.url('pullrequest_comments', params); | ||||
} // commit case | ||||
else { | ||||
return | ||||
} | ||||
var currentIDs = [] | ||||
$.each($('.comment'), function (idx, element) { | ||||
currentIDs.push($(element).data('commentId')); | ||||
}); | ||||
r4500 | var data = {"comments": currentIDs}; | |||
r4485 | ||||
var $targetElem = $('.comments-content-table'); | ||||
$targetElem.css('opacity', 0.3); | ||||
r4562 | var $counterElem = $('#comments-count'); | |||
var success = refreshCommentsSuccess($targetElem, $counterElem); | ||||
r4500 | ajaxPOST(loadUrl, data, success, null, {}) | |||
r4485 | } | |||
window.refreshTODOs = function (version) { | ||||
version = version || templateContext.pull_request_data.pull_request_version || ''; | ||||
// Pull request case | ||||
if (templateContext.pull_request_data.pull_request_id !== null) { | ||||
var params = { | ||||
'pull_request_id': templateContext.pull_request_data.pull_request_id, | ||||
'repo_name': templateContext.repo_name, | ||||
'version': version, | ||||
}; | ||||
r4539 | var loadUrl = pyroutes.url('pullrequest_todos', params); | |||
r4485 | } // commit case | |||
else { | ||||
return | ||||
} | ||||
var currentIDs = [] | ||||
$.each($('.comment'), function (idx, element) { | ||||
currentIDs.push($(element).data('commentId')); | ||||
}); | ||||
r4500 | var data = {"comments": currentIDs}; | |||
r4485 | var $targetElem = $('.todos-content-table'); | |||
$targetElem.css('opacity', 0.3); | ||||
r4562 | var $counterElem = $('#todos-count'); | |||
var success = refreshCommentsSuccess($targetElem, $counterElem); | ||||
r4500 | ||||
ajaxPOST(loadUrl, data, success, null, {}) | ||||
r4485 | } | |||
r4562 | window.refreshDraftComments = function () { | |||
// Pull request case | ||||
if (templateContext.pull_request_data.pull_request_id !== null) { | ||||
var params = { | ||||
'pull_request_id': templateContext.pull_request_data.pull_request_id, | ||||
'repo_name': templateContext.repo_name, | ||||
}; | ||||
var loadUrl = pyroutes.url('pullrequest_drafts', params); | ||||
} // commit case | ||||
else { | ||||
return | ||||
} | ||||
var data = {}; | ||||
var $targetElem = $('.drafts-content-table'); | ||||
$targetElem.css('opacity', 0.3); | ||||
var $counterElem = $('#drafts-count'); | ||||
var extraCallback = function(data) { | ||||
if ($(data).data('counter') == 0){ | ||||
$('#draftsTable').hide(); | ||||
} else { | ||||
$('#draftsTable').show(); | ||||
} | ||||
// uncheck on load the select all checkbox | ||||
$('[name=select_all_drafts]').prop('checked', 0); | ||||
} | ||||
var success = refreshCommentsSuccess($targetElem, $counterElem, extraCallback); | ||||
ajaxPOST(loadUrl, data, success, null, {}) | ||||
}; | ||||
r4485 | window.refreshAllComments = function (version) { | |||
version = version || templateContext.pull_request_data.pull_request_version || ''; | ||||
refreshComments(version); | ||||
refreshTODOs(version); | ||||
}; | ||||
r4500 | ||||
window.sidebarComment = function (commentId) { | ||||
var jsonData = $('#commentHovercard{0}'.format(commentId)).data('commentJsonB64'); | ||||
if (!jsonData) { | ||||
return 'Failed to load comment {0}'.format(commentId) | ||||
} | ||||
var funcData = JSON.parse(atob(jsonData)); | ||||
return renderTemplate('sideBarCommentHovercard', funcData) | ||||
}; | ||||