commits/pr pages various fixes.

- added navbar to commit page
- improvements on UI
- code cleanups
This commit is contained in:
Marcin Kuzminski 2020-09-23 11:45:43 +02:00
parent 5e1a429afd
commit f634c67b68
24 changed files with 1513 additions and 1029 deletions

View file

@ -485,23 +485,10 @@ class TestRepoCommitCommentsView(TestController):
def assert_comment_links(response, comments, inline_comments):
if comments == 1:
comments_text = "%d General" % comments
else:
comments_text = "%d General" % comments
response.mustcontain(
'<span class="display-none" id="general-comments-count">{}</span>'.format(comments))
response.mustcontain(
'<span class="display-none" id="inline-comments-count">{}</span>'.format(inline_comments))
if inline_comments == 1:
inline_comments_text = "%d Inline" % inline_comments
else:
inline_comments_text = "%d Inline" % inline_comments
if comments:
response.mustcontain('<a href="#comments">%s</a>,' % comments_text)
else:
response.mustcontain(comments_text)
if inline_comments:
response.mustcontain(
'id="inline-comments-counter">%s' % inline_comments_text)
else:
response.mustcontain(inline_comments_text)

View file

@ -18,8 +18,8 @@
# RhodeCode Enterprise Edition, including its added features, Support services,
# and proprietary license terms, please see https://rhodecode.com/licenses/
import logging
import collections
from pyramid.httpexceptions import (
HTTPNotFound, HTTPBadRequest, HTTPFound, HTTPForbidden, HTTPConflict)
@ -34,14 +34,14 @@ from rhodecode.apps.file_store.exceptions import FileNotAllowedException, FileOv
from rhodecode.lib import diffs, codeblocks
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous, CSRFRequired)
from rhodecode.lib.ext_json import json
from rhodecode.lib.compat import OrderedDict
from rhodecode.lib.diffs import (
cache_diff, load_cached_diff, diff_cache_exist, get_diff_context,
get_diff_whitespace_flag)
from rhodecode.lib.exceptions import StatusChangeOnClosedPullRequestError, CommentVersionMismatch
import rhodecode.lib.helpers as h
from rhodecode.lib.utils2 import safe_unicode, str2bool
from rhodecode.lib.utils2 import safe_unicode, str2bool, StrictAttributeDict
from rhodecode.lib.vcs.backends.base import EmptyCommit
from rhodecode.lib.vcs.exceptions import (
RepositoryError, CommitDoesNotExistError)
@ -87,7 +87,6 @@ class RepoCommitsView(RepoAppView):
diff_limit = c.visual.cut_off_limit_diff
file_limit = c.visual.cut_off_limit_file
# get ranges of commit ids if preset
commit_range = commit_id_range.split('...')[:2]
@ -116,6 +115,7 @@ class RepoCommitsView(RepoAppView):
except Exception:
log.exception("General failure")
raise HTTPNotFound()
single_commit = len(c.commit_ranges) == 1
c.changes = OrderedDict()
c.lines_added = 0
@ -129,23 +129,48 @@ class RepoCommitsView(RepoAppView):
c.inline_comments = []
c.files = []
c.statuses = []
c.comments = []
c.unresolved_comments = []
c.resolved_comments = []
if len(c.commit_ranges) == 1:
# Single commit
if single_commit:
commit = c.commit_ranges[0]
c.comments = CommentsModel().get_comments(
self.db_repo.repo_id,
revision=commit.raw_id)
c.statuses.append(ChangesetStatusModel().get_status(
self.db_repo.repo_id, commit.raw_id))
# comments from PR
statuses = ChangesetStatusModel().get_statuses(
self.db_repo.repo_id, commit.raw_id,
with_revisions=True)
prs = set(st.pull_request for st in statuses
if st.pull_request is not None)
prs = set()
reviewers = list()
reviewers_duplicates = set() # to not have duplicates from multiple votes
for c_status in statuses:
# extract associated pull-requests from votes
if c_status.pull_request:
prs.add(c_status.pull_request)
# extract reviewers
_user_id = c_status.author.user_id
if _user_id not in reviewers_duplicates:
reviewers.append(
StrictAttributeDict({
'user': c_status.author,
# fake attributed for commit, page that we don't have
# but we share the display with PR page
'mandatory': False,
'reasons': [],
'rule_user_group_data': lambda: None
})
)
reviewers_duplicates.add(_user_id)
c.allowed_reviewers = reviewers
# from associated statuses, check the pull requests, and
# show comments from them
for pr in prs:
@ -156,6 +181,37 @@ class RepoCommitsView(RepoAppView):
c.resolved_comments = CommentsModel()\
.get_commit_resolved_todos(commit.raw_id)
c.inline_comments_flat = CommentsModel()\
.get_commit_inline_comments(commit.raw_id)
review_statuses = ChangesetStatusModel().aggregate_votes_by_user(
statuses, reviewers)
c.commit_review_status = ChangesetStatus.STATUS_NOT_REVIEWED
c.commit_set_reviewers_data_json = collections.OrderedDict({'reviewers': []})
for review_obj, member, reasons, mandatory, status in review_statuses:
member_reviewer = h.reviewer_as_json(
member, reasons=reasons, mandatory=mandatory,
user_group=None
)
current_review_status = status[0][1].status if status else ChangesetStatus.STATUS_NOT_REVIEWED
member_reviewer['review_status'] = current_review_status
member_reviewer['review_status_label'] = h.commit_status_lbl(current_review_status)
member_reviewer['allowed_to_update'] = False
c.commit_set_reviewers_data_json['reviewers'].append(member_reviewer)
c.commit_set_reviewers_data_json = json.dumps(c.commit_set_reviewers_data_json)
# NOTE(marcink): this uses the same voting logic as in pull-requests
c.commit_review_status = ChangesetStatusModel().calculate_status(review_statuses)
c.commit_broadcast_channel = u'/repo${}$/commit/{}'.format(
c.repo_name,
commit.raw_id
)
diff = None
# Iterate over ranges (default commit view is always one commit)
for commit in c.commit_ranges:
@ -397,6 +453,7 @@ class RepoCommitsView(RepoAppView):
}
if comment:
c.co = comment
c.at_version_num = 0
rendered_comment = render(
'rhodecode:templates/changeset/changeset_comment_block.mako',
self._get_template_context(c), self.request)

View file

@ -39,7 +39,7 @@ from rhodecode.lib.ext_json import json
from rhodecode.lib.auth import (
LoginRequired, HasRepoPermissionAny, HasRepoPermissionAnyDecorator,
NotAnonymous, CSRFRequired)
from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode
from rhodecode.lib.utils2 import str2bool, safe_str, safe_unicode, safe_int
from rhodecode.lib.vcs.backends.base import EmptyCommit, UpdateFailureReason
from rhodecode.lib.vcs.exceptions import (
CommitDoesNotExistError, RepositoryRequirementError, EmptyRepositoryError)
@ -474,9 +474,6 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
c.pull_request_set_reviewers_data_json = json.dumps(c.pull_request_set_reviewers_data_json)
general_comments, inline_comments = \
self.register_comments_vars(c, pull_request_latest, versions)
@ -980,7 +977,7 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
version = self.request.GET.get('version')
_render = self.request.get_partial_renderer(
'rhodecode:templates/pullrequests/pullrequest_show.mako')
'rhodecode:templates/base/sidebar.mako')
c = _render.get_call_context()
(pull_request_latest,
@ -999,7 +996,11 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
self.register_comments_vars(c, pull_request_latest, versions)
all_comments = c.inline_comments_flat + c.comments
return _render('comments_table', all_comments, len(all_comments))
existing_ids = filter(
lambda e: e, map(safe_int, self.request.POST.getall('comments[]')))
return _render('comments_table', all_comments, len(all_comments),
existing_ids=existing_ids)
@LoginRequired()
@NotAnonymous()
@ -1017,7 +1018,7 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
version = self.request.GET.get('version')
_render = self.request.get_partial_renderer(
'rhodecode:templates/pullrequests/pullrequest_show.mako')
'rhodecode:templates/base/sidebar.mako')
c = _render.get_call_context()
(pull_request_latest,
pull_request_at_ver,
@ -1039,7 +1040,10 @@ class RepoPullRequestsView(RepoAppView, DataGridAppView):
.get_pull_request_resolved_todos(pull_request)
all_comments = c.unresolved_comments + c.resolved_comments
return _render('comments_table', all_comments, len(c.unresolved_comments), todo_comments=True)
existing_ids = filter(
lambda e: e, map(safe_int, self.request.POST.getall('comments[]')))
return _render('comments_table', all_comments, len(c.unresolved_comments),
todo_comments=True, existing_ids=existing_ids)
@LoginRequired()
@NotAnonymous()

View file

@ -354,34 +354,37 @@ class ChangesetStatusModel(BaseModel):
Session().add(new_status)
return new_statuses
def aggregate_votes_by_user(self, commit_statuses, reviewers_data):
commit_statuses_map = collections.defaultdict(list)
for st in commit_statuses:
commit_statuses_map[st.author.username] += [st]
reviewers = []
def version(commit_status):
return commit_status.version
for obj in reviewers_data:
if not obj.user:
continue
statuses = commit_statuses_map.get(obj.user.username, None)
if statuses:
status_groups = itertools.groupby(
sorted(statuses, key=version), version)
statuses = [(x, list(y)[0]) for x, y in status_groups]
reviewers.append((obj, obj.user, obj.reasons, obj.mandatory, statuses))
return reviewers
def reviewers_statuses(self, pull_request):
_commit_statuses = self.get_statuses(
pull_request.source_repo,
pull_request=pull_request,
with_revisions=True)
commit_statuses = collections.defaultdict(list)
for st in _commit_statuses:
commit_statuses[st.author.username] += [st]
pull_request_reviewers = []
def version(commit_status):
return commit_status.version
for obj in pull_request.reviewers:
if not obj.user:
continue
statuses = commit_statuses.get(obj.user.username, None)
if statuses:
status_groups = itertools.groupby(
sorted(statuses, key=version), version)
statuses = [(x, list(y)[0]) for x, y in status_groups]
pull_request_reviewers.append(
(obj, obj.user, obj.reasons, obj.mandatory, statuses))
return pull_request_reviewers
return self.aggregate_votes_by_user(_commit_statuses, pull_request.reviewers)
def calculated_review_status(self, pull_request, reviewers_statuses=None):
"""

View file

@ -228,6 +228,14 @@ class CommentsModel(BaseModel):
return todos
def get_commit_inline_comments(self, commit_id):
inline_comments = Session().query(ChangesetComment) \
.filter(ChangesetComment.line_no != None) \
.filter(ChangesetComment.f_path != None) \
.filter(ChangesetComment.revision == commit_id)
inline_comments = inline_comments.all()
return inline_comments
def _log_audit_action(self, action, action_data, auth_user, comment):
audit_logger.store(
action=action,

View file

@ -55,3 +55,16 @@
margin: 0 auto 35px auto;
}
}
.alert-text-success {
color: @alert1;
}
.alert-text-error {
color: @alert2;
}
.alert-text-warning {
color: @alert3;
}

View file

@ -254,7 +254,7 @@ input[type="button"] {
.btn-group-actions {
position: relative;
z-index: 100;
z-index: 50;
&:not(.open) .btn-action-switcher-container {
display: none;

View file

@ -1078,10 +1078,16 @@ input.filediff-collapse-state {
background: @color5;
color: white;
}
&[op="comments"] { /* comments on file */
background: @grey4;
color: white;
}
&[op="options"] { /* context menu */
background: @grey6;
color: black;
}
}
}

View file

@ -31,6 +31,10 @@ a { cursor: pointer; }
clear: both;
}
.display-none {
display: none;
}
.pull-right {
float: right !important;
}

View file

@ -83,6 +83,11 @@ body {
}
}
.flex-container {
display: flex;
justify-content: space-between;
}
.action-link{
margin-left: @padding;
padding-left: @padding;
@ -482,6 +487,15 @@ ul.auth_plugins {
text-align: left;
overflow: hidden;
white-space: pre-line;
padding-top: 5px
}
#add_reviewer {
padding-top: 10px;
}
#add_reviewer_input {
padding-top: 10px
}
.pr-details-title-author-pref {
@ -1169,9 +1183,12 @@ label {
a {
color: @grey5
}
@media screen and (max-width: 1200px) {
// 1024px or smaller
@media screen and (max-width: 1180px) {
display: none;
}
}
img {
@ -1553,6 +1570,7 @@ table.integrations {
width: 16px;
padding: 0;
color: black;
cursor: pointer;
}
.reviewer_member_mandatory_remove {
@ -1682,7 +1700,7 @@ table.group_members {
}
.reviewer_ac .ac-input {
width: 92%;
width: 100%;
margin-bottom: 1em;
}
@ -2756,7 +2774,7 @@ table.rctable td.td-search-results div {
}
#help_kb .modal-content{
max-width: 750px;
max-width: 800px;
margin: 10% auto;
table{
@ -3053,4 +3071,141 @@ form.markup-form {
.pr-hovercard-title {
padding-top: 5px;
}
}
.action-divider {
opacity: 0.5;
}
.details-inline-block {
display: inline-block;
position: relative;
}
.details-inline-block summary {
list-style: none;
}
details:not([open]) > :not(summary) {
display: none !important;
}
.details-reset > summary {
list-style: none;
}
.details-reset > summary::-webkit-details-marker {
display: none;
}
.details-dropdown {
position: absolute;
top: 100%;
width: 185px;
list-style: none;
background-color: #fff;
background-clip: padding-box;
border: 1px solid @grey5;
box-shadow: 0 8px 24px rgba(149, 157, 165, .2);
left: -150px;
text-align: left;
z-index: 90;
}
.dropdown-divider {
display: block;
height: 0;
margin: 8px 0;
border-top: 1px solid @grey5;
}
.dropdown-item {
display: block;
padding: 4px 8px 4px 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-weight: normal;
}
.right-sidebar {
position: fixed;
top: 0px;
bottom: 0;
right: 0;
background: #fafafa;
z-index: 50;
}
.right-sidebar {
border-left: 1px solid @grey5;
}
.right-sidebar.right-sidebar-expanded {
width: 300px;
overflow: scroll;
}
.right-sidebar.right-sidebar-collapsed {
width: 40px;
padding: 0;
display: block;
overflow: hidden;
}
.sidenav {
float: right;
will-change: min-height;
background: #fafafa;
width: 100%;
}
.sidebar-toggle {
height: 30px;
text-align: center;
margin: 15px 0px 0 0;
}
.sidebar-toggle a {
}
.sidebar-content {
margin-left: 15px;
margin-right: 15px;
}
.sidebar-heading {
font-size: 1.2em;
font-weight: 700;
margin-top: 10px;
}
.sidebar-element {
margin-top: 20px;
}
.right-sidebar-collapsed-state {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 10px;
cursor: pointer;
font-size: 1.3em;
margin: 0 -15px;
}
.right-sidebar-collapsed-state:hover {
background-color: @grey5;
}
.old-comments-marker {
text-align: left;
}
.old-comments-marker td {
padding-top: 15px;
border-bottom: 1px solid @grey5;
}

View file

@ -790,7 +790,7 @@ input {
&.main_filter_input {
padding: 5px 10px;
min-width: 340px;
color: @grey7;
background: @black;
min-height: 18px;
@ -800,11 +800,34 @@ input {
color: @grey2 !important;
background: white !important;
}
&:focus {
color: @grey2 !important;
background: white !important;
}
min-width: 360px;
@media screen and (max-width: 1600px) {
min-width: 300px;
}
@media screen and (max-width: 1500px) {
min-width: 280px;
}
@media screen and (max-width: 1400px) {
min-width: 260px;
}
@media screen and (max-width: 1300px) {
min-width: 240px;
}
@media screen and (max-width: 1200px) {
min-width: 220px;
}
@media screen and (max-width: 720px) {
min-width: 140px;
}
}
}

View file

@ -168,6 +168,7 @@
.icon-remove:before { content: '\e810'; } /* '' */
.icon-fork:before { content: '\e811'; } /* '' */
.icon-more:before { content: '\e812'; } /* '' */
.icon-options:before { content: '\e812'; } /* '' */
.icon-search:before { content: '\e813'; } /* '' */
.icon-scissors:before { content: '\e814'; } /* '' */
.icon-download:before { content: '\e815'; } /* '' */
@ -251,6 +252,7 @@
// TRANSFORM
.icon-merge:before {transform: rotate(180deg);}
.icon-wide-mode:before {transform: rotate(90deg);}
.icon-options:before {transform: rotate(90deg);}
// -- END ICON CLASSES -- //

View file

@ -131,6 +131,11 @@ function setRCMouseBindings(repoName, repoLandingRev) {
window.location = pyroutes.url(
'edit_repo_perms', {'repo_name': repoName});
});
Mousetrap.bind(['t s'], function(e) {
if (window.toggleSidebar !== undefined) {
window.toggleSidebar();
}
});
}
}

View file

@ -35,4 +35,75 @@ var quick_repo_menu = function() {
}, function() {
hide_quick_repo_menus();
});
};
};
window.toggleElement = function (elem, target) {
var $elem = $(elem);
var $target = $(target);
if ($target.is(':visible') || $target.length === 0) {
$target.hide();
$elem.html($elem.data('toggleOn'))
} else {
$target.show();
$elem.html($elem.data('toggleOff'))
}
return false
}
var marginExpVal = '300' // needs a sync with `.right-sidebar.right-sidebar-expanded` value
var marginColVal = '40' // needs a sync with `.right-sidebar.right-sidebar-collapsed` value
var marginExpanded = {'margin': '0 {0}px 0 0'.format(marginExpVal)};
var marginCollapsed = {'margin': '0 {0}px 0 0'.format(marginColVal)};
var updateStickyHeader = function () {
if (window.updateSticky !== undefined) {
// potentially our comments change the active window size, so we
// notify sticky elements
updateSticky()
}
}
var expandSidebar = function () {
var $sideBar = $('.right-sidebar');
$('.outerwrapper').css(marginExpanded);
$('.sidebar-toggle a').html('<i class="icon-right" style="margin-right: -10px"></i><i class="icon-right"></i>');
$('.right-sidebar-collapsed-state').hide();
$('.right-sidebar-expanded-state').show();
$('.branding').addClass('display-none');
$sideBar.addClass('right-sidebar-expanded')
$sideBar.removeClass('right-sidebar-collapsed')
}
var collapseSidebar = function () {
var $sideBar = $('.right-sidebar');
$('.outerwrapper').css(marginCollapsed);
$('.sidebar-toggle a').html('<i class="icon-left" style="margin-right: -10px"></i><i class="icon-left"></i>');
$('.right-sidebar-collapsed-state').show();
$('.right-sidebar-expanded-state').hide();
$('.branding').removeClass('display-none');
$sideBar.removeClass('right-sidebar-expanded')
$sideBar.addClass('right-sidebar-collapsed')
}
window.toggleSidebar = function () {
var $sideBar = $('.right-sidebar');
if ($sideBar.hasClass('right-sidebar-expanded')) {
// expanded -> collapsed transition
collapseSidebar();
var sidebarState = 'collapsed';
} else {
// collapsed -> expanded
expandSidebar();
var sidebarState = 'expanded';
}
// update our other sticky header in same context
updateStickyHeader();
storeUserSessionAttr('rc_user_session_attr.sidebarState', sidebarState);
}

View file

@ -279,8 +279,11 @@ ReviewersController = function () {
$('#user').show(); // show user autocomplete after load
var commitElements = data["diff_info"]['commits'];
if (commitElements.length === 0) {
prButtonLock(true, _gettext('no commits'), 'all');
var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
_gettext('There are no commits to merge.'));
prButtonLock(true, noCommitsMsg, 'all');
} else {
// un-lock PR button, so we cannot send PR before it's calculated
@ -324,7 +327,6 @@ ReviewersController = function () {
};
this.addReviewMember = function (reviewer_obj, reasons, mandatory) {
var members = self.$reviewMembers.get(0);
var id = reviewer_obj.user_id;
var username = reviewer_obj.username;
@ -333,10 +335,10 @@ ReviewersController = function () {
// register IDS to check if we don't have this ID already in
var currentIds = [];
var _els = self.$reviewMembers.find('li').toArray();
for (el in _els) {
currentIds.push(_els[el].id)
}
$.each(self.$reviewMembers.find('.reviewer_entry'), function (index, value) {
currentIds.push($(value).data('reviewerUserId'))
})
var userAllowedReview = function (userId) {
var allowed = true;
@ -354,12 +356,12 @@ ReviewersController = function () {
alert(_gettext('User `{0}` not allowed to be a reviewer').format(username));
} else {
// only add if it's not there
var alreadyReviewer = currentIds.indexOf('reviewer_' + id) != -1;
var alreadyReviewer = currentIds.indexOf(id) != -1;
if (alreadyReviewer) {
alert(_gettext('User `{0}` already in reviewers').format(username));
} else {
members.innerHTML += renderTemplate('reviewMemberEntry', {
var reviewerEntry = renderTemplate('reviewMemberEntry', {
'member': reviewer_obj,
'mandatory': mandatory,
'reasons': reasons,
@ -368,7 +370,9 @@ ReviewersController = function () {
'review_status_label': _gettext('Not Reviewed'),
'user_group': reviewer_obj.user_group,
'create': true,
});
'rule_show': true,
})
$(self.$reviewMembers.selector).append(reviewerEntry);
tooltipActivate();
}
}
@ -492,7 +496,7 @@ var ReviewerAutoComplete = function(inputId) {
};
VersionController = function () {
window.VersionController = function () {
var self = this;
this.$verSource = $('input[name=ver_source]');
this.$verTarget = $('input[name=ver_target]');
@ -612,25 +616,10 @@ VersionController = function () {
return false
};
this.toggleElement = function (elem, target) {
var $elem = $(elem);
var $target = $(target);
if ($target.is(':visible') || $target.length === 0) {
$target.hide();
$elem.html($elem.data('toggleOn'))
} else {
$target.show();
$elem.html($elem.data('toggleOff'))
}
return false
}
};
UpdatePrController = function () {
window.UpdatePrController = function () {
var self = this;
this.$updateCommits = $('#update_commits');
this.$updateCommitsSwitcher = $('#update_commits_switcher');
@ -672,4 +661,230 @@ UpdatePrController = function () {
templateContext.repo_name,
templateContext.pull_request_data.pull_request_id, force);
};
};
};
/**
* Reviewer display panel
*/
window.ReviewersPanel = {
editButton: null,
closeButton: null,
addButton: null,
removeButtons: null,
reviewRules: null,
setReviewers: null,
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');
},
init: function (reviewRules, setReviewers) {
var self = this;
self.setSelectors();
this.reviewRules = reviewRules;
this.setReviewers = setReviewers;
this.editButton.on('click', function (e) {
self.edit();
});
this.closeButton.on('click', function (e) {
self.close();
self.renderReviewers();
});
self.renderReviewers();
},
renderReviewers: function () {
$('#review_members').html('')
$.each(this.setReviewers.reviewers, function (key, val) {
var member = val;
var entry = renderTemplate('reviewMemberEntry', {
'member': member,
'mandatory': member.mandatory,
'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,
'create': false
});
$('#review_members').append(entry)
});
tooltipActivate();
},
edit: function (event) {
this.editButton.hide();
this.closeButton.show();
this.addButton.show();
$(this.removeButtons.selector).css('visibility', 'visible');
// review rules
reviewersController.loadReviewRules(this.reviewRules);
},
close: function (event) {
this.editButton.show();
this.closeButton.hide();
this.addButton.hide();
$(this.removeButtons.selector).css('visibility', 'hidden');
// hide review rules
reviewersController.hideReviewRules()
}
};
/**
* 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);
this.render()
}
};
this.handleChannelUpdate = function (data) {
if (data.channel === this.channel) {
this.storeUsers(data.state.users);
this.render()
}
};
/* 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));
};
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'));
});
var data = {"comments[]": currentIDs};
var $targetElem = $('.comments-content-table');
$targetElem.css('opacity', 0.3);
$targetElem.load(
loadUrl, data, function (responseText, textStatus, jqXHR) {
if (jqXHR.status !== 200) {
return false;
}
var $counterElem = $('#comments-count');
var newCount = $(responseText).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);
tooltipActivate();
}
);
}
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,
};
var loadUrl = pyroutes.url('pullrequest_comments', params);
} // commit case
else {
return
}
var currentIDs = []
$.each($('.comment'), function (idx, element) {
currentIDs.push($(element).data('commentId'));
});
var data = {"comments[]": currentIDs};
var $targetElem = $('.todos-content-table');
$targetElem.css('opacity', 0.3);
$targetElem.load(
loadUrl, data, function (responseText, textStatus, jqXHR) {
if (jqXHR.status !== 200) {
return false;
}
var $counterElem = $('#todos-count')
var newCount = $(responseText).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);
tooltipActivate();
}
);
}
window.refreshAllComments = function (version) {
version = version || templateContext.pull_request_data.pull_request_version || '';
refreshComments(version);
refreshTODOs(version);
};

View file

@ -701,9 +701,6 @@
notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
notice_display = 'none' if len(notice_messages) == 0 else ''
%>
<style>
</style>
<ul id="quick" class="main_nav navigation horizontal-list">
## notice box for important system messages
@ -1202,6 +1199,7 @@
('g p', 'Goto pull requests page'),
('g o', 'Goto repository settings'),
('g O', 'Goto repository access permissions settings'),
('t s', 'Toggle sidebar on some pages'),
]
%>
%for key, desc in elems:
@ -1221,3 +1219,36 @@
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<script type="text/javascript">
(function () {
"use sctrict";
var $sideBar = $('.right-sidebar');
var expanded = $sideBar.hasClass('right-sidebar-expanded');
var sidebarState = templateContext.session_attrs.sidebarState;
var sidebarEnabled = $('aside.right-sidebar').get(0);
if (sidebarState === 'expanded') {
expanded = true
} else if (sidebarState === 'collapsed') {
expanded = false
}
if (sidebarEnabled) {
// show sidebar since it's hidden on load
$('.right-sidebar').show();
// init based on set initial class, or if defined user session attrs
if (expanded) {
window.expandSidebar();
window.updateStickyHeader();
} else {
window.collapseSidebar();
window.updateStickyHeader();
}
}
})()
</script>

View file

@ -0,0 +1,134 @@
## snippet for sidebar elements
## usage:
## <%namespace name="sidebar" file="/base/sidebar.mako"/>
## ${sidebar.comments_table()}
<%namespace name="base" file="/base/base.mako"/>
<%def name="comments_table(comments, counter_num, todo_comments=False, existing_ids=None, is_pr=True)">
<%
if todo_comments:
cls_ = 'todos-content-table'
def sorter(entry):
user_id = entry.author.user_id
resolved = '1' if entry.resolved else '0'
if user_id == c.rhodecode_user.user_id:
# own comments first
user_id = 0
return '{}'.format(str(entry.comment_id).zfill(10000))
else:
cls_ = 'comments-content-table'
def sorter(entry):
user_id = entry.author.user_id
return '{}'.format(str(entry.comment_id).zfill(10000))
existing_ids = existing_ids or []
%>
<table class="todo-table ${cls_}" data-total-count="${len(comments)}" data-counter="${counter_num}">
% for loop_obj, comment_obj in h.looper(reversed(sorted(comments, key=sorter))):
<%
display = ''
_cls = ''
%>
<%
comment_ver_index = comment_obj.get_index_version(getattr(c, 'versions', []))
prev_comment_ver_index = 0
if loop_obj.previous:
prev_comment_ver_index = loop_obj.previous.get_index_version(getattr(c, 'versions', []))
ver_info = None
if getattr(c, 'versions', []):
ver_info = c.versions[comment_ver_index-1] if comment_ver_index else None
%>
<% hidden_at_ver = comment_obj.outdated_at_version_js(c.at_version_num) %>
<% is_from_old_ver = comment_obj.older_than_version_js(c.at_version_num) %>
<%
if (prev_comment_ver_index > comment_ver_index):
comments_ver_divider = comment_ver_index
else:
comments_ver_divider = None
%>
% if todo_comments:
% if comment_obj.resolved:
<% _cls = 'resolved-todo' %>
<% display = 'none' %>
% endif
% else:
## SKIP TODOs we display them in other area
% if comment_obj.is_todo:
<% display = 'none' %>
% endif
## Skip outdated comments
% if comment_obj.outdated:
<% display = 'none' %>
<% _cls = 'hidden-comment' %>
% endif
% endif
% if not todo_comments and comments_ver_divider:
<tr class="old-comments-marker">
<td colspan="3">
% if ver_info:
<code>v${comments_ver_divider} ${h.age_component(ver_info.created_on, time_is_local=True, tooltip=False)}</code>
% else:
<code>v${comments_ver_divider}</code>
% endif
</td>
</tr>
% endif
<tr class="${_cls}" style="display: ${display};" data-sidebar-comment-id="${comment_obj.comment_id}">
<td class="td-todo-number">
<a class="${('todo-resolved' if comment_obj.resolved else '')} permalink"
href="#comment-${comment_obj.comment_id}"
onclick="return Rhodecode.comments.scrollToComment($('#comment-${comment_obj.comment_id}'), 0, ${hidden_at_ver})">
<%
version_info = ''
if is_pr:
version_info = (' made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else ' made in this version')
%>
% if todo_comments:
% if comment_obj.is_inline:
<i class="tooltip icon-code" title="Inline TODO comment${version_info}."></i>
% else:
<i class="tooltip icon-comment" title="General TODO comment${version_info}."></i>
% endif
% else:
% if comment_obj.outdated:
<i class="tooltip icon-comment-toggle" title="Inline Outdated made in v${comment_ver_index}."></i>
% elif comment_obj.is_inline:
<i class="tooltip icon-code" title="Inline comment${version_info}."></i>
% else:
<i class="tooltip icon-comment" title="General comment${version_info}."></i>
% endif
% endif
</a>
## NEW, since refresh
% if existing_ids and comment_obj.comment_id not in existing_ids:
<span class="tag">NEW</span>
% endif
</td>
<td class="td-todo-gravatar">
${base.gravatar(comment_obj.author.email, 16, user=comment_obj.author, tooltip=True, extra_class=['no-margin'])}
</td>
<td class="todo-comment-text-wrapper">
<div class="tooltip todo-comment-text timeago ${('todo-resolved' if comment_obj.resolved else '')} " title="${h.format_date(comment_obj.created_on)}" datetime="${comment_obj.created_on}${h.get_timezone(comment_obj.created_on, time_is_local=True)}">
<code>${h.chop_at_smart(comment_obj.text, '\n', suffix_if_chopped='...')}</code>
</div>
</td>
</tr>
% endfor
</table>
</%def>

View file

@ -4,6 +4,8 @@
<%namespace name="base" file="/base/base.mako"/>
<%namespace name="diff_block" file="/changeset/diff_block.mako"/>
<%namespace name="file_base" file="/files/base.mako"/>
<%namespace name="sidebar" file="/base/sidebar.mako"/>
<%def name="title()">
${_('{} Commit').format(c.repo_name)} - ${h.show_id(c.commit)}
@ -100,22 +102,6 @@
% endif
</div>
%if c.statuses:
<div class="tag status-tag-${c.statuses[0]} pull-right">
<i class="icon-circle review-status-${c.statuses[0]}"></i>
<div class="pull-right">${h.commit_status_lbl(c.statuses[0])}</div>
</div>
%endif
</div>
</div>
</div>
<div class="fieldset collapsable-content" data-toggle="summary-details" style="display: none;">
<div class="left-label-summary">
<p>${_('Commit navigation')}:</p>
<div class="right-label-summary">
<span id="parent_link" class="tag tagtag">
<a href="#parentCommit" title="${_('Parent Commit')}"><i class="icon-left icon-no-margin"></i>${_('parent')}</a>
</span>
@ -123,7 +109,9 @@
<span id="child_link" class="tag tagtag">
<a href="#childCommit" title="${_('Child Commit')}">${_('child')}<i class="icon-right icon-no-margin"></i></a>
</span>
</div>
</div>
</div>
@ -160,7 +148,9 @@
<%namespace name="cbdiffs" file="/codeblocks/diffs.mako"/>
${cbdiffs.render_diffset_menu(c.changes[c.commit.raw_id], commit=c.commit)}
${cbdiffs.render_diffset(
c.changes[c.commit.raw_id], commit=c.commit, use_comments=True,inline_comments=c.inline_comments )}
c.changes[c.commit.raw_id], commit=c.commit, use_comments=True,
inline_comments=c.inline_comments,
show_todos=False)}
</div>
## template for inline comment form
@ -169,7 +159,7 @@
## comments heading with count
<div class="comments-heading">
<i class="icon-comment"></i>
${_('Comments')} ${len(c.comments)}
${_('General Comments')} ${len(c.comments)}
</div>
## render comments
@ -180,123 +170,262 @@
h.commit_status(c.rhodecode_db_repo, c.commit.raw_id))}
</div>
## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
<script type="text/javascript">
### NAV SIDEBAR
<aside class="right-sidebar right-sidebar-expanded" id="commit-nav-sticky" style="display: none">
<div class="sidenav navbar__inner" >
## TOGGLE
<div class="sidebar-toggle" onclick="toggleSidebar(); return false">
<a href="#toggleSidebar" class="grey-link-action">
$(document).ready(function() {
</a>
</div>
var boxmax = parseInt($('#trimmed_message_box').css('max-height'), 10);
if($('#trimmed_message_box').height() === boxmax){
$('#message_expand').show();
}
## CONTENT
<div class="sidebar-content">
$('#message_expand').on('click', function(e){
$('#trimmed_message_box').css('max-height', 'none');
$(this).hide();
});
## RULES SUMMARY/RULES
<div class="sidebar-element clear-both">
<% vote_title = _ungettext(
'Status calculated based on votes from {} reviewer',
'Status calculated based on votes from {} reviewers', len(c.allowed_reviewers)).format(len(c.allowed_reviewers))
%>
$('.show-inline-comments').on('click', function(e){
var boxid = $(this).attr('data-comment-id');
var button = $(this);
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
<i class="icon-circle review-status-${c.commit_review_status}"></i>
${len(c.allowed_reviewers)}
</div>
</div>
if(button.hasClass("comments-visible")) {
$('#{0} .inline-comments'.format(boxid)).each(function(index){
$(this).hide();
## REVIEWERS
<div class="right-sidebar-expanded-state pr-details-title">
<span class="tooltip sidebar-heading" title="${vote_title}">
<i class="icon-circle review-status-${c.commit_review_status}"></i>
${_('Reviewers')}
</span>
</div>
<div id="reviewers" class="right-sidebar-expanded-state pr-details-content reviewers">
<table id="review_members" class="group_members">
## This content is loaded via JS and ReviewersPanel
</table>
</div>
## TODOs
<div class="sidebar-element clear-both">
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="TODOs">
<i class="icon-flag-filled"></i>
<span id="todos-count">${len(c.unresolved_comments)}</span>
</div>
<div class="right-sidebar-expanded-state pr-details-title">
## Only show unresolved, that is only what matters
<span class="sidebar-heading noselect" onclick="refreshTODOs(); return false">
<i class="icon-flag-filled"></i>
TODOs
</span>
% if c.resolved_comments:
<span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
% else:
<span class="block-right last-item noselect">Show resolved</span>
% endif
</div>
<div class="right-sidebar-expanded-state pr-details-content">
% if c.unresolved_comments + c.resolved_comments:
${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True, is_pr=False)}
% else:
<table>
<tr>
<td>
${_('No TODOs yet')}
</td>
</tr>
</table>
% endif
</div>
</div>
## COMMENTS
<div class="sidebar-element clear-both">
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
<i class="icon-comment" style="color: #949494"></i>
<span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
<span class="display-none" id="general-comments-count">${len(c.comments)}</span>
<span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
</div>
<div class="right-sidebar-expanded-state pr-details-title">
<span class="sidebar-heading noselect" onclick="refreshComments(); return false">
<i class="icon-comment" style="color: #949494"></i>
${_('Comments')}
</span>
</div>
<div class="right-sidebar-expanded-state pr-details-content">
% if c.inline_comments_flat + c.comments:
${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments), is_pr=False)}
% else:
<table>
<tr>
<td>
${_('No Comments yet')}
</td>
</tr>
</table>
% endif
</div>
</div>
</div>
</div>
</aside>
## FORM FOR MAKING JS ACTION AS CHANGESET COMMENTS
<script type="text/javascript">
window.setReviewersData = ${c.commit_set_reviewers_data_json | n};
$(document).ready(function () {
var boxmax = parseInt($('#trimmed_message_box').css('max-height'), 10);
if ($('#trimmed_message_box').height() === boxmax) {
$('#message_expand').show();
}
$('#message_expand').on('click', function (e) {
$('#trimmed_message_box').css('max-height', 'none');
$(this).hide();
});
$('.show-inline-comments').on('click', function (e) {
var boxid = $(this).attr('data-comment-id');
var button = $(this);
if (button.hasClass("comments-visible")) {
$('#{0} .inline-comments'.format(boxid)).each(function (index) {
$(this).hide();
});
button.removeClass("comments-visible");
} else {
$('#{0} .inline-comments'.format(boxid)).each(function(index){
$(this).show();
} else {
$('#{0} .inline-comments'.format(boxid)).each(function (index) {
$(this).show();
});
button.addClass("comments-visible");
}
});
}
});
// next links
$('#child_link').on('click', function(e){
// fetch via ajax what is going to be the next link, if we have
// >1 links show them to user to choose
if(!$('#child_link').hasClass('disabled')){
$.ajax({
// next links
$('#child_link').on('click', function (e) {
// fetch via ajax what is going to be the next link, if we have
// >1 links show them to user to choose
if (!$('#child_link').hasClass('disabled')) {
$.ajax({
url: '${h.route_path('repo_commit_children',repo_name=c.repo_name, commit_id=c.commit.raw_id)}',
success: function(data) {
if(data.results.length === 0){
$('#child_link').html("${_('No Child Commits')}").addClass('disabled');
}
if(data.results.length === 1){
var commit = data.results[0];
window.location = pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': commit.raw_id});
}
else if(data.results.length === 2){
$('#child_link').addClass('disabled');
$('#child_link').addClass('double');
success: function (data) {
if (data.results.length === 0) {
$('#child_link').html("${_('No Child Commits')}").addClass('disabled');
}
if (data.results.length === 1) {
var commit = data.results[0];
window.location = pyroutes.url('repo_commit', {
'repo_name': '${c.repo_name}',
'commit_id': commit.raw_id
});
} else if (data.results.length === 2) {
$('#child_link').addClass('disabled');
$('#child_link').addClass('double');
var _html = '';
_html +='<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> '
.replace('__branch__', data.results[0].branch)
.replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
.replace('__title__', data.results[0].message)
.replace('__url__', pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': data.results[0].raw_id}));
_html +=' | ';
_html +='<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> '
.replace('__branch__', data.results[1].branch)
.replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
.replace('__title__', data.results[1].message)
.replace('__url__', pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': data.results[1].raw_id}));
$('#child_link').html(_html);
}
var _html = '';
_html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> '
.replace('__branch__', data.results[0].branch)
.replace('__rev__', 'r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0, 6)))
.replace('__title__', data.results[0].message)
.replace('__url__', pyroutes.url('repo_commit', {
'repo_name': '${c.repo_name}',
'commit_id': data.results[0].raw_id
}));
_html += ' | ';
_html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a> '
.replace('__branch__', data.results[1].branch)
.replace('__rev__', 'r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0, 6)))
.replace('__title__', data.results[1].message)
.replace('__url__', pyroutes.url('repo_commit', {
'repo_name': '${c.repo_name}',
'commit_id': data.results[1].raw_id
}));
$('#child_link').html(_html);
}
}
});
e.preventDefault();
}
});
});
e.preventDefault();
}
});
// prev links
$('#parent_link').on('click', function(e){
// fetch via ajax what is going to be the next link, if we have
// >1 links show them to user to choose
if(!$('#parent_link').hasClass('disabled')){
$.ajax({
// prev links
$('#parent_link').on('click', function (e) {
// fetch via ajax what is going to be the next link, if we have
// >1 links show them to user to choose
if (!$('#parent_link').hasClass('disabled')) {
$.ajax({
url: '${h.route_path("repo_commit_parents",repo_name=c.repo_name, commit_id=c.commit.raw_id)}',
success: function(data) {
if(data.results.length === 0){
$('#parent_link').html('${_('No Parent Commits')}').addClass('disabled');
}
if(data.results.length === 1){
var commit = data.results[0];
window.location = pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': commit.raw_id});
}
else if(data.results.length === 2){
$('#parent_link').addClass('disabled');
$('#parent_link').addClass('double');
success: function (data) {
if (data.results.length === 0) {
$('#parent_link').html('${_('No Parent Commits')}').addClass('disabled');
}
if (data.results.length === 1) {
var commit = data.results[0];
window.location = pyroutes.url('repo_commit', {
'repo_name': '${c.repo_name}',
'commit_id': commit.raw_id
});
} else if (data.results.length === 2) {
$('#parent_link').addClass('disabled');
$('#parent_link').addClass('double');
var _html = '';
_html +='<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>'
.replace('__branch__', data.results[0].branch)
.replace('__rev__','r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0,6)))
.replace('__title__', data.results[0].message)
.replace('__url__', pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': data.results[0].raw_id}));
_html +=' | ';
_html +='<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>'
.replace('__branch__', data.results[1].branch)
.replace('__rev__','r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0,6)))
.replace('__title__', data.results[1].message)
.replace('__url__', pyroutes.url('repo_commit', {'repo_name': '${c.repo_name}','commit_id': data.results[1].raw_id}));
$('#parent_link').html(_html);
}
var _html = '';
_html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>'
.replace('__branch__', data.results[0].branch)
.replace('__rev__', 'r{0}:{1}'.format(data.results[0].revision, data.results[0].raw_id.substr(0, 6)))
.replace('__title__', data.results[0].message)
.replace('__url__', pyroutes.url('repo_commit', {
'repo_name': '${c.repo_name}',
'commit_id': data.results[0].raw_id
}));
_html += ' | ';
_html += '<a title="__title__" href="__url__"><span class="tag branchtag"><i class="icon-code-fork"></i>__branch__</span> __rev__</a>'
.replace('__branch__', data.results[1].branch)
.replace('__rev__', 'r{0}:{1}'.format(data.results[1].revision, data.results[1].raw_id.substr(0, 6)))
.replace('__title__', data.results[1].message)
.replace('__url__', pyroutes.url('repo_commit', {
'repo_name': '${c.repo_name}',
'commit_id': data.results[1].raw_id
}));
$('#parent_link').html(_html);
}
}
});
e.preventDefault();
}
});
});
e.preventDefault();
}
});
// browse tree @ revision
$('#files_link').on('click', function(e){
window.location = '${h.route_path('repo_files:default_path',repo_name=c.repo_name, commit_id=c.commit.raw_id)}';
e.preventDefault();
});
// browse tree @ revision
$('#files_link').on('click', function (e) {
window.location = '${h.route_path('repo_files:default_path',repo_name=c.repo_name, commit_id=c.commit.raw_id)}';
e.preventDefault();
});
})
</script>
ReviewersPanel.init(null, setReviewersData);
var channel = '${c.commit_broadcast_channel}';
new ReviewerPresenceController(channel)
})
</script>
</%def>

View file

@ -11,6 +11,10 @@
<%namespace name="base" file="/base/base.mako"/>
<%def name="comment_block(comment, inline=False, active_pattern_entries=None)">
<%
from rhodecode.model.comment import CommentsModel
comment_model = CommentsModel()
%>
<% comment_ver = comment.get_index_version(getattr(c, 'versions', [])) %>
<% latest_ver = len(getattr(c, 'versions', [])) %>
@ -155,20 +159,16 @@
</div>
%endif
<a class="permalink" href="#comment-${comment.comment_id}">&para; #${comment.comment_id}</a>
<div class="comment-links-block">
% if inline:
<a class="pr-version-inline" href="${request.current_route_path(_query=dict(version=comment.pull_request_version_id), _anchor='comment-{}'.format(comment.comment_id))}">
% if outdated_at_ver:
<code class="tooltip pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">
outdated ${'v{}'.format(comment_ver)} |
</code>
<code class="tooltip pr-version-num" title="${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">outdated ${'v{}'.format(comment_ver)}</code>
<code class="action-divider">|</code>
% elif comment_ver:
<code class="tooltip pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">
${'v{}'.format(comment_ver)} |
</code>
<code class="tooltip pr-version-num" title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}">${'v{}'.format(comment_ver)}</code>
<code class="action-divider">|</code>
% endif
</a>
% else:
@ -179,45 +179,70 @@
href="?version=${comment.pull_request_version_id}#comment-${comment.comment_id}"
>
${_('Outdated comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}
</a> |
</a>
<code class="action-divider">|</code>
% else:
<a class="tooltip pr-version"
title="${_('Comment from pull request version v{0}, latest v{1}').format(comment_ver, latest_ver)}"
href="${h.route_path('pullrequest_show',repo_name=comment.pull_request.target_repo.repo_name,pull_request_id=comment.pull_request.pull_request_id, version=comment.pull_request_version_id)}"
>
<code class="pr-version-num">
${'v{}'.format(comment_ver)}
</code>
</a> |
<code class="pr-version-num">${'v{}'.format(comment_ver)}</code>
</a>
<code class="action-divider">|</code>
% endif
% endif
% endif
## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
%if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
## permissions to delete
%if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id):
<a onclick="return Rhodecode.comments.editComment(this);"
class="edit-comment">${_('Edit')}</a>
| <a onclick="return Rhodecode.comments.deleteComment(this);"
class="delete-comment">${_('Delete')}</a>
%else:
<a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
| <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
%endif
%else:
<a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
| <a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
%endif
<details class="details-reset details-inline-block">
<summary class="noselect"><i class="icon-options cursor-pointer"></i></summary>
<details-menu class="details-dropdown">
<div class="dropdown-item">
${_('Comment')} #${comment.comment_id}
<span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${comment_model.get_url(comment,request, permalink=True, anchor='comment-{}'.format(comment.comment_id))}" title="${_('Copy permalink')}"></span>
</div>
## show delete comment if it's not a PR (regular comments) or it's PR that is not closed
## only super-admin, repo admin OR comment owner can delete, also hide delete if currently viewed comment is outdated
%if not outdated_at_ver and (not comment.pull_request or (comment.pull_request and not comment.pull_request.is_closed())):
## permissions to delete
%if comment.immutable is False and (c.is_super_admin or h.HasRepoPermissionAny('repository.admin')(c.repo_name) or comment.author.user_id == c.rhodecode_user.user_id):
<div class="dropdown-divider"></div>
<div class="dropdown-item">
<a onclick="return Rhodecode.comments.editComment(this);" class="btn btn-link btn-sm edit-comment">${_('Edit')}</a>
</div>
<div class="dropdown-item">
<a onclick="return Rhodecode.comments.deleteComment(this);" class="btn btn-link btn-sm btn-danger delete-comment">${_('Delete')}</a>
</div>
%else:
<div class="dropdown-divider"></div>
<div class="dropdown-item">
<a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
</div>
<div class="dropdown-item">
<a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
</div>
%endif
%else:
<div class="dropdown-divider"></div>
<div class="dropdown-item">
<a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Edit')}</a>
</div>
<div class="dropdown-item">
<a class="tooltip edit-comment link-disabled" disabled="disabled" title="${_('Action unavailable')}">${_('Delete')}</a>
</div>
%endif
</details-menu>
</details>
<code class="action-divider">|</code>
% if outdated_at_ver:
| <a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous outdated comment')}"> <i class="icon-angle-left"></i> </a>
| <a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="tooltip next-comment" title="${_('Jump to the next outdated comment')}"> <i class="icon-angle-right"></i></a>
<a onclick="return Rhodecode.comments.prevOutdatedComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous outdated comment')}"> <i class="icon-angle-left"></i> </a>
<a onclick="return Rhodecode.comments.nextOutdatedComment(this);" class="tooltip next-comment" title="${_('Jump to the next outdated comment')}"> <i class="icon-angle-right"></i></a>
% else:
| <a onclick="return Rhodecode.comments.prevComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous comment')}"> <i class="icon-angle-left"></i></a>
| <a onclick="return Rhodecode.comments.nextComment(this);" class="tooltip next-comment" title="${_('Jump to the next comment')}"> <i class="icon-angle-right"></i></a>
<a onclick="return Rhodecode.comments.prevComment(this);" class="tooltip prev-comment" title="${_('Jump to the previous comment')}"> <i class="icon-angle-left"></i></a>
<a onclick="return Rhodecode.comments.nextComment(this);" class="tooltip next-comment" title="${_('Jump to the next comment')}"> <i class="icon-angle-right"></i></a>
% endif
</div>

View file

@ -102,6 +102,11 @@
<%namespace name="diff_block" file="/changeset/diff_block.mako"/>
%for commit in c.commit_ranges:
## commit range header for each individual diff
<h3>
<a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=c.repo_name,commit_id=commit.raw_id)}">${('r%s:%s' % (commit.idx,h.short_id(commit.raw_id)))}</a>
</h3>
${cbdiffs.render_diffset_menu(c.changes[commit.raw_id])}
${cbdiffs.render_diffset(
diffset=c.changes[commit.raw_id],

View file

@ -61,6 +61,8 @@ return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
diffset_container_id = h.md5(diffset.target_ref)
collapse_all = len(diffset.files) > collapse_when_files_over
active_pattern_entries = h.get_active_pattern_entries(getattr(c, 'repo_name', None))
from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
%>
%if use_comments:
@ -208,13 +210,6 @@ return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
<a href="${h.current_route_path(request, fulldiff=1)}" onclick="return confirm('${_("Showing a big diff might take some time and resources, continue?")}')">${_('Show full diff')}</a>
</h2>
</div>
## commit range header for each individual diff
% elif commit and hasattr(c, 'commit_ranges') and len(c.commit_ranges) > 1:
<div class="diffset-heading ${(diffset.limited_diff and 'diffset-heading-warning' or '')}">
<div class="clearinner">
<a class="tooltip revision" title="${h.tooltip(commit.message)}" href="${h.route_path('repo_commit',repo_name=diffset.repo_name,commit_id=commit.raw_id)}">${('r%s:%s' % (commit.idx,h.short_id(commit.raw_id)))}</a>
</div>
</div>
% endif
<div id="todo-box">
@ -239,6 +234,43 @@ return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
<% over_lines_changed_limit = False %>
%for i, filediff in enumerate(diffset.files):
%if filediff.source_file_path and filediff.target_file_path:
%if filediff.source_file_path != filediff.target_file_path:
## file was renamed, or copied
%if RENAMED_FILENODE in filediff.patch['stats']['ops']:
<%
final_file_name = h.literal(u'{} <i class="icon-angle-left"></i> <del>{}</del>'.format(filediff.target_file_path, filediff.source_file_path))
final_path = filediff.target_file_path
%>
%elif COPIED_FILENODE in filediff.patch['stats']['ops']:
<%
final_file_name = h.literal(u'{} <i class="icon-angle-left"></i> {}'.format(filediff.target_file_path, filediff.source_file_path))
final_path = filediff.target_file_path
%>
%endif
%else:
## file was modified
<%
final_file_name = filediff.source_file_path
final_path = final_file_name
%>
%endif
%else:
%if filediff.source_file_path:
## file was deleted
<%
final_file_name = filediff.source_file_path
final_path = final_file_name
%>
%else:
## file was added
<%
final_file_name = filediff.target_file_path
final_path = final_file_name
%>
%endif
%endif
<%
lines_changed = filediff.patch['stats']['added'] + filediff.patch['stats']['deleted']
over_lines_changed_limit = lines_changed > lines_changed_limit
@ -258,13 +290,39 @@ return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
total_file_comments = [_c for _c in h.itertools.chain.from_iterable(file_comments) if not _c.outdated]
%>
<div class="filediff-collapse-indicator icon-"></div>
<span class="pill-group pull-right" >
<span class="pill" op="comments">
## Comments/Options PILL
<span class="pill-group pull-right">
<span class="pill" op="comments">
<i class="icon-comment"></i> ${len(total_file_comments)}
</span>
<details class="details-reset details-inline-block">
<summary class="noselect">
<i class="pill icon-options cursor-pointer" op="options"></i>
</summary>
<details-menu class="details-dropdown">
<div class="dropdown-item">
<span>${final_path}</span>
<span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="Copy file path"></span>
</div>
<div class="dropdown-divider"></div>
<div class="dropdown-item">
<% permalink = request.current_route_url(_anchor='a_{}'.format(h.FID(filediff.raw_id, filediff.patch['filename']))) %>
<a href="${permalink}">¶ permalink</a>
<span class="pull-right icon-clipboard clipboard-action" data-clipboard-text="${permalink}" title="Copy permalink"></span>
</div>
</details-menu>
</details>
</span>
${diff_ops(filediff)}
${diff_ops(final_file_name, filediff)}
</label>
@ -463,43 +521,15 @@ return '%s_%s_%i' % (h.md5_safe(commit+filename), type, line)
</div>
</%def>
<%def name="diff_ops(filediff)">
<%
from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
%>
<%def name="diff_ops(file_name, filediff)">
<%
from rhodecode.lib.diffs import NEW_FILENODE, DEL_FILENODE, \
MOD_FILENODE, RENAMED_FILENODE, CHMOD_FILENODE, BIN_FILENODE, COPIED_FILENODE
%>
<span class="pill">
<i class="icon-file-text"></i>
%if filediff.source_file_path and filediff.target_file_path:
%if filediff.source_file_path != filediff.target_file_path:
## file was renamed, or copied
%if RENAMED_FILENODE in filediff.patch['stats']['ops']:
${filediff.target_file_path} ⬅ <del>${filediff.source_file_path}</del>
<% final_path = filediff.target_file_path %>
%elif COPIED_FILENODE in filediff.patch['stats']['ops']:
${filediff.target_file_path} ⬅ ${filediff.source_file_path}
<% final_path = filediff.target_file_path %>
%endif
%else:
## file was modified
${filediff.source_file_path}
<% final_path = filediff.source_file_path %>
%endif
%else:
%if filediff.source_file_path:
## file was deleted
${filediff.source_file_path}
<% final_path = filediff.source_file_path %>
%else:
## file was added
${filediff.target_file_path}
<% final_path = filediff.target_file_path %>
%endif
%endif
<i style="color: #aaa" class="on-hover-icon icon-clipboard clipboard-action" data-clipboard-text="${final_path}" title="${_('Copy file path')}" onclick="return false;"></i>
${file_name}
</span>
## anchor link
<a class="pill filediff-anchor" href="#a_${h.FID(filediff.raw_id, filediff.patch['filename'])}">¶</a>
<span class="pill-group pull-right">

View file

@ -53,6 +53,14 @@ var data_hovercard_url = pyroutes.url('hovercard_user', {"user_id": user_id})
var reviewGroup = null;
var reviewGroupColor = 'transparent';
}
var rule_show = rule_show || false;
if (rule_show) {
var rule_visibility = 'table-cell';
} else {
var rule_visibility = 'none';
}
%>
<tr id="reviewer_<%= member.user_id %>" class="reviewer_entry" tooltip="Review Group" data-reviewer-user-id="<%= member.user_id %>">
@ -98,9 +106,9 @@ var data_hovercard_url = pyroutes.url('hovercard_user', {"user_id": user_id})
</td>
<% } else { %>
<td>
<td style="text-align: right;width: 10px;">
<% if (allowed_to_update) { %>
<div class="reviewer_member_remove action_button" onclick="reviewersController.removeReviewMember(<%= member.user_id %>, true)" style="visibility: <%= edit_visibility %>;">
<div class="reviewer_member_remove" onclick="reviewersController.removeReviewMember(<%= member.user_id %>, true)" style="visibility: <%= edit_visibility %>;">
<i class="icon-remove"></i>
</div>
<% } %>
@ -110,7 +118,7 @@ var data_hovercard_url = pyroutes.url('hovercard_user', {"user_id": user_id})
</tr>
<tr>
<td colspan="4" style="display: none" class="pr-user-rule-container">
<td colspan="4" style="display: <%= rule_visibility %>" class="pr-user-rule-container">
<input type="hidden" name="__start__" value="reviewer:mapping">
<%if (member.user_group && member.user_group.vote_rule) { %>

View file

@ -19,21 +19,74 @@
<div class="box">
${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
<div class="box pr-summary">
<div class="box">
<div class="summary-details block-left">
<div class="pr-details-title">
${_('New pull request')}
</div>
<div class="form" style="padding-top: 10px">
<!-- fields -->
<div class="fields" >
<div class="field">
## COMMIT FLOW
<div class="field">
<div class="label label-textarea">
<label for="commit_flow">${_('Commit flow')}:</label>
</div>
<div class="content">
<div class="flex-container">
<div style="width: 45%;">
<div class="panel panel-default source-panel">
<div class="panel-heading">
<h3 class="panel-title">${_('Source repository')}</h3>
</div>
<div class="panel-body">
<div style="display:none">${c.rhodecode_db_repo.description}</div>
${h.hidden('source_repo')}
${h.hidden('source_ref')}
<div id="pr_open_message"></div>
</div>
</div>
</div>
<div style="width: 90px; text-align: center; padding-top: 30px">
<div>
<i class="icon-right" style="font-size: 2.2em"></i>
</div>
<div style="position: relative; top: 10px">
<span class="tag tag">
<span id="switch_base"></span>
</span>
</div>
</div>
<div style="width: 45%;">
<div class="panel panel-default target-panel">
<div class="panel-heading">
<h3 class="panel-title">${_('Target repository')}</h3>
</div>
<div class="panel-body">
<div style="display:none" id="target_repo_desc"></div>
${h.hidden('target_repo')}
${h.hidden('target_ref')}
<span id="target_ref_loading" style="display: none">
${_('Loading refs...')}
</span>
</div>
</div>
</div>
</div>
</div>
</div>
## TITLE
<div class="field">
<div class="label">
<label for="pullrequest_title">${_('Title')}:</label>
</div>
@ -43,8 +96,9 @@
<p class="help-block">
Start the title with WIP: to prevent accidental merge of Work In Progress pull request before it's ready.
</p>
</div>
</div>
## DESC
<div class="field">
<div class="label label-textarea">
<label for="pullrequest_desc">${_('Description')}:</label>
@ -55,39 +109,49 @@
</div>
</div>
## REVIEWERS
<div class="field">
<div class="label label-textarea">
<label for="commit_flow">${_('Commit flow')}:</label>
</div>
## TODO: johbo: Abusing the "content" class here to get the
## desired effect. Should be replaced by a proper solution.
##ORG
<div class="content">
<strong>${_('Source repository')}:</strong>
${c.rhodecode_db_repo.description}
<label for="pullrequest_reviewers">${_('Reviewers')}:</label>
</div>
<div class="content">
${h.hidden('source_repo')}
${h.hidden('source_ref')}
</div>
## REVIEW RULES
<div id="review_rules" style="display: none" class="reviewers-title">
<div class="pr-details-title">
${_('Reviewer rules')}
</div>
<div class="pr-reviewer-rules">
## review rules will be appended here, by default reviewers logic
</div>
</div>
##OTHER, most Probably the PARENT OF THIS FORK
<div class="content">
## filled with JS
<div id="target_repo_desc"></div>
</div>
## REVIEWERS
<div class="reviewers-title">
<div class="pr-details-title">
${_('Pull request reviewers')}
<span class="calculate-reviewers"> - ${_('loading...')}</span>
</div>
</div>
<div id="reviewers" class="pr-details-content reviewers">
## members goes here, filled via JS based on initial selection !
<input type="hidden" name="__start__" value="review_members:sequence">
<table id="review_members" class="group_members">
## This content is loaded via JS and ReviewersPanel
</table>
<input type="hidden" name="__end__" value="review_members:sequence">
<div class="content">
${h.hidden('target_repo')}
${h.hidden('target_ref')}
<span id="target_ref_loading" style="display: none">
${_('Loading refs...')}
</span>
<div id="add_reviewer_input" class='ac'>
<div class="reviewer_ac">
${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
<div id="reviewers_container"></div>
</div>
</div>
</div>
</div>
</div>
## SUBMIT
<div class="field">
<div class="label label-textarea">
<label for="pullrequest_submit"></label>
@ -96,66 +160,14 @@
<div class="pr-submit-button">
<input id="pr_submit" class="btn" name="save" type="submit" value="${_('Submit Pull Request')}">
</div>
<div id="pr_open_message"></div>
</div>
</div>
<div class="pr-spacing-container"></div>
</div>
</div>
</div>
<div>
## AUTHOR
<div class="reviewers-title block-right">
<div class="pr-details-title">
${_('Author of this pull request')}
</div>
</div>
<div class="block-right pr-details-content reviewers">
<ul class="group_members">
<li>
${self.gravatar_with_user(c.rhodecode_user.email, 16, tooltip=True)}
</li>
</ul>
</div>
## REVIEW RULES
<div id="review_rules" style="display: none" class="reviewers-title block-right">
<div class="pr-details-title">
${_('Reviewer rules')}
</div>
<div class="pr-reviewer-rules">
## review rules will be appended here, by default reviewers logic
</div>
</div>
## REVIEWERS
<div class="reviewers-title block-right">
<div class="pr-details-title">
${_('Pull request reviewers')}
<span class="calculate-reviewers"> - ${_('loading...')}</span>
</div>
</div>
<div id="reviewers" class="block-right pr-details-content reviewers">
## members goes here, filled via JS based on initial selection !
<input type="hidden" name="__start__" value="review_members:sequence">
<ul id="review_members" class="group_members"></ul>
<input type="hidden" name="__end__" value="review_members:sequence">
<div id="add_reviewer_input" class='ac'>
<div class="reviewer_ac">
${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
<div id="reviewers_container"></div>
</div>
</div>
</div>
</div>
</div>
<div class="box">
<div>
## overview pulled by ajax
<div id="pull_request_overview"></div>
</div>
</div>
${h.end_form()}
</div>
@ -243,8 +255,6 @@
var diffDataHandler = function(data) {
$('#pull_request_overview').html(data);
var commitElements = data['commits'];
var files = data['files'];
var added = data['stats'][0]
@ -303,27 +313,33 @@
msg += '<input type="hidden" name="__end__" value="revisions:sequence">'
msg += _ngettext(
'This pull requests will consist of <strong>{0} commit</strong>.',
'This pull requests will consist of <strong>{0} commits</strong>.',
'Compare summary: <strong>{0} commit</strong>',
'Compare summary: <strong>{0} commits</strong>',
commitElements.length).format(commitElements.length)
msg += '\n';
msg += '';
msg += _ngettext(
'<strong>{0} file</strong> changed, ',
'<strong>{0} files</strong> changed, ',
'<strong>, and {0} file</strong> changed.',
'<strong>, and {0} files</strong> changed.',
files.length).format(files.length)
msg += '<span class="op-added">{0} lines inserted</span>, <span class="op-deleted">{1} lines deleted</span>.'.format(added, deleted)
msg += '\n\n <a class="" id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
msg += '\n Diff: <span class="op-added">{0} lines inserted</span>, <span class="op-deleted">{1} lines deleted </span>.'.format(added, deleted)
msg += '\n <a class="" id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
if (commitElements.length) {
var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
}
else {
prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
var noCommitsMsg = '<span class="alert-text-warning">{0}</span>'.format(
_gettext('There are no commits to merge.'));
prButtonLock(true, noCommitsMsg, 'compare');
}
//make both panels equal
$('.target-panel').height($('.source-panel').height())
};
reviewersController = new ReviewersController();
@ -429,10 +445,12 @@
var targetRepoChanged = function(repoData) {
// generate new DESC of target repo displayed next to select
$('#target_repo_desc').html(repoData['description']);
var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
$('#target_repo_desc').html(
"<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Switch base, and use as source.</a>".format(repoData['description'], prLink)
);
var title = _gettext('Switch target repository with the source.')
$('#switch_base').html("<a class=\"tooltip\" title=\"{0}\" href=\"{1}\">Switch sides</a>".format(title, prLink))
// generate dynamic select2 for refs.
initTargetRefs(repoData['refs']['select2_refs'],

View file

@ -1,6 +1,8 @@
<%inherit file="/base/base.mako"/>
<%namespace name="base" file="/base/base.mako"/>
<%namespace name="dt" file="/data_table/_dt_elements.mako"/>
<%namespace name="sidebar" file="/base/sidebar.mako"/>
<%def name="title()">
${_('{} Pull Request !{}').format(c.repo_name, c.pull_request.pull_request_id)}
@ -21,113 +23,6 @@
${self.repo_menu(active='showpullrequest')}
</%def>
<%def name="comments_table(comments, counter_num, todo_comments=False)">
<%
old_comments = False
if todo_comments:
cls_ = 'todos-content-table'
def sorter(entry):
user_id = entry.author.user_id
resolved = '1' if entry.resolved else '0'
if user_id == c.rhodecode_user.user_id:
# own comments first
user_id = 0
return '{}'.format(str(entry.comment_id).zfill(10000))
else:
cls_ = 'comments-content-table'
def sorter(entry):
user_id = entry.author.user_id
return '{}'.format(str(entry.comment_id).zfill(10000))
%>
<table class="todo-table ${cls_}" data-total-count="${len(comments)}" data-counter="${counter_num}">
% for loop_obj, comment_obj in h.looper(reversed(sorted(comments, key=sorter))):
<%
display = ''
_cls = ''
%>
<% comment_ver_index = comment_obj.get_index_version(getattr(c, 'versions', [])) %>
<%
prev_comment_ver_index = 0
if loop_obj.previous:
prev_comment_ver_index = loop_obj.previous.get_index_version(getattr(c, 'versions', []))
%>
<% hidden_at_ver = comment_obj.outdated_at_version_js(c.at_version_num) %>
<% is_from_old_ver = comment_obj.older_than_version_js(c.at_version_num) %>
<%
if (prev_comment_ver_index > comment_ver_index) and old_comments is False:
old_comments = True
%>
% if todo_comments:
% if comment_obj.resolved:
<% _cls = 'resolved-todo' %>
<% display = 'none' %>
% endif
% else:
## SKIP TODOs we display them in other area
% if comment_obj.is_todo:
<% display = 'none' %>
% endif
## Skip outdated comments
% if comment_obj.outdated:
<% display = 'none' %>
<% _cls = 'hidden-comment' %>
% endif
% endif
% if not todo_comments and old_comments:
<tr class="old-comments-marker">
<td colspan="3"> <code>comments from older versions</code> </td>
</tr>
## reset markers so we only show this marker once
<% old_comments = None %>
% endif
<tr class="${_cls}" style="display: ${display};">
<td class="td-todo-number">
<a class="${('todo-resolved' if comment_obj.resolved else '')} permalink"
href="#comment-${comment_obj.comment_id}"
onclick="return Rhodecode.comments.scrollToComment($('#comment-${comment_obj.comment_id}'), 0, ${hidden_at_ver})">
% if todo_comments:
% if comment_obj.is_inline:
<i class="tooltip icon-code" title="Inline TODO comment ${('made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else 'made in this version')}."></i>
% else:
<i class="tooltip icon-comment" title="General TODO comment ${('made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else 'made in this version')}."></i>
% endif
% else:
% if comment_obj.outdated:
<i class="tooltip icon-comment-toggle" title="Inline Outdated made in v${comment_ver_index}."></i>
% elif comment_obj.is_inline:
<i class="tooltip icon-code" title="Inline comment ${('made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else 'made in this version')}."></i>
% else:
<i class="tooltip icon-comment" title="General comment ${('made in older version (v{})'.format(comment_ver_index) if is_from_old_ver == 'true' else 'made in this version')}."></i>
% endif
% endif
#${comment_obj.comment_id}
</a>
</td>
<td class="td-todo-gravatar">
${base.gravatar(comment_obj.author.email, 16, user=comment_obj.author, tooltip=True, extra_class=['no-margin'])}
</td>
<td class="todo-comment-text-wrapper">
<div class="tooltip todo-comment-text timeago" title="${h.format_date(comment_obj.created_on)}" datetime="${comment_obj.created_on}${h.get_timezone(comment_obj.created_on, time_is_local=True)}">
<code>${h.chop_at_smart(comment_obj.text, '\n', suffix_if_chopped='...')}</code>
</div>
</td>
</tr>
% endfor
</table>
</%def>
<%def name="main()">
## Container to gather extracted Tickets
@ -140,6 +35,7 @@
// TODO: marcink switch this to pyroutes
AJAX_COMMENT_DELETE_URL = "${h.route_path('pullrequest_comment_delete',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id,comment_id='__COMMENT_ID__')}";
templateContext.pull_request_data.pull_request_id = ${c.pull_request.pull_request_id};
templateContext.pull_request_data.pull_request_version = '${request.GET.get('version', '')}';
</script>
<div class="box">
@ -226,7 +122,7 @@
${_('of')} <a href="${h.route_path('repo_summary', repo_name=c.pull_request.target_repo.repo_name)}">${c.pull_request.target_repo.repo_name}</a>
<a class="source-details-action" href="#expand-source-details" onclick="return versionController.toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
<a class="source-details-action" href="#expand-source-details" onclick="return toggleElement(this, '.source-details')" data-toggle-on='<i class="icon-angle-down">more details</i>' data-toggle-off='<i class="icon-angle-up">less details</i>'>
<i class="icon-angle-down">more details</i>
</a>
@ -643,101 +539,12 @@
</div>
### NAVBOG RIGHT
<style>
.right-sidebar {
position: fixed;
top: 0px;
bottom: 0;
right: 0;
background: #fafafa;
z-index: 50;
}
.right-sidebar {
border-left: 1px solid #dbdbdb;
}
.right-sidebar.right-sidebar-expanded {
width: 320px;
overflow: scroll;
}
.right-sidebar.right-sidebar-collapsed {
width: 50px;
padding: 0;
display: block;
overflow: hidden;
}
.sidenav {
float: right;
will-change: min-height;
background: #fafafa;
width: 100%;
padding-top: 50px;
}
.sidebar-toggle {
height: 30px;
text-align: center;
margin: 15px 0px 0 0;
}
.sidebar-toggle a {
}
.sidebar-content {
margin-left: 15px;
margin-right: 15px;
}
.sidebar-heading {
font-size: 1.2em;
font-weight: 700;
margin-top: 10px;
}
.sidebar-element {
margin-top: 20px;
}
.right-sidebar-collapsed-state {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 0 10px;
cursor: pointer;
font-size: 1.3em;
margin: 0 -15px;
}
.right-sidebar-collapsed-state:hover {
background-color: #dbd9da;
}
.old-comments-marker {
text-align: center;
}
.old-comments-marker td {
padding-top: 15px;
border-bottom: 1px solid #dbd9da;
}
#add_reviewer {
padding-top: 10px;
}
</style>
### NAV SIDEBAR
<aside class="right-sidebar right-sidebar-expanded" id="pr-nav-sticky" style="display: none">
<div class="sidenav navbar__inner" >
## TOGGLE
<div class="sidebar-toggle" onclick="toggleSidebar(); return false">
<a href="#toggleSidebar">
<a href="#toggleSidebar" class="grey-link-action">
</a>
</div>
@ -747,8 +554,12 @@
## RULES SUMMARY/RULES
<div class="sidebar-element clear-both">
<% vote_title = _ungettext(
'Status calculated based on votes from {} reviewer',
'Status calculated based on votes from {} reviewers', len(c.allowed_reviewers)).format(len(c.allowed_reviewers))
%>
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Reviewers')}">
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${vote_title}">
<i class="icon-circle review-status-${c.pull_request_review_status}"></i>
${len(c.allowed_reviewers)}
</div>
@ -769,7 +580,7 @@
## REVIEWERS
<div class="right-sidebar-expanded-state pr-details-title">
<span class="tooltip sidebar-heading" title="${_ungettext('Review status calculated based on {} reviewer vote', 'Review status calculated based on {} reviewers votes', len(c.allowed_reviewers)).format(len(c.allowed_reviewers))}">
<span class="tooltip sidebar-heading" title="${vote_title}">
<i class="icon-circle review-status-${c.pull_request_review_status}"></i>
${_('Reviewers')}
</span>
@ -846,7 +657,7 @@
% if not c.at_version:
% if c.resolved_comments:
<span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return versionController.toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
<span class="block-right action_button last-item noselect" onclick="$('.unresolved-todo-text').toggle(); return toggleElement(this, '.resolved-todo');" data-toggle-on="Show resolved" data-toggle-off="Hide resolved">Show resolved</span>
% else:
<span class="block-right last-item noselect">Show resolved</span>
% endif
@ -863,7 +674,7 @@
</table>
% else:
% if c.unresolved_comments + c.resolved_comments:
${comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
${sidebar.comments_table(c.unresolved_comments + c.resolved_comments, len(c.unresolved_comments), todo_comments=True)}
% else:
<table>
<tr>
@ -882,6 +693,8 @@
<div class="tooltip right-sidebar-collapsed-state" style="display: none" onclick="toggleSidebar(); return false" title="${_('Comments')}">
<i class="icon-comment" style="color: #949494"></i>
<span id="comments-count">${len(c.inline_comments_flat+c.comments)}</span>
<span class="display-none" id="general-comments-count">${len(c.comments)}</span>
<span class="display-none" id="inline-comments-count">${len(c.inline_comments_flat)}</span>
</div>
<div class="right-sidebar-expanded-state pr-details-title">
@ -903,7 +716,7 @@
</span>
% if outdated_comm_count_ver:
<span class="block-right action_button last-item noselect" onclick="return versionController.toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
<span class="block-right action_button last-item noselect" onclick="return toggleElement(this, '.hidden-comment');" data-toggle-on="Show outdated" data-toggle-off="Hide outdated">Show outdated</span>
% else:
<span class="block-right last-item noselect">Show hidden</span>
% endif
@ -912,7 +725,7 @@
<div class="right-sidebar-expanded-state pr-details-content">
% if c.inline_comments_flat + c.comments:
${comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
${sidebar.comments_table(c.inline_comments_flat + c.comments, len(c.inline_comments_flat+c.comments))}
% else:
<table>
<tr>
@ -942,7 +755,7 @@
<div class="right-sidebar-expanded-state pr-details-content">
<table>
<tr><td><code>${_('Pull Request Description')}</code></td></tr>
<tr><td><code>${_('In pull request description')}:</code></td></tr>
% if c.referenced_desc_issues:
% for ticket_dict in c.referenced_desc_issues:
<tr>
@ -961,7 +774,7 @@
</tr>
% endif
<tr><td style="padding-top: 10px"><code>${_('Commit Messages')}</code></td></tr>
<tr><td style="padding-top: 10px"><code>${_('In commit messages')}:</code></td></tr>
% if c.referenced_commit_issues:
% for ticket_dict in c.referenced_commit_issues:
<tr>
@ -992,451 +805,189 @@
## This JS needs to be at the end
<script type="text/javascript">
versionController = new VersionController();
versionController.init();
versionController = new VersionController();
versionController.init();
reviewersController = new ReviewersController();
commitsController = new CommitsController();
reviewersController = new ReviewersController();
commitsController = new CommitsController();
updateController = new UpdatePrController();
updateController = new UpdatePrController();
/** leak object to top level scope **/
window.PullRequestPresenceController;
window.reviewerRulesData = ${c.pull_request_default_reviewers_data_json | n};
window.setReviewersData = ${c.pull_request_set_reviewers_data_json | n};
(function () {
"use strict";
window.PullRequestPresenceController = function (channel) {
var self = this;
this.channel = channel;
this.users = {};
// custom code mirror
var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
this.storeUsers = function (users) {
self.users = {}
$.each(users, function(index, value) {
var userId = value.state.id;
self.users[userId] = value.state;
})
var PRDetails = {
editButton: $('#open_edit_pullrequest'),
closeButton: $('#close_edit_pullrequest'),
deleteButton: $('#delete_pullrequest'),
viewFields: $('#pr-desc, #pr-title'),
editFields: $('#pr-desc-edit, #pr-title-edit, .pr-save'),
init: function () {
var that = this;
this.editButton.on('click', function (e) {
that.edit();
});
this.closeButton.on('click', function (e) {
that.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();
}
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);
this.render()
}
};
this.handleChannelUpdate = function (data) {
if (data.channel === this.channel) {
this.storeUsers(data.state.users);
this.render()
}
};
/* subscribe our chat to topics that are interesting to it */
$.Topic('/connection_controller/channel_update').subscribe(this.handleChannelUpdate.bind(this));
$.Topic('/connection_controller/presence').subscribe(this.handlePresence.bind(this));
};
PRDetails.init();
ReviewersPanel.init(reviewerRulesData, setReviewersData);
window.showOutdated = function (self) {
$('.comment-inline.comment-outdated').show();
$('.filediff-outdated').show();
$('.showOutdatedComments').hide();
$('.hideOutdatedComments').show();
};
window.hideOutdated = function (self) {
$('.comment-inline.comment-outdated').hide();
$('.filediff-outdated').hide();
$('.hideOutdatedComments').hide();
$('.showOutdatedComments').show();
};
window.refreshMergeChecks = function () {
var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
$('.pull-request-merge').css('opacity', 0.3);
$('.action-buttons-extra').css('opacity', 0.3);
$('.pull-request-merge').load(
loadUrl, function () {
$('.pull-request-merge').css('opacity', 1);
$('.action-buttons-extra').css('opacity', 1);
}
);
};
window.closePullRequest = function (status) {
if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
return false;
}
// inject closing flag
$('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
$(generalCommentForm.statusChange).select2("val", status).trigger('change');
$(generalCommentForm.submitForm).submit();
};
//TODO this functionality is now missing
$('#show-outdated-comments').on('click', function (e) {
var button = $(this);
var outdated = $('.comment-outdated');
if (button.html() === "(Show)") {
button.html("(Hide)");
outdated.show();
} else {
button.html("(Show)");
outdated.hide();
}
});
$('#merge_pull_request_form').submit(function () {
if (!$('#merge_pull_request').attr('disabled')) {
$('#merge_pull_request').attr('disabled', 'disabled');
}
return true;
});
$('#edit_pull_request').on('click', function (e) {
var title = $('#pr-title-input').val();
var description = codeMirrorInstance.getValue();
var renderer = $('#pr-renderer-input').val();
editPullRequest(
"${c.repo_name}", "${c.pull_request.pull_request_id}",
title, description, renderer);
});
$('#update_pull_request').on('click', function (e) {
$(this).attr('disabled', 'disabled');
$(this).addClass('disabled');
$(this).html(_gettext('Saving...'));
reviewersController.updateReviewers(
"${c.repo_name}", "${c.pull_request.pull_request_id}");
});
// fixing issue with caches on firefox
$('#update_commits').removeAttr("disabled");
$('.show-inline-comments').on('click', function (e) {
var boxid = $(this).attr('data-comment-id');
var button = $(this);
if (button.hasClass("comments-visible")) {
$('#{0} .inline-comments'.format(boxid)).each(function (index) {
$(this).hide();
});
button.removeClass("comments-visible");
} else {
$('#{0} .inline-comments'.format(boxid)).each(function (index) {
$(this).show();
});
button.addClass("comments-visible");
}
});
$('.show-inline-comments').on('change', function (e) {
var show = 'none';
var target = e.currentTarget;
if (target.checked) {
show = ''
}
var boxid = $(target).attr('id_for');
var comments = $('#{0} .inline-comments'.format(boxid));
var fn_display = function (idx) {
$(this).css('display', show);
};
$(comments).each(fn_display);
var btns = $('#{0} .inline-comments-button'.format(boxid));
$(btns).each(fn_display);
});
// register submit callback on commentForm form to track TODOs
window.commentFormGlobalSubmitSuccessCallback = function () {
refreshMergeChecks();
};
ReviewerAutoComplete('#user');
})();
$(document).ready(function () {
$(function () {
var channel = '${c.pr_broadcast_channel}';
new ReviewerPresenceController(channel)
// custom code mirror
var codeMirrorInstance = $('#pr-description-input').get(0).MarkupForm.cm;
var PRDetails = {
editButton: $('#open_edit_pullrequest'),
closeButton: $('#close_edit_pullrequest'),
deleteButton: $('#delete_pullrequest'),
viewFields: $('#pr-desc, #pr-title'),
editFields: $('#pr-desc-edit, #pr-title-edit, .pr-save'),
init: function () {
var that = this;
this.editButton.on('click', function (e) {
that.edit();
});
this.closeButton.on('click', function (e) {
that.view();
});
},
edit: function (event) {
this.viewFields.hide();
this.editButton.hide();
this.deleteButton.hide();
this.closeButton.show();
this.editFields.show();
codeMirrorInstance.refresh();
},
view: function (event) {
this.editButton.show();
this.deleteButton.show();
this.editFields.hide();
this.closeButton.hide();
this.viewFields.show();
}
};
var ReviewersPanel = {
editButton: $('#open_edit_reviewers'),
closeButton: $('#close_edit_reviewers'),
addButton: $('#add_reviewer'),
removeButtons: $('.reviewer_member_remove,.reviewer_member_mandatory_remove'),
reviewRules: ${c.pull_request_default_reviewers_data_json | n},
setReviewers: ${c.pull_request_set_reviewers_data_json | n},
init: function () {
var self = this;
this.editButton.on('click', function (e) {
self.edit();
});
this.closeButton.on('click', function (e) {
self.close();
self.renderReviewers();
});
self.renderReviewers();
},
renderReviewers: function () {
$('#review_members').html('')
$.each(this.setReviewers.reviewers, function (key, val) {
var member = val;
var entry = renderTemplate('reviewMemberEntry', {
'member': member,
'mandatory': member.mandatory,
'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,
'create': false
});
$('#review_members').append(entry)
});
tooltipActivate();
},
edit: function (event) {
this.editButton.hide();
this.closeButton.show();
this.addButton.show();
$(this.removeButtons.selector).css('visibility', 'visible');
// review rules
reviewersController.loadReviewRules(this.reviewRules);
},
close: function (event) {
this.editButton.show();
this.closeButton.hide();
this.addButton.hide();
$(this.removeButtons.selector).css('visibility', 'hidden');
// hide review rules
reviewersController.hideReviewRules()
}
};
PRDetails.init();
ReviewersPanel.init();
showOutdated = function (self) {
$('.comment-inline.comment-outdated').show();
$('.filediff-outdated').show();
$('.showOutdatedComments').hide();
$('.hideOutdatedComments').show();
};
hideOutdated = function (self) {
$('.comment-inline.comment-outdated').hide();
$('.filediff-outdated').hide();
$('.hideOutdatedComments').hide();
$('.showOutdatedComments').show();
};
refreshMergeChecks = function () {
var loadUrl = "${request.current_route_path(_query=dict(merge_checks=1))}";
$('.pull-request-merge').css('opacity', 0.3);
$('.action-buttons-extra').css('opacity', 0.3);
$('.pull-request-merge').load(
loadUrl, function () {
$('.pull-request-merge').css('opacity', 1);
$('.action-buttons-extra').css('opacity', 1);
}
);
};
refreshComments = function () {
var params = {
'pull_request_id': templateContext.pull_request_data.pull_request_id,
'repo_name': templateContext.repo_name,
'version': '${request.GET.get('version', '')}',
};
var data = {"comments[]": ["1"]};
var loadUrl = pyroutes.url('pullrequest_comments', params);
var $targetElem = $('.comments-content-table');
$targetElem.css('opacity', 0.3);
$targetElem.load(
loadUrl, data, function (responseText, textStatus, jqXHR) {
if (jqXHR.status !== 200) {
return false;
}
var $counterElem = $('#comments-count');
var newCount = $(responseText).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);
tooltipActivate();
}
);
}
refreshTODOs = function () {
var params = {
'pull_request_id': templateContext.pull_request_data.pull_request_id,
'repo_name': templateContext.repo_name,
'version': '${request.GET.get('version', '')}',
};
var data = {"comments[]": ["1"]};
var loadUrl = pyroutes.url('pullrequest_todos', params);
var $targetElem = $('.todos-content-table');
$targetElem.css('opacity', 0.3);
$targetElem.load(
loadUrl, data, function (responseText, textStatus, jqXHR) {
if (jqXHR.status !== 200) {
return false;
}
var $counterElem = $('#todos-count')
var newCount = $(responseText).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);
tooltipActivate();
}
);
}
refreshAllComments = function() {
refreshComments();
refreshTODOs();
}
closePullRequest = function (status) {
if (!confirm(_gettext('Are you sure to close this pull request without merging?'))) {
return false;
}
// inject closing flag
$('.action-buttons-extra').append('<input type="hidden" class="close-pr-input" id="close_pull_request" value="1">');
$(generalCommentForm.statusChange).select2("val", status).trigger('change');
$(generalCommentForm.submitForm).submit();
};
$('#show-outdated-comments').on('click', function (e) {
var button = $(this);
var outdated = $('.comment-outdated');
if (button.html() === "(Show)") {
button.html("(Hide)");
outdated.show();
} else {
button.html("(Show)");
outdated.hide();
}
});
$('.show-inline-comments').on('change', function (e) {
var show = 'none';
var target = e.currentTarget;
if (target.checked) {
show = ''
}
var boxid = $(target).attr('id_for');
var comments = $('#{0} .inline-comments'.format(boxid));
var fn_display = function (idx) {
$(this).css('display', show);
};
$(comments).each(fn_display);
var btns = $('#{0} .inline-comments-button'.format(boxid));
$(btns).each(fn_display);
});
$('#merge_pull_request_form').submit(function () {
if (!$('#merge_pull_request').attr('disabled')) {
$('#merge_pull_request').attr('disabled', 'disabled');
}
return true;
});
$('#edit_pull_request').on('click', function (e) {
var title = $('#pr-title-input').val();
var description = codeMirrorInstance.getValue();
var renderer = $('#pr-renderer-input').val();
editPullRequest(
"${c.repo_name}", "${c.pull_request.pull_request_id}",
title, description, renderer);
});
$('#update_pull_request').on('click', function (e) {
$(this).attr('disabled', 'disabled');
$(this).addClass('disabled');
$(this).html(_gettext('Saving...'));
reviewersController.updateReviewers(
"${c.repo_name}", "${c.pull_request.pull_request_id}");
});
// fixing issue with caches on firefox
$('#update_commits').removeAttr("disabled");
$('.show-inline-comments').on('click', function (e) {
var boxid = $(this).attr('data-comment-id');
var button = $(this);
if (button.hasClass("comments-visible")) {
$('#{0} .inline-comments'.format(boxid)).each(function (index) {
$(this).hide();
});
button.removeClass("comments-visible");
} else {
$('#{0} .inline-comments'.format(boxid)).each(function (index) {
$(this).show();
});
button.addClass("comments-visible");
}
});
// register submit callback on commentForm form to track TODOs
window.commentFormGlobalSubmitSuccessCallback = function () {
refreshMergeChecks();
};
ReviewerAutoComplete('#user');
})
$(document).ready(function () {
var $sideBar = $('.right-sidebar');
var marginExpVal = '320'
var marginColVal = '50'
var marginExpanded = {'margin': '0 {0}px 0 0'.format(marginExpVal)};
var marginCollapsed = {'margin': '0 {0}px 0 0'.format(marginColVal)};
var marginExpandedHeader = {'margin': '0 -{0}px 0 0'.format(marginExpVal), 'z-index': 10000};
var marginCollapsedHeader = {'margin': '0 -{0}px 0 0'.format(marginColVal), 'z-index': 10000};
var updateStickyHeader = function() {
if (window.updateSticky !== undefined) {
// potentially our comments change the active window size, so we
// notify sticky elements
updateSticky()
}
}
var expandSidebar = function() {
var $sideBar = $('.right-sidebar');
$('.outerwrapper').css(marginExpanded);
$('.header').css(marginExpandedHeader);
$('.sidebar-toggle a').html('<i class="icon-right" style="margin-right: -10px"></i><i class="icon-right"></i>');
$('.right-sidebar-collapsed-state').hide();
$('.right-sidebar-expanded-state').show();
$sideBar.addClass('right-sidebar-expanded')
$sideBar.removeClass('right-sidebar-collapsed')
}
var collapseSidebar = function() {
var $sideBar = $('.right-sidebar');
$('.outerwrapper').css(marginCollapsed);
$('.header').css(marginCollapsedHeader);
$('.sidebar-toggle a').html('<i class="icon-left" style="margin-right: -10px"></i><i class="icon-left"></i>');
$('.right-sidebar-collapsed-state').show();
$('.right-sidebar-expanded-state').hide();
$sideBar.removeClass('right-sidebar-expanded')
$sideBar.addClass('right-sidebar-collapsed')
}
toggleSidebar = function () {
var $sideBar = $('.right-sidebar');
if ($sideBar.hasClass('right-sidebar-expanded')) {
// expanded -> collapsed transition
collapseSidebar();
var sidebarState = 'collapsed';
} else {
// collapsed -> expanded
expandSidebar();
var sidebarState = 'expanded';
}
// update our other sticky header in same context
updateStickyHeader();
storeUserSessionAttr('rc_user_session_attr.sidebarState', sidebarState);
}
var expanded = $sideBar.hasClass('right-sidebar-expanded');
if (templateContext.session_attrs.sidebarState === 'expanded') {
expanded = true
} else if (templateContext.session_attrs.sidebarState === 'collapsed') {
expanded = false
}
// show sidebar since it's hidden on load
$('.right-sidebar').show();
// init based on set initial class, or if defined user session attrs
if (expanded) {
expandSidebar();
updateStickyHeader();
} else {
collapseSidebar();
updateStickyHeader();
}
var channel = '${c.pr_broadcast_channel}';
new PullRequestPresenceController(channel)
})
</script>
})
</script>
</%def>