Show More
@@ -36,7 +36,8 b' from pylons.decorators import jsonify' | |||||
36 |
|
36 | |||
37 | from rhodecode.lib.compat import json |
|
37 | from rhodecode.lib.compat import json | |
38 | from rhodecode.lib.base import BaseRepoController, render |
|
38 | from rhodecode.lib.base import BaseRepoController, render | |
39 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator |
|
39 | from rhodecode.lib.auth import LoginRequired, HasRepoPermissionAnyDecorator,\ | |
|
40 | NotAnonymous | |||
40 | from rhodecode.lib import helpers as h |
|
41 | from rhodecode.lib import helpers as h | |
41 | from rhodecode.lib import diffs |
|
42 | from rhodecode.lib import diffs | |
42 | from rhodecode.lib.utils import action_logger |
|
43 | from rhodecode.lib.utils import action_logger | |
@@ -58,6 +59,9 b' class PullrequestsController(BaseRepoCon' | |||||
58 | 'repository.admin') |
|
59 | 'repository.admin') | |
59 | def __before__(self): |
|
60 | def __before__(self): | |
60 | super(PullrequestsController, self).__before__() |
|
61 | super(PullrequestsController, self).__before__() | |
|
62 | repo_model = RepoModel() | |||
|
63 | c.users_array = repo_model.get_users_js() | |||
|
64 | c.users_groups_array = repo_model.get_users_groups_js() | |||
61 |
|
65 | |||
62 | def _get_repo_refs(self, repo): |
|
66 | def _get_repo_refs(self, repo): | |
63 | hist_l = [] |
|
67 | hist_l = [] | |
@@ -128,17 +132,10 b' class PullrequestsController(BaseRepoCon' | |||||
128 | } |
|
132 | } | |
129 |
|
133 | |||
130 | c.other_repos_info = json.dumps(other_repos_info) |
|
134 | c.other_repos_info = json.dumps(other_repos_info) | |
131 | c.review_members = [] |
|
135 | c.review_members = [org_repo.user] | |
132 | c.available_members = [] |
|
|||
133 | for u in User.query().filter(User.username != 'default').all(): |
|
|||
134 | uname = u.username |
|
|||
135 | if org_repo.user == u: |
|
|||
136 | uname = _('%s (owner)') % u.username |
|
|||
137 | # auto add owner to pull-request recipients |
|
|||
138 | c.review_members.append([u.user_id, uname]) |
|
|||
139 | c.available_members.append([u.user_id, uname]) |
|
|||
140 | return render('/pullrequests/pullrequest.html') |
|
136 | return render('/pullrequests/pullrequest.html') | |
141 |
|
137 | |||
|
138 | @NotAnonymous() | |||
142 | def create(self, repo_name): |
|
139 | def create(self, repo_name): | |
143 | req_p = request.POST |
|
140 | req_p = request.POST | |
144 | org_repo = req_p['org_repo'] |
|
141 | org_repo = req_p['org_repo'] | |
@@ -147,6 +144,7 b' class PullrequestsController(BaseRepoCon' | |||||
147 | other_ref = req_p['other_ref'] |
|
144 | other_ref = req_p['other_ref'] | |
148 | revisions = req_p.getall('revisions') |
|
145 | revisions = req_p.getall('revisions') | |
149 | reviewers = req_p.getall('review_members') |
|
146 | reviewers = req_p.getall('review_members') | |
|
147 | ||||
150 | #TODO: wrap this into a FORM !!! |
|
148 | #TODO: wrap this into a FORM !!! | |
151 |
|
149 | |||
152 | title = req_p['pullrequest_title'] |
|
150 | title = req_p['pullrequest_title'] |
@@ -1429,7 +1429,8 b' tbody .yui-dt-editable { cursor: pointer' | |||||
1429 | margin: 0 0 0 0px; |
|
1429 | margin: 0 0 0 0px; | |
1430 | } |
|
1430 | } | |
1431 |
|
1431 | |||
1432 |
#content div.box div.form div.fields div.field div.input input |
|
1432 | #content div.box div.form div.fields div.field div.input input, | |
|
1433 | .reviewer_ac input { | |||
1433 | background: #FFF; |
|
1434 | background: #FFF; | |
1434 | border-top: 1px solid #b3b3b3; |
|
1435 | border-top: 1px solid #b3b3b3; | |
1435 | border-left: 1px solid #b3b3b3; |
|
1436 | border-left: 1px solid #b3b3b3; | |
@@ -1549,12 +1550,21 b' input.disabled {' | |||||
1549 | padding: 5px 5px 5px 0; |
|
1550 | padding: 5px 5px 5px 0; | |
1550 | } |
|
1551 | } | |
1551 |
|
1552 | |||
1552 | #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 |
|
1553 | #content div.box div.form div.fields div.field input[type=text]:focus, | |
|
1554 | #content div.box div.form div.fields div.field input[type=password]:focus, | |||
|
1555 | #content div.box div.form div.fields div.field input[type=file]:focus, | |||
|
1556 | #content div.box div.form div.fields div.field textarea:focus, | |||
|
1557 | #content div.box div.form div.fields div.field select:focus, | |||
|
1558 | .reviewer_ac input:focus | |||
1553 | { |
|
1559 | { | |
1554 | background: #f6f6f6; |
|
1560 | background: #f6f6f6; | |
1555 | border-color: #666; |
|
1561 | border-color: #666; | |
1556 | } |
|
1562 | } | |
1557 |
|
1563 | |||
|
1564 | .reviewer_ac { | |||
|
1565 | padding:10px | |||
|
1566 | } | |||
|
1567 | ||||
1558 | div.form div.fields div.field div.button { |
|
1568 | div.form div.fields div.field div.button { | |
1559 | margin: 0; |
|
1569 | margin: 0; | |
1560 | padding: 0 0 0 8px; |
|
1570 | padding: 0 0 0 8px; | |
@@ -3783,6 +3793,11 b' div#legend_container table td,div#legend' | |||||
3783 | padding:0px 0px 0px 10px; |
|
3793 | padding:0px 0px 0px 10px; | |
3784 | } |
|
3794 | } | |
3785 |
|
3795 | |||
|
3796 | .reviewers_member{ | |||
|
3797 | height: 15px; | |||
|
3798 | padding:0px 0px 0px 10px; | |||
|
3799 | } | |||
|
3800 | ||||
3786 | .emails_wrap{ |
|
3801 | .emails_wrap{ | |
3787 | padding: 0px 20px; |
|
3802 | padding: 0px 20px; | |
3788 | } |
|
3803 | } |
@@ -63,6 +63,18 b' String.prototype.rstrip = function(char)' | |||||
63 | return this.replace(new RegExp(''+char+'+$'),''); |
|
63 | return this.replace(new RegExp(''+char+'+$'),''); | |
64 | } |
|
64 | } | |
65 |
|
65 | |||
|
66 | ||||
|
67 | if(!Array.prototype.indexOf) { | |||
|
68 | Array.prototype.indexOf = function(needle) { | |||
|
69 | for(var i = 0; i < this.length; i++) { | |||
|
70 | if(this[i] === needle) { | |||
|
71 | return i; | |||
|
72 | } | |||
|
73 | } | |||
|
74 | return -1; | |||
|
75 | }; | |||
|
76 | } | |||
|
77 | ||||
66 | /** |
|
78 | /** | |
67 | * SmartColorGenerator |
|
79 | * SmartColorGenerator | |
68 | * |
|
80 | * | |
@@ -1204,7 +1216,8 b' var MentionsAutoComplete = function (div' | |||||
1204 | return [unam, chunks]; |
|
1216 | return [unam, chunks]; | |
1205 | } |
|
1217 | } | |
1206 | return [null, null]; |
|
1218 | return [null, null]; | |
1207 |
}; |
|
1219 | }; | |
|
1220 | ||||
1208 | ownerAC.textboxKeyUpEvent.subscribe(function(type, args){ |
|
1221 | ownerAC.textboxKeyUpEvent.subscribe(function(type, args){ | |
1209 |
|
1222 | |||
1210 | var ac_obj = args[0]; |
|
1223 | var ac_obj = args[0]; | |
@@ -1229,6 +1242,167 b' var MentionsAutoComplete = function (div' | |||||
1229 | } |
|
1242 | } | |
1230 |
|
1243 | |||
1231 |
|
1244 | |||
|
1245 | var PullRequestAutoComplete = function (divid, cont, users_list, groups_list) { | |||
|
1246 | var myUsers = users_list; | |||
|
1247 | var myGroups = groups_list; | |||
|
1248 | ||||
|
1249 | // Define a custom search function for the DataSource of users | |||
|
1250 | var matchUsers = function (sQuery) { | |||
|
1251 | // Case insensitive matching | |||
|
1252 | var query = sQuery.toLowerCase(); | |||
|
1253 | var i = 0; | |||
|
1254 | var l = myUsers.length; | |||
|
1255 | var matches = []; | |||
|
1256 | ||||
|
1257 | // Match against each name of each contact | |||
|
1258 | for (; i < l; i++) { | |||
|
1259 | contact = myUsers[i]; | |||
|
1260 | if (((contact.fname+"").toLowerCase().indexOf(query) > -1) || | |||
|
1261 | ((contact.lname+"").toLowerCase().indexOf(query) > -1) || | |||
|
1262 | ((contact.nname) && ((contact.nname).toLowerCase().indexOf(query) > -1))) { | |||
|
1263 | matches[matches.length] = contact; | |||
|
1264 | } | |||
|
1265 | } | |||
|
1266 | return matches; | |||
|
1267 | }; | |||
|
1268 | ||||
|
1269 | // Define a custom search function for the DataSource of usersGroups | |||
|
1270 | var matchGroups = function (sQuery) { | |||
|
1271 | // Case insensitive matching | |||
|
1272 | var query = sQuery.toLowerCase(); | |||
|
1273 | var i = 0; | |||
|
1274 | var l = myGroups.length; | |||
|
1275 | var matches = []; | |||
|
1276 | ||||
|
1277 | // Match against each name of each contact | |||
|
1278 | for (; i < l; i++) { | |||
|
1279 | matched_group = myGroups[i]; | |||
|
1280 | if (matched_group.grname.toLowerCase().indexOf(query) > -1) { | |||
|
1281 | matches[matches.length] = matched_group; | |||
|
1282 | } | |||
|
1283 | } | |||
|
1284 | return matches; | |||
|
1285 | }; | |||
|
1286 | ||||
|
1287 | //match all | |||
|
1288 | var matchAll = function (sQuery) { | |||
|
1289 | u = matchUsers(sQuery); | |||
|
1290 | return u | |||
|
1291 | }; | |||
|
1292 | ||||
|
1293 | // DataScheme for owner | |||
|
1294 | var ownerDS = new YAHOO.util.FunctionDataSource(matchUsers); | |||
|
1295 | ||||
|
1296 | ownerDS.responseSchema = { | |||
|
1297 | fields: ["id", "fname", "lname", "nname", "gravatar_lnk"] | |||
|
1298 | }; | |||
|
1299 | ||||
|
1300 | // Instantiate AutoComplete for mentions | |||
|
1301 | var reviewerAC = new YAHOO.widget.AutoComplete(divid, cont, ownerDS); | |||
|
1302 | reviewerAC.useShadow = false; | |||
|
1303 | reviewerAC.resultTypeList = false; | |||
|
1304 | reviewerAC.suppressInputUpdate = true; | |||
|
1305 | reviewerAC.animVert = false; | |||
|
1306 | reviewerAC.animHoriz = false; | |||
|
1307 | reviewerAC.animSpeed = 0.1; | |||
|
1308 | ||||
|
1309 | // Helper highlight function for the formatter | |||
|
1310 | var highlightMatch = function (full, snippet, matchindex) { | |||
|
1311 | return full.substring(0, matchindex) | |||
|
1312 | + "<span class='match'>" | |||
|
1313 | + full.substr(matchindex, snippet.length) | |||
|
1314 | + "</span>" + full.substring(matchindex + snippet.length); | |||
|
1315 | }; | |||
|
1316 | ||||
|
1317 | // Custom formatter to highlight the matching letters | |||
|
1318 | reviewerAC.formatResult = function (oResultData, sQuery, sResultMatch) { | |||
|
1319 | var org_sQuery = sQuery; | |||
|
1320 | if(this.dataSource.mentionQuery != null){ | |||
|
1321 | sQuery = this.dataSource.mentionQuery; | |||
|
1322 | } | |||
|
1323 | ||||
|
1324 | var query = sQuery.toLowerCase(); | |||
|
1325 | var _gravatar = function(res, em, group){ | |||
|
1326 | if (group !== undefined){ | |||
|
1327 | em = '/images/icons/group.png' | |||
|
1328 | } | |||
|
1329 | tmpl = '<div class="ac-container-wrap"><img class="perm-gravatar-ac" src="{0}"/>{1}</div>' | |||
|
1330 | return tmpl.format(em,res) | |||
|
1331 | } | |||
|
1332 | if (oResultData.nname != undefined) { | |||
|
1333 | var fname = oResultData.fname || ""; | |||
|
1334 | var lname = oResultData.lname || ""; | |||
|
1335 | var nname = oResultData.nname; | |||
|
1336 | ||||
|
1337 | // Guard against null value | |||
|
1338 | var fnameMatchIndex = fname.toLowerCase().indexOf(query), | |||
|
1339 | lnameMatchIndex = lname.toLowerCase().indexOf(query), | |||
|
1340 | nnameMatchIndex = nname.toLowerCase().indexOf(query), | |||
|
1341 | displayfname, displaylname, displaynname; | |||
|
1342 | ||||
|
1343 | if (fnameMatchIndex > -1) { | |||
|
1344 | displayfname = highlightMatch(fname, query, fnameMatchIndex); | |||
|
1345 | } else { | |||
|
1346 | displayfname = fname; | |||
|
1347 | } | |||
|
1348 | ||||
|
1349 | if (lnameMatchIndex > -1) { | |||
|
1350 | displaylname = highlightMatch(lname, query, lnameMatchIndex); | |||
|
1351 | } else { | |||
|
1352 | displaylname = lname; | |||
|
1353 | } | |||
|
1354 | ||||
|
1355 | if (nnameMatchIndex > -1) { | |||
|
1356 | displaynname = "(" + highlightMatch(nname, query, nnameMatchIndex) + ")"; | |||
|
1357 | } else { | |||
|
1358 | displaynname = nname ? "(" + nname + ")" : ""; | |||
|
1359 | } | |||
|
1360 | ||||
|
1361 | return _gravatar(displayfname + " " + displaylname + " " + displaynname, oResultData.gravatar_lnk); | |||
|
1362 | } else { | |||
|
1363 | return ''; | |||
|
1364 | } | |||
|
1365 | }; | |||
|
1366 | ||||
|
1367 | //members cache to catch duplicates | |||
|
1368 | reviewerAC.dataSource.cache = []; | |||
|
1369 | // hack into select event | |||
|
1370 | if(reviewerAC.itemSelectEvent){ | |||
|
1371 | reviewerAC.itemSelectEvent.subscribe(function (sType, aArgs) { | |||
|
1372 | ||||
|
1373 | var myAC = aArgs[0]; // reference back to the AC instance | |||
|
1374 | var elLI = aArgs[1]; // reference to the selected LI element | |||
|
1375 | var oData = aArgs[2]; // object literal of selected item's result data | |||
|
1376 | var members = YUD.get('review_members'); | |||
|
1377 | //fill the autocomplete with value | |||
|
1378 | ||||
|
1379 | if (oData.nname != undefined) { | |||
|
1380 | if (myAC.dataSource.cache.indexOf(oData.id) != -1){ | |||
|
1381 | return | |||
|
1382 | } | |||
|
1383 | ||||
|
1384 | var tmpl = '<li>'+ | |||
|
1385 | '<div class="reviewers_member">'+ | |||
|
1386 | '<div class="gravatar"><img alt="gravatar" src="{0}"/> </div>'+ | |||
|
1387 | '<div style="float:left">{1}</div>'+ | |||
|
1388 | '<input type="hidden" value="{2}" name="review_members" />'+ | |||
|
1389 | '</div>'+ | |||
|
1390 | '</li>' | |||
|
1391 | ||||
|
1392 | var displayname = "{0} {1} ({2})".format(oData.fname,oData.lname,oData.nname); | |||
|
1393 | var element = tmpl.format(oData.gravatar_lnk,displayname,oData.id); | |||
|
1394 | members.innerHTML += element; | |||
|
1395 | myAC.dataSource.cache.push(oData.id); | |||
|
1396 | } | |||
|
1397 | }); | |||
|
1398 | } | |||
|
1399 | return { | |||
|
1400 | ownerDS: ownerDS, | |||
|
1401 | reviewerAC: reviewerAC, | |||
|
1402 | }; | |||
|
1403 | } | |||
|
1404 | ||||
|
1405 | ||||
1232 | /** |
|
1406 | /** | |
1233 | * QUICK REPO MENU |
|
1407 | * QUICK REPO MENU | |
1234 | */ |
|
1408 | */ |
@@ -69,40 +69,28 b'' | |||||
69 | <div style="float:left; border-left:1px dashed #eee"> |
|
69 | <div style="float:left; border-left:1px dashed #eee"> | |
70 | <h4>${_('Pull request reviewers')}</h4> |
|
70 | <h4>${_('Pull request reviewers')}</h4> | |
71 | <div id="reviewers" style="padding:0px 0px 0px 15px"> |
|
71 | <div id="reviewers" style="padding:0px 0px 0px 15px"> | |
72 | ##TODO: make this nicer :) |
|
72 | ## members goes here ! | |
73 | <table class="table noborder"> |
|
73 | <div class="group_members_wrap"> | |
74 | <tr> |
|
74 | <ul id="review_members" class="group_members"> | |
75 | <td> |
|
75 | %for member in c.review_members: | |
76 |
|
|
76 | <li> | |
77 | <div style="float:left"> |
|
77 | <div class="reviewers_member"> | |
78 | <div class="text" style="padding: 0px 0px 6px;">${_('Chosen reviewers')}</div> |
|
78 | <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(member.email,14)}"/> </div> | |
79 | ${h.select('review_members',[x[0] for x in c.review_members],c.review_members,multiple=True,size=8,style="min-width:210px")} |
|
79 | <div style="float:left">${member.full_name} (${_('owner')})</div> | |
80 | <div id="remove_all_elements" style="cursor:pointer;text-align:center"> |
|
80 | <input type="hidden" value="${member.user_id}" name="review_members" /> | |
81 | ${_('Remove all elements')} |
|
81 | </div> | |
82 | <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/> |
|
82 | </li> | |
83 | </div> |
|
83 | %endfor | |
84 | </div> |
|
84 | </ul> | |
85 | <div style="float:left;width:20px;padding-top:50px"> |
|
85 | </div> | |
86 | <img alt="add" id="add_element" |
|
86 | ||
87 | style="padding:2px;cursor:pointer" |
|
87 | <div class='ac'> | |
88 | src="${h.url('/images/icons/arrow_left.png')}"/> |
|
88 | <div class="reviewer_ac"> | |
89 | <br /> |
|
89 | ${h.text('user', class_='yui-ac-input')} | |
90 | <img alt="remove" id="remove_element" |
|
90 | <span class="help-block">${_('Add reviewer to this pull request.')}</span> | |
91 | style="padding:2px;cursor:pointer" |
|
91 | <div id="reviewers_container"></div> | |
92 | src="${h.url('/images/icons/arrow_right.png')}"/> |
|
92 | </div> | |
93 |
|
|
93 | </div> | |
94 | <div style="float:left"> |
|
|||
95 | <div class="text" style="padding: 0px 0px 6px;">${_('Available reviewers')}</div> |
|
|||
96 | ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")} |
|
|||
97 | <div id="add_all_elements" style="cursor:pointer;text-align:center"> |
|
|||
98 | <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/> |
|
|||
99 | ${_('Add all elements')} |
|
|||
100 | </div> |
|
|||
101 | </div> |
|
|||
102 | </div> |
|
|||
103 | </td> |
|
|||
104 | </tr> |
|
|||
105 | </table> |
|
|||
106 | </div> |
|
94 | </div> | |
107 | </div> |
|
95 | </div> | |
108 | <h3>${_('Create new pull request')}</h3> |
|
96 | <h3>${_('Create new pull request')}</h3> | |
@@ -141,7 +129,10 b'' | |||||
141 | </div> |
|
129 | </div> | |
142 |
|
130 | |||
143 | <script type="text/javascript"> |
|
131 | <script type="text/javascript"> | |
144 | MultiSelectWidget('review_members','available_members','pull_request_form'); |
|
132 | var _USERS_AC_DATA = ${c.users_array|n}; | |
|
133 | var _GROUPS_AC_DATA = ${c.users_groups_array|n}; | |||
|
134 | PullRequestAutoComplete('user', 'reviewers_container', _USERS_AC_DATA, _GROUPS_AC_DATA); | |||
|
135 | ||||
145 | var other_repos_info = ${c.other_repos_info|n}; |
|
136 | var other_repos_info = ${c.other_repos_info|n}; | |
146 | var loadPreview = function(){ |
|
137 | var loadPreview = function(){ | |
147 | YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none'); |
|
138 | YUD.setStyle(YUD.get('pull_request_overview_url').parentElement,'display','none'); |
General Comments 0
You need to be logged in to leave comments.
Login now