Changeset - 3f50a5e8fc4d
[Not reviewed]
beta
0 5 0
Marcin Kuzminski - 13 years ago 2012-07-15 21:16:14
marcin@python-works.com
Added editing of pull-request reviewers.
5 files changed with 109 insertions and 26 deletions:
0 comments (0 inline, 0 general)
rhodecode/config/routing.py
Show inline comments
 
@@ -429,48 +429,53 @@ def make_map(config):
 
                 revision='tip', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('compare_url',
 
                 '/{repo_name:.*}/compare/{org_ref_type}@{org_ref}...{other_ref_type}@{other_ref}',
 
                 controller='compare', action='index',
 
                 conditions=dict(function=check_repo),
 
                 requirements=dict(org_ref_type='(branch|book|tag)',
 
                                   other_ref_type='(branch|book|tag)'))
 

	
 
    rmap.connect('pullrequest_home',
 
                 '/{repo_name:.*}/pull-request/new', controller='pullrequests',
 
                 action='index', conditions=dict(function=check_repo,
 
                                                 method=["GET"]))
 

	
 
    rmap.connect('pullrequest',
 
                 '/{repo_name:.*}/pull-request/new', controller='pullrequests',
 
                 action='create', conditions=dict(function=check_repo,
 
                                                  method=["POST"]))
 

	
 
    rmap.connect('pullrequest_show',
 
                 '/{repo_name:.*}/pull-request/{pull_request_id}',
 
                 controller='pullrequests',
 
                 action='show', conditions=dict(function=check_repo,
 
                                                method=["GET"]))
 
    rmap.connect('pullrequest_update',
 
                 '/{repo_name:.*}/pull-request/{pull_request_id}',
 
                 controller='pullrequests',
 
                 action='update', conditions=dict(function=check_repo,
 
                                                method=["PUT"]))
 

	
 
    rmap.connect('pullrequest_show_all',
 
                 '/{repo_name:.*}/pull-request',
 
                 controller='pullrequests',
 
                 action='show_all', conditions=dict(function=check_repo,
 
                                                method=["GET"]))
 

	
 
    rmap.connect('pullrequest_comment',
 
                 '/{repo_name:.*}/pull-request-comment/{pull_request_id}',
 
                 controller='pullrequests',
 
                 action='comment', conditions=dict(function=check_repo,
 
                                                method=["POST"]))
 

	
 
    rmap.connect('pullrequest_comment_delete',
 
                 '/{repo_name:.*}/pull-request-comment/{comment_id}/delete',
 
                controller='pullrequests', action='delete_comment',
 
                conditions=dict(function=check_repo, method=["DELETE"]))
 

	
 
    rmap.connect('summary_home', '/{repo_name:.*}/summary',
 
                controller='summary', conditions=dict(function=check_repo))
 

	
 
    rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
 
                controller='shortlog', conditions=dict(function=check_repo))
 

	
rhodecode/controllers/pullrequests.py
Show inline comments
 
@@ -146,48 +146,61 @@ class PullrequestsController(BaseRepoCon
 
        reviewers = req_p.getall('review_members')
 

	
 
        #TODO: wrap this into a FORM !!!
 

	
 
        title = req_p['pullrequest_title']
 
        description = req_p['pullrequest_desc']
 

	
 
        try:
 
            pull_request = PullRequestModel().create(
 
                self.rhodecode_user.user_id, org_repo, org_ref, other_repo,
 
                other_ref, revisions, reviewers, title, description
 
            )
 
            Session().commit()
 
            h.flash(_('Successfully opened new pull request'),
 
                    category='success')
 
        except Exception:
 
            h.flash(_('Error occurred during sending pull request'),
 
                    category='error')
 
            log.error(traceback.format_exc())
 
            return redirect(url('changelog_home', repo_name=org_repo,))
 

	
 
        return redirect(url('pullrequest_show', repo_name=other_repo,
 
                            pull_request_id=pull_request.pull_request_id))
 

	
 
    @NotAnonymous()
 
    @jsonify
 
    def update(self, repo_name, pull_request_id):
 
        pull_request = PullRequest.get_or_404(pull_request_id)
 
        if pull_request.is_closed():
 
            raise HTTPForbidden()
 

	
 
        reviewers_ids = map(int, filter(lambda v: v not in [None, ''],
 
                   request.POST.get('reviewers_ids', '').split(',')))
 
        PullRequestModel().update_reviewers(pull_request_id, reviewers_ids)
 
        Session.commit()
 
        return True
 

	
 
    def _load_compare_data(self, pull_request, enable_comments=True):
 
        """
 
        Load context data needed for generating compare diff
 

	
 
        :param pull_request:
 
        :type pull_request:
 
        """
 

	
 
        org_repo = pull_request.org_repo
 
        org_ref_type, org_ref_, org_ref = pull_request.org_ref.split(':')
 
        other_repo = pull_request.other_repo
 
        other_ref_type, other_ref, other_ref_ = pull_request.other_ref.split(':')
 

	
 
        org_ref = (org_ref_type, org_ref)
 
        other_ref = (other_ref_type, other_ref)
 

	
 
        c.org_repo = org_repo
 
        c.other_repo = other_repo
 

	
 
        c.cs_ranges, discovery_data = PullRequestModel().get_compare_data(
 
                                       org_repo, org_ref, other_repo, other_ref
 
                                      )
 

	
 
        c.statuses = c.rhodecode_db_repo.statuses([x.raw_id for x in
 
@@ -316,25 +329,25 @@ class PullrequestsController(BaseRepoCon
 
        data = {
 
           'target_id': h.safeid(h.safe_unicode(request.POST.get('f_path'))),
 
        }
 
        if comm:
 
            c.co = comm
 
            data.update(comm.get_dict())
 
            data.update({'rendered_text':
 
                         render('changeset/changeset_comment_block.html')})
 

	
 
        return data
 

	
 
    @jsonify
 
    def delete_comment(self, repo_name, comment_id):
 
        co = ChangesetComment.get(comment_id)
 
        if co.pull_request.is_closed():
 
            #don't allow deleting comments on closed pull request
 
            raise HTTPForbidden()
 

	
 
        owner = lambda: co.author.user_id == c.rhodecode_user.user_id
 
        if h.HasPermissionAny('hg.admin', 'repository.admin')() or owner:
 
            ChangesetCommentsModel().delete(comment=co)
 
            Session().commit()
 
            return True
 
        else:
 
            raise HTTPForbidden()
 
\ No newline at end of file
 
            raise HTTPForbidden()
rhodecode/model/pull_request.py
Show inline comments
 
@@ -76,48 +76,76 @@ class PullRequestModel(BaseModel):
 
            reviewer = PullRequestReviewers(_usr, new)
 
            self.sa.add(reviewer)
 

	
 
        #notification to reviewers
 
        notif = NotificationModel()
 

	
 
        subject = safe_unicode(
 
            h.link_to(
 
              _('%(user)s wants you to review pull request #%(pr_id)s') % \
 
                {'user': created_by_user.username,
 
                 'pr_id': new.pull_request_id},
 
              h.url('pullrequest_show', repo_name=other_repo.repo_name,
 
                    pull_request_id=new.pull_request_id,
 
                    qualified=True,
 
              )
 
            )
 
        )
 
        body = description
 
        notif.create(created_by=created_by_user, subject=subject, body=body,
 
                     recipients=reviewers,
 
                     type_=Notification.TYPE_PULL_REQUEST,)
 

	
 
        return new
 

	
 
    def update_reviewers(self, pull_request, reviewers_ids):
 
        reviewers_ids = set(reviewers_ids)
 
        pull_request = self.__get_pull_request(pull_request)
 
        current_reviewers = PullRequestReviewers.query()\
 
                            .filter(PullRequestReviewers.pull_request==
 
                                   pull_request)\
 
                            .all()
 
        current_reviewers_ids = set([x.user.user_id for x in current_reviewers])
 

	
 
        to_add = reviewers_ids.difference(current_reviewers_ids)
 
        to_remove = current_reviewers_ids.difference(reviewers_ids)
 

	
 
        log.debug("Adding %s reviewers" % to_add)
 
        log.debug("Removing %s reviewers" % to_remove)
 

	
 
        for uid in to_add:
 
            _usr = self._get_user(uid)
 
            reviewer = PullRequestReviewers(_usr, pull_request)
 
            self.sa.add(reviewer)
 

	
 
        for uid in to_remove:
 
            reviewer = PullRequestReviewers.query()\
 
                    .filter(PullRequestReviewers.user_id==uid,
 
                            PullRequestReviewers.pull_request==pull_request)\
 
                    .scalar()
 
            if reviewer:
 
                self.sa.delete(reviewer)
 

	
 
    def close_pull_request(self, pull_request):
 
        pull_request = self.__get_pull_request(pull_request)
 
        pull_request.status = PullRequest.STATUS_CLOSED
 
        pull_request.updated_on = datetime.datetime.now()
 
        self.sa.add(pull_request)
 

	
 
    def _get_changesets(self, org_repo, org_ref, other_repo, other_ref,
 
                        discovery_data):
 
        """
 
        Returns a list of changesets that are incoming from org_repo@org_ref
 
        to other_repo@other_ref
 

	
 
        :param org_repo:
 
        :type org_repo:
 
        :param org_ref:
 
        :type org_ref:
 
        :param other_repo:
 
        :type other_repo:
 
        :param other_ref:
 
        :type other_ref:
 
        :param tmp:
 
        :type tmp:
 
        """
 
        changesets = []
rhodecode/public/js/rhodecode.js
Show inline comments
 
@@ -480,48 +480,57 @@ var injectInlineForm = function(tr){
 
	  
 
	  setTimeout(function(){
 
		  // callbacks
 
		  tooltip_activate();
 
		  MentionsAutoComplete('text_'+lineno, 'mentions_container_'+lineno, 
 
	                         _USERS_AC_DATA, _GROUPS_AC_DATA);
 
		  YUD.get('text_'+lineno).focus();
 
	  },10)
 
};
 

	
 
var deleteComment = function(comment_id){
 
	var url = AJAX_COMMENT_DELETE_URL.replace('__COMMENT_ID__',comment_id);
 
    var postData = {'_method':'delete'};
 
    var success = function(o){
 
        var n = YUD.get('comment-tr-'+comment_id);
 
        var root = n.previousElementSibling.previousElementSibling;
 
        n.parentNode.removeChild(n);
 

	
 
        // scann nodes, and attach add button to last one
 
        placeAddButton(root);
 
    }
 
    ajaxPOST(url,postData,success);
 
}
 

	
 
var updateReviewers = function(reviewers_ids){
 
	var url = AJAX_UPDATE_PULLREQUEST;
 
	var postData = {'_method':'put',
 
			        'reviewers_ids': reviewers_ids};
 
	var success = function(o){
 
		window.location.reload();
 
	}
 
	ajaxPOST(url,postData,success);
 
}
 

	
 
var createInlineAddButton = function(tr){
 

	
 
	var label = TRANSLATION_MAP['add another comment'];
 
	
 
	var html_el = document.createElement('div');
 
	YUD.addClass(html_el, 'add-comment');
 
	html_el.innerHTML = '<span class="ui-btn">{0}</span>'.format(label);
 
	
 
	var add = new YAHOO.util.Element(html_el);
 
	add.on('click', function(e) {
 
		injectInlineForm(tr);
 
	});
 
	return add;
 
};
 

	
 
var getLineNo = function(tr) {
 
	var line;
 
	var o = tr.children[0].id.split('_');
 
	var n = tr.children[1].id.split('_');
 

	
 
	if (n.length >= 2) {
 
		line = n[n.length-1];
 
	} else if (o.length >= 2) {
rhodecode/templates/pullrequests/pullrequest_show.html
Show inline comments
 
@@ -14,133 +14,161 @@
 

	
 
<%def name="main()">
 

	
 
<div class="box">
 
    <!-- box / title -->
 
    <div class="title">
 
        ${self.breadcrumbs()}
 
    </div>
 
        %if c.pull_request.is_closed():
 
        <div style="padding:10px; font-size:22px;width:100%;text-align: center; color:#88D882">${_('Closed %s') % (h.age(c.pull_request.updated_on))}</div>
 
        %endif        
 
    <h3>${_('Title')}: ${c.pull_request.title} 
 
        <div class="changeset-status-container" style="float:none">
 
        %if c.current_changeset_status:
 
          <div title="${_('Pull request status')}" class="changeset-status-lbl">[${h.changeset_status_lbl(c.current_changeset_status)}]</div>
 
          <div class="changeset-status-ico" style="padding:4px"><img src="${h.url('/images/icons/flag_status_%s.png' % c.current_changeset_status)}" /></div>          
 
        %endif
 
        </div>
 
    </h3>
 
    <div style="white-space:pre-wrap;padding:3px 3px 5px 20px">${h.literal(c.pull_request.description)}</div>
 
    <div style="padding:4px 4px 10px 20px">
 
      <div>${_('Created on')}: ${h.fmt_date(c.pull_request.created_on)}</div>
 
    </div>
 

	
 
    ## REVIEWERS
 
    <div>
 
      <div class="table" style="float:right;width:46%;clear:none">
 
          <div id="body" class="diffblock">
 
              <div style="white-space:pre-wrap;padding:5px">${_('Pull request reviewers')}</div>
 
          </div>
 
          <div style="border: 1px solid #CCC">
 
            <div class="group_members_wrap">
 
              <ul class="group_members">
 
              %for user,status in c.pull_request_reviewers:
 
                <li>
 
                  <div class="group_member">
 
                    <div style="float:left;padding:3px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
 
                      <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
 
                    </div>
 
                    <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,20)}"/> </div>
 
                    <div style="float:left">${user.username}</div>
 
                  </div>
 
                </li>
 
              %endfor
 
              </ul>
 
            </div>                
 
          </div>
 
      </div>
 
      ##DIFF
 
      <div class="table" style="float:left;width:46%;clear:none">
 
      <div class="table" style="float:left;clear:none">
 
          <div id="body" class="diffblock">
 
              <div style="white-space:pre-wrap;padding:5px">${_('Compare view')}</div>
 
          </div>
 
          <div id="changeset_compare_view_content">
 
              ##CS
 
              <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Incoming changesets')}</div>
 
              <%include file="/compare/compare_cs.html" />
 
  
 
              ## FILES
 
              <div style="font-size:1.1em;font-weight: bold;clear:both;padding-top:10px">${_('Files affected')}</div>
 
              <div class="cs_files">
 
                %for fid, change, f, stat in c.files:
 
                    <div class="cs_${change}">
 
                      <div class="node">${h.link_to(h.safe_unicode(f),h.url.current(anchor=fid))}</div>
 
                      <div class="changes">${h.fancy_file_stats(stat)}</div>
 
                    </div>
 
                %endfor
 
              </div>
 
          </div>
 
      </div>
 
      ## REVIEWERS
 
       <div style="float:left; border-left:1px dashed #eee">
 
       <h4>${_('Pull request reviewers')}</h4>
 
        <div id="reviewers" style="padding:0px 0px 0px 15px">
 
          ## members goes here !
 
          <div class="group_members_wrap">
 
            <ul id="review_members" class="group_members">
 
            %for member,status in c.pull_request_reviewers:
 
              <li id="reviewer_${member.user_id}">
 
                <div class="reviewers_member">
 
                    <div style="float:left;padding:0px 3px 0px 0px" class="tooltip" title="${h.tooltip(h.changeset_status_lbl(status[0][1].status if status else 'not_reviewed'))}">
 
                      <img src="${h.url(str('/images/icons/flag_status_%s.png' % (status[0][1].status if status else 'not_reviewed')))}"/>
 
                    </div>                
 
                  <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div>
 
                  <div style="float:left">${member.full_name} (${_('owner')})</div>
 
                  <input type="hidden" value="${member.user_id}" name="review_members" />
 
                  %if not c.pull_request.is_closed():
 
                  <span class="delete_icon action_button" onclick="removeReviewer(${member.user_id})"></span>
 
                  %endif
 
                </div>
 
              </li>
 
            %endfor
 
            </ul>
 
          </div>                
 
          %if not c.pull_request.is_closed():
 
          <div class='ac'>
 
            <div class="reviewer_ac">
 
               ${h.text('user', class_='yui-ac-input')}
 
               <span class="help-block">${_('Add reviewer to this pull request.')}</span>
 
               <div id="reviewers_container"></div>       
 
            </div>
 
            <div style="padding:0px 10px">
 
             <span id="update_pull_request" class="ui-btn xsmall">${_('save')}</span>
 
            </div>
 
          </div>
 
          %endif
 
        </div>      
 
       </div>      
 
    </div>
 
    <script>
 
    var _USERS_AC_DATA = ${c.users_array|n};
 
    var _GROUPS_AC_DATA = ${c.users_groups_array|n};
 
    AJAX_COMMENT_URL = "${url('pullrequest_comment',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}";
 
    AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";  
 
    AJAX_COMMENT_DELETE_URL = "${url('pullrequest_comment_delete',repo_name=c.repo_name,comment_id='__COMMENT_ID__')}";
 
    AJAX_UPDATE_PULLREQUEST = "${url('pullrequest_update',repo_name=c.repo_name,pull_request_id=c.pull_request.pull_request_id)}"
 
    </script>
 

	
 
    ## diff block
 
    <%namespace name="diff_block" file="/changeset/diff_block.html"/>
 
    %for fid, change, f, stat in c.files:
 
      ${diff_block.diff_block_simple([c.changes[fid]])}
 
    %endfor
 

	
 
    ## template for inline comment form
 
    <%namespace name="comment" file="/changeset/changeset_file_comment.html"/>
 
    ${comment.comment_inline_form()}
 
    
 
    ## render comments and inlines
 
    ${comment.generate_comments()}
 
    
 
    % if not c.pull_request.is_closed():
 
      ## main comment form and it status
 
      ${comment.comments(h.url('pullrequest_comment', repo_name=c.repo_name,
 
                                pull_request_id=c.pull_request.pull_request_id),
 
                                c.current_changeset_status,
 
                                close_btn=True)}
 
    %endif
 

	
 
    <script type="text/javascript">
 
      YUE.onDOMReady(function(){
 
    	  PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA);
 

	
 
          YUE.on(YUQ('.show-inline-comments'),'change',function(e){
 
              var show = 'none';
 
              var target = e.currentTarget;
 
              if(target.checked){
 
                  var show = ''
 
              }
 
              var boxid = YUD.getAttribute(target,'id_for');
 
              var comments = YUQ('#{0} .inline-comments'.format(boxid));
 
              for(c in comments){
 
                 YUD.setStyle(comments[c],'display',show);
 
              }
 
              var btns = YUQ('#{0} .inline-comments-button'.format(boxid));
 
              for(c in btns){
 
                  YUD.setStyle(btns[c],'display',show);
 
               }
 
          })
 

	
 
          YUE.on(YUQ('.line'),'click',function(e){
 
              var tr = e.currentTarget;
 
              injectInlineForm(tr);
 
          });
 

	
 
          // inject comments into they proper positions
 
          var file_comments = YUQ('.inline-comment-placeholder');
 
          renderInlineComments(file_comments);
 
          
 
          YUE.on(YUD.get('update_pull_request'),'click',function(e){
 
        	  
 
        	  var reviewers_ids = [];
 
        	  var ids = YUQ('#review_members input');
 
        	  for(var i=0; i<ids.length;i++){
 
        		  var id = ids[i].value
 
        		  reviewers_ids.push(id);
 
        	  }
 
        	  updateReviewers(reviewers_ids);
 
          })
 
      })
 
    </script>
 

	
 
</div>
 

	
 
</%def>
0 comments (0 inline, 0 general)