diff --git a/rhodecode/controllers/pullrequests.py b/rhodecode/controllers/pullrequests.py --- a/rhodecode/controllers/pullrequests.py +++ b/rhodecode/controllers/pullrequests.py @@ -36,7 +36,8 @@ from pylons.decorators import jsonify from rhodecode.lib.compat import json from rhodecode.lib.base import BaseRepoController, render -from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator +from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\ + NotAnonymous from rhodecode.lib import helpers as h from rhodecode.lib import diffs from rhodecode.lib.utils import action_logger @@ -58,6 +59,9 @@ class PullrequestsController(BaseRepoCon 'repository.admin') def __before__(self): super(PullrequestsController, self).__before__() + repo_model = RepoModel() + c.users_array = repo_model.get_users_js() + c.users_groups_array = repo_model.get_users_groups_js() def _get_repo_refs(self, repo): hist_l = [] @@ -128,17 +132,10 @@ class PullrequestsController(BaseRepoCon } c.other_repos_info = json.dumps(other_repos_info) - c.review_members = [] - c.available_members = [] - for u in User.query().filter(User.username != 'default').all(): - uname = u.username - if org_repo.user == u: - uname = _('%s (owner)') % u.username - # auto add owner to pull-request recipients - c.review_members.append([u.user_id, uname]) - c.available_members.append([u.user_id, uname]) + c.review_members = [org_repo.user] return render('/pullrequests/pullrequest.html') + @NotAnonymous() def create(self, repo_name): req_p = request.POST org_repo = req_p['org_repo'] @@ -147,6 +144,7 @@ class PullrequestsController(BaseRepoCon other_ref = req_p['other_ref'] revisions = req_p.getall('revisions') reviewers = req_p.getall('review_members') + #TODO: wrap this into a FORM !!! title = req_p['pullrequest_title'] diff --git a/rhodecode/public/css/style.css b/rhodecode/public/css/style.css --- a/rhodecode/public/css/style.css +++ b/rhodecode/public/css/style.css @@ -1429,7 +1429,8 @@ tbody .yui-dt-editable { cursor: pointer margin: 0 0 0 0px; } -#content div.box div.form div.fields div.field div.input input { +#content div.box div.form div.fields div.field div.input input, +.reviewer_ac input { background: #FFF; border-top: 1px solid #b3b3b3; border-left: 1px solid #b3b3b3; @@ -1549,12 +1550,21 @@ input.disabled { padding: 5px 5px 5px 0; } -#content div.box div.form div.fields div.field input[type=text]:focus,#content div.box div.form div.fields div.field input[type=password]:focus,#content div.box div.form div.fields div.field input[type=file]:focus,#content div.box div.form div.fields div.field textarea:focus,#content div.box div.form div.fields div.field select:focus +#content div.box div.form div.fields div.field input[type=text]:focus, +#content div.box div.form div.fields div.field input[type=password]:focus, +#content div.box div.form div.fields div.field input[type=file]:focus, +#content div.box div.form div.fields div.field textarea:focus, +#content div.box div.form div.fields div.field select:focus, +.reviewer_ac input:focus { background: #f6f6f6; border-color: #666; } +.reviewer_ac { + padding:10px +} + div.form div.fields div.field div.button { margin: 0; padding: 0 0 0 8px; @@ -3783,6 +3793,11 @@ div#legend_container table td,div#legend padding:0px 0px 0px 10px; } +.reviewers_member{ + height: 15px; + padding:0px 0px 0px 10px; +} + .emails_wrap{ padding: 0px 20px; } diff --git a/rhodecode/public/js/rhodecode.js b/rhodecode/public/js/rhodecode.js --- a/rhodecode/public/js/rhodecode.js +++ b/rhodecode/public/js/rhodecode.js @@ -63,6 +63,18 @@ String.prototype.rstrip = function(char) return this.replace(new RegExp(''+char+'+$'),''); } + +if(!Array.prototype.indexOf) { + Array.prototype.indexOf = function(needle) { + for(var i = 0; i < this.length; i++) { + if(this[i] === needle) { + return i; + } + } + return -1; + }; +} + /** * SmartColorGenerator * @@ -1204,7 +1216,8 @@ var MentionsAutoComplete = function (div return [unam, chunks]; } return [null, null]; - }; + }; + ownerAC.textboxKeyUpEvent.subscribe(function(type, args){ var ac_obj = args[0]; @@ -1229,6 +1242,167 @@ var MentionsAutoComplete = function (div } +var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) { + var myUsers = users_list; + var myGroups = groups_list; + + // Define a custom search function for the DataSource of users + var matchUsers = function (sQuery) { + // Case insensitive matching + var query = sQuery.toLowerCase(); + var i = 0; + var l = myUsers.length; + var matches = []; + + // Match against each name of each contact + for (; i < l; i++) { + contact = myUsers[i]; + if (((contact.fname+"").toLowerCase().indexOf(query) > -1) || + ((contact.lname+"").toLowerCase().indexOf(query) > -1) || + ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) { + matches[matches.length] = contact; + } + } + return matches; + }; + + // Define a custom search function for the DataSource of usersGroups + var matchGroups = function (sQuery) { + // Case insensitive matching + var query = sQuery.toLowerCase(); + var i = 0; + var l = myGroups.length; + var matches = []; + + // Match against each name of each contact + for (; i < l; i++) { + matched_group = myGroups[i]; + if (matched_group.grname.toLowerCase().indexOf(query) > -1) { + matches[matches.length] = matched_group; + } + } + return matches; + }; + + //match all + var matchAll = function (sQuery) { + u = matchUsers(sQuery); + return u + }; + + // DataScheme for owner + var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); + + ownerDS.responseSchema = { + fields: ["id", "fname", "lname", "nname", "gravatar_lnk"] + }; + + // Instantiate AutoComplete for mentions + var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS); + reviewerAC.useShadow = false; + reviewerAC.resultTypeList = false; + reviewerAC.suppressInputUpdate = true; + reviewerAC.animVert = false; + reviewerAC.animHoriz = false; + reviewerAC.animSpeed = 0.1; + + // Helper highlight function for the formatter + var highlightMatch = function (full, snippet, matchindex) { + return full.substring(0, matchindex) + + "" + + full.substr(matchindex, snippet.length) + + "" + full.substring(matchindex + snippet.length); + }; + + // Custom formatter to highlight the matching letters + reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) { + var org_sQuery = sQuery; + if(this.dataSource.mentionQuery != null){ + sQuery = this.dataSource.mentionQuery; + } + + var query = sQuery.toLowerCase(); + var _gravatar = function(res, em, group){ + if (group !== undefined){ + em = '/images/icons/group.png' + } + tmpl = '
{1}
' + return tmpl.format(em,res) + } + if (oResultData.nname != undefined) { + var fname = oResultData.fname || ""; + var lname = oResultData.lname || ""; + var nname = oResultData.nname; + + // Guard against null value + var fnameMatchIndex = fname.toLowerCase().indexOf(query), + lnameMatchIndex = lname.toLowerCase().indexOf(query), + nnameMatchIndex = nname.toLowerCase().indexOf(query), + displayfname, displaylname, displaynname; + + if (fnameMatchIndex > -1) { + displayfname = highlightMatch(fname, query, fnameMatchIndex); + } else { + displayfname = fname; + } + + if (lnameMatchIndex > -1) { + displaylname = highlightMatch(lname, query, lnameMatchIndex); + } else { + displaylname = lname; + } + + if (nnameMatchIndex > -1) { + displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; + } else { + displaynname = nname ? "(" + nname + ")" : ""; + } + + return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk); + } else { + return ''; + } + }; + + //members cache to catch duplicates + reviewerAC.dataSource.cache = []; + // hack into select event + if(reviewerAC.itemSelectEvent){ + reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) { + + var myAC = aArgs[0]; // reference back to the AC instance + var elLI = aArgs[1]; // reference to the selected LI element + var oData = aArgs[2]; // object literal of selected item's result data + var members = YUD.get('review_members'); + //fill the autocomplete with value + + if (oData.nname != undefined) { + if (myAC.dataSource.cache.indexOf(oData.id) != -1){ + return + } + + var tmpl = '
  • '+ + '
    '+ + '
    gravatar
    '+ + '
    {1}
    '+ + ''+ + '
    '+ + '
  • ' + + var displayname = "{0} {1} ({2})".format(oData.fname,oData.lname,oData.nname); + var element = tmpl.format(oData.gravatar_lnk,displayname,oData.id); + members.innerHTML += element; + myAC.dataSource.cache.push(oData.id); + } + }); + } + return { + ownerDS: ownerDS, + reviewerAC: reviewerAC, + }; +} + + /** * QUICK REPO MENU */ diff --git a/rhodecode/templates/pullrequests/pullrequest.html b/rhodecode/templates/pullrequests/pullrequest.html --- a/rhodecode/templates/pullrequests/pullrequest.html +++ b/rhodecode/templates/pullrequests/pullrequest.html @@ -69,40 +69,28 @@

    ${_('Pull request reviewers')}

    - ##TODO: make this nicer :) - - - - -
    -
    -
    -
    ${_('Chosen reviewers')}
    - ${h.select('review_members',[x[0] for x in c.review_members],c.review_members,multiple=True,size=8,style="min-width:210px")} -
    - ${_('Remove all elements')} - remove -
    -
    -
    - add -
    - remove -
    -
    -
    ${_('Available reviewers')}
    - ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")} -
    - add - ${_('Add all elements')} -
    -
    -
    -
    + ## members goes here ! +
    +
      + %for member in c.review_members: +
    • +
      +
      gravatar
      +
      ${member.full_name} (${_('owner')})
      + +
      +
    • + %endfor +
    +
    + +
    +
    + ${h.text('user', class_='yui-ac-input')} + ${_('Add reviewer to this pull request.')} +
    +
    +

    ${_('Create new pull request')}

    @@ -141,7 +129,10 @@