##// END OF EJS Templates
pull-requests: don't report abort type of ajax requests as errors.
marcink -
r2480:a15e9323 default
parent child Browse files
Show More
@@ -1,527 +1,530 b''
1 1 <%inherit file="/base/base.mako"/>
2 2
3 3 <%def name="title()">
4 4 ${c.repo_name} ${_('New pull request')}
5 5 </%def>
6 6
7 7 <%def name="breadcrumbs_links()">
8 8 ${_('New pull request')}
9 9 </%def>
10 10
11 11 <%def name="menu_bar_nav()">
12 12 ${self.menu_items(active='repositories')}
13 13 </%def>
14 14
15 15 <%def name="menu_bar_subnav()">
16 16 ${self.repo_menu(active='showpullrequest')}
17 17 </%def>
18 18
19 19 <%def name="main()">
20 20 <div class="box">
21 21 <div class="title">
22 22 ${self.repo_page_title(c.rhodecode_db_repo)}
23 23 </div>
24 24
25 25 ${h.secure_form(h.route_path('pullrequest_create', repo_name=c.repo_name, _query=request.GET.mixed()), id='pull_request_form', request=request)}
26 26
27 27 ${self.breadcrumbs()}
28 28
29 29 <div class="box pr-summary">
30 30
31 31 <div class="summary-details block-left">
32 32
33 33
34 34 <div class="pr-details-title">
35 35 ${_('Pull request summary')}
36 36 </div>
37 37
38 38 <div class="form" style="padding-top: 10px">
39 39 <!-- fields -->
40 40
41 41 <div class="fields" >
42 42
43 43 <div class="field">
44 44 <div class="label">
45 45 <label for="pullrequest_title">${_('Title')}:</label>
46 46 </div>
47 47 <div class="input">
48 48 ${h.text('pullrequest_title', c.default_title, class_="medium autogenerated-title")}
49 49 </div>
50 50 </div>
51 51
52 52 <div class="field">
53 53 <div class="label label-textarea">
54 54 <label for="pullrequest_desc">${_('Description')}:</label>
55 55 </div>
56 56 <div class="textarea text-area editor">
57 57 ${h.textarea('pullrequest_desc',size=30, )}
58 58 <span class="help-block">${_('Write a short description on this pull request')}</span>
59 59 </div>
60 60 </div>
61 61
62 62 <div class="field">
63 63 <div class="label label-textarea">
64 64 <label for="pullrequest_desc">${_('Commit flow')}:</label>
65 65 </div>
66 66
67 67 ## TODO: johbo: Abusing the "content" class here to get the
68 68 ## desired effect. Should be replaced by a proper solution.
69 69
70 70 ##ORG
71 71 <div class="content">
72 72 <strong>${_('Source repository')}:</strong>
73 73 ${c.rhodecode_db_repo.description}
74 74 </div>
75 75 <div class="content">
76 76 ${h.hidden('source_repo')}
77 77 ${h.hidden('source_ref')}
78 78 </div>
79 79
80 80 ##OTHER, most Probably the PARENT OF THIS FORK
81 81 <div class="content">
82 82 ## filled with JS
83 83 <div id="target_repo_desc"></div>
84 84 </div>
85 85
86 86 <div class="content">
87 87 ${h.hidden('target_repo')}
88 88 ${h.hidden('target_ref')}
89 89 <span id="target_ref_loading" style="display: none">
90 90 ${_('Loading refs...')}
91 91 </span>
92 92 </div>
93 93 </div>
94 94
95 95 <div class="field">
96 96 <div class="label label-textarea">
97 97 <label for="pullrequest_submit"></label>
98 98 </div>
99 99 <div class="input">
100 100 <div class="pr-submit-button">
101 101 ${h.submit('save',_('Submit Pull Request'),class_="btn")}
102 102 </div>
103 103 <div id="pr_open_message"></div>
104 104 </div>
105 105 </div>
106 106
107 107 <div class="pr-spacing-container"></div>
108 108 </div>
109 109 </div>
110 110 </div>
111 111 <div>
112 112 ## AUTHOR
113 113 <div class="reviewers-title block-right">
114 114 <div class="pr-details-title">
115 115 ${_('Author of this pull request')}
116 116 </div>
117 117 </div>
118 118 <div class="block-right pr-details-content reviewers">
119 119 <ul class="group_members">
120 120 <li>
121 121 ${self.gravatar_with_user(c.rhodecode_user.email, 16)}
122 122 </li>
123 123 </ul>
124 124 </div>
125 125
126 126 ## REVIEW RULES
127 127 <div id="review_rules" style="display: none" class="reviewers-title block-right">
128 128 <div class="pr-details-title">
129 129 ${_('Reviewer rules')}
130 130 </div>
131 131 <div class="pr-reviewer-rules">
132 132 ## review rules will be appended here, by default reviewers logic
133 133 </div>
134 134 </div>
135 135
136 136 ## REVIEWERS
137 137 <div class="reviewers-title block-right">
138 138 <div class="pr-details-title">
139 139 ${_('Pull request reviewers')}
140 140 <span class="calculate-reviewers"> - ${_('loading...')}</span>
141 141 </div>
142 142 </div>
143 143 <div id="reviewers" class="block-right pr-details-content reviewers">
144 144 ## members goes here, filled via JS based on initial selection !
145 145 <input type="hidden" name="__start__" value="review_members:sequence">
146 146 <ul id="review_members" class="group_members"></ul>
147 147 <input type="hidden" name="__end__" value="review_members:sequence">
148 148 <div id="add_reviewer_input" class='ac'>
149 149 <div class="reviewer_ac">
150 150 ${h.text('user', class_='ac-input', placeholder=_('Add reviewer or reviewer group'))}
151 151 <div id="reviewers_container"></div>
152 152 </div>
153 153 </div>
154 154 </div>
155 155 </div>
156 156 </div>
157 157 <div class="box">
158 158 <div>
159 159 ## overview pulled by ajax
160 160 <div id="pull_request_overview"></div>
161 161 </div>
162 162 </div>
163 163 ${h.end_form()}
164 164 </div>
165 165
166 166 <script type="text/javascript">
167 167 $(function(){
168 168 var defaultSourceRepo = '${c.default_repo_data['source_repo_name']}';
169 169 var defaultSourceRepoData = ${c.default_repo_data['source_refs_json']|n};
170 170 var defaultTargetRepo = '${c.default_repo_data['target_repo_name']}';
171 171 var defaultTargetRepoData = ${c.default_repo_data['target_refs_json']|n};
172 172
173 173 var $pullRequestForm = $('#pull_request_form');
174 174 var $sourceRepo = $('#source_repo', $pullRequestForm);
175 175 var $targetRepo = $('#target_repo', $pullRequestForm);
176 176 var $sourceRef = $('#source_ref', $pullRequestForm);
177 177 var $targetRef = $('#target_ref', $pullRequestForm);
178 178
179 179 var sourceRepo = function() { return $sourceRepo.eq(0).val() };
180 180 var sourceRef = function() { return $sourceRef.eq(0).val().split(':') };
181 181
182 182 var targetRepo = function() { return $targetRepo.eq(0).val() };
183 183 var targetRef = function() { return $targetRef.eq(0).val().split(':') };
184 184
185 185 var calculateContainerWidth = function() {
186 186 var maxWidth = 0;
187 187 var repoSelect2Containers = ['#source_repo', '#target_repo'];
188 188 $.each(repoSelect2Containers, function(idx, value) {
189 189 $(value).select2('container').width('auto');
190 190 var curWidth = $(value).select2('container').width();
191 191 if (maxWidth <= curWidth) {
192 192 maxWidth = curWidth;
193 193 }
194 194 $.each(repoSelect2Containers, function(idx, value) {
195 195 $(value).select2('container').width(maxWidth + 10);
196 196 });
197 197 });
198 198 };
199 199
200 200 var initRefSelection = function(selectedRef) {
201 201 return function(element, callback) {
202 202 // translate our select2 id into a text, it's a mapping to show
203 203 // simple label when selecting by internal ID.
204 204 var id, refData;
205 205 if (selectedRef === undefined) {
206 206 id = element.val();
207 207 refData = element.val().split(':');
208 208 } else {
209 209 id = selectedRef;
210 210 refData = selectedRef.split(':');
211 211 }
212 212
213 213 var text = refData[1];
214 214 if (refData[0] === 'rev') {
215 215 text = text.substring(0, 12);
216 216 }
217 217
218 218 var data = {id: id, text: text};
219 219
220 220 callback(data);
221 221 };
222 222 };
223 223
224 224 var formatRefSelection = function(item) {
225 225 var prefix = '';
226 226 var refData = item.id.split(':');
227 227 if (refData[0] === 'branch') {
228 228 prefix = '<i class="icon-branch"></i>';
229 229 }
230 230 else if (refData[0] === 'book') {
231 231 prefix = '<i class="icon-bookmark"></i>';
232 232 }
233 233 else if (refData[0] === 'tag') {
234 234 prefix = '<i class="icon-tag"></i>';
235 235 }
236 236
237 237 var originalOption = item.element;
238 238 return prefix + item.text;
239 239 };
240 240
241 241 // custom code mirror
242 242 var codeMirrorInstance = initPullRequestsCodeMirror('#pullrequest_desc');
243 243
244 244 reviewersController = new ReviewersController();
245 245
246 246 var queryTargetRepo = function(self, query) {
247 247 // cache ALL results if query is empty
248 248 var cacheKey = query.term || '__';
249 249 var cachedData = self.cachedDataSource[cacheKey];
250 250
251 251 if (cachedData) {
252 252 query.callback({results: cachedData.results});
253 253 } else {
254 254 $.ajax({
255 255 url: pyroutes.url('pullrequest_repo_destinations', {'repo_name': templateContext.repo_name}),
256 256 data: {query: query.term},
257 257 dataType: 'json',
258 258 type: 'GET',
259 259 success: function(data) {
260 260 self.cachedDataSource[cacheKey] = data;
261 261 query.callback({results: data.results});
262 262 },
263 263 error: function(data, textStatus, errorThrown) {
264 264 alert(
265 265 "Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
266 266 }
267 267 });
268 268 }
269 269 };
270 270
271 271 var queryTargetRefs = function(initialData, query) {
272 272 var data = {results: []};
273 273 // filter initialData
274 274 $.each(initialData, function() {
275 275 var section = this.text;
276 276 var children = [];
277 277 $.each(this.children, function() {
278 278 if (query.term.length === 0 ||
279 279 this.text.toUpperCase().indexOf(query.term.toUpperCase()) >= 0 ) {
280 280 children.push({'id': this.id, 'text': this.text})
281 281 }
282 282 });
283 283 data.results.push({'text': section, 'children': children})
284 284 });
285 285 query.callback({results: data.results});
286 286 };
287 287
288 288 var loadRepoRefDiffPreview = function() {
289 289
290 290 var url_data = {
291 291 'repo_name': targetRepo(),
292 292 'target_repo': sourceRepo(),
293 293 'source_ref': targetRef()[2],
294 294 'source_ref_type': 'rev',
295 295 'target_ref': sourceRef()[2],
296 296 'target_ref_type': 'rev',
297 297 'merge': true,
298 298 '_': Date.now() // bypass browser caching
299 299 }; // gather the source/target ref and repo here
300 300
301 301 if (sourceRef().length !== 3 || targetRef().length !== 3) {
302 302 prButtonLock(true, "${_('Please select source and target')}");
303 303 return;
304 304 }
305 305 var url = pyroutes.url('repo_compare', url_data);
306 306
307 307 // lock PR button, so we cannot send PR before it's calculated
308 308 prButtonLock(true, "${_('Loading compare ...')}", 'compare');
309 309
310 310 if (loadRepoRefDiffPreview._currentRequest) {
311 311 loadRepoRefDiffPreview._currentRequest.abort();
312 312 }
313 313
314 314 loadRepoRefDiffPreview._currentRequest = $.get(url)
315 315 .error(function(data, textStatus, errorThrown) {
316 alert(
317 "Error while processing request.\nError code {0} ({1}).".format(
318 data.status, data.statusText));
316 if (textStatus !== 'abort') {
317 alert(
318 "Error while processing request.\nError code {0} ({1}).".format(
319 data.status, data.statusText));
320 }
321
319 322 })
320 323 .done(function(data) {
321 324 loadRepoRefDiffPreview._currentRequest = null;
322 325 $('#pull_request_overview').html(data);
323 326
324 327 var commitElements = $(data).find('tr[commit_id]');
325 328
326 329 var prTitleAndDesc = getTitleAndDescription(
327 330 sourceRef()[1], commitElements, 5);
328 331
329 332 var title = prTitleAndDesc[0];
330 333 var proposedDescription = prTitleAndDesc[1];
331 334
332 335 var useGeneratedTitle = (
333 336 $('#pullrequest_title').hasClass('autogenerated-title') ||
334 337 $('#pullrequest_title').val() === "");
335 338
336 339 if (title && useGeneratedTitle) {
337 340 // use generated title if we haven't specified our own
338 341 $('#pullrequest_title').val(title);
339 342 $('#pullrequest_title').addClass('autogenerated-title');
340 343
341 344 }
342 345
343 346 var useGeneratedDescription = (
344 347 !codeMirrorInstance._userDefinedDesc ||
345 348 codeMirrorInstance.getValue() === "");
346 349
347 350 if (proposedDescription && useGeneratedDescription) {
348 351 // set proposed content, if we haven't defined our own,
349 352 // or we don't have description written
350 353 codeMirrorInstance._userDefinedDesc = false; // reset state
351 354 codeMirrorInstance.setValue(proposedDescription);
352 355 }
353 356
354 357 var msg = '';
355 358 if (commitElements.length === 1) {
356 359 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 1)}";
357 360 } else {
358 361 msg = "${_ungettext('This pull request will consist of __COMMITS__ commit.', 'This pull request will consist of __COMMITS__ commits.', 2)}";
359 362 }
360 363
361 364 msg += ' <a id="pull_request_overview_url" href="{0}" target="_blank">${_('Show detailed compare.')}</a>'.format(url);
362 365
363 366 if (commitElements.length) {
364 367 var commitsLink = '<a href="#pull_request_overview"><strong>{0}</strong></a>'.format(commitElements.length);
365 368 prButtonLock(false, msg.replace('__COMMITS__', commitsLink), 'compare');
366 369 }
367 370 else {
368 371 prButtonLock(true, "${_('There are no commits to merge.')}", 'compare');
369 372 }
370 373
371 374
372 375 });
373 376 };
374 377
375 378 var Select2Box = function(element, overrides) {
376 379 var globalDefaults = {
377 380 dropdownAutoWidth: true,
378 381 containerCssClass: "drop-menu",
379 382 dropdownCssClass: "drop-menu-dropdown"
380 383 };
381 384
382 385 var initSelect2 = function(defaultOptions) {
383 386 var options = jQuery.extend(globalDefaults, defaultOptions, overrides);
384 387 element.select2(options);
385 388 };
386 389
387 390 return {
388 391 initRef: function() {
389 392 var defaultOptions = {
390 393 minimumResultsForSearch: 5,
391 394 formatSelection: formatRefSelection
392 395 };
393 396
394 397 initSelect2(defaultOptions);
395 398 },
396 399
397 400 initRepo: function(defaultValue, readOnly) {
398 401 var defaultOptions = {
399 402 initSelection : function (element, callback) {
400 403 var data = {id: defaultValue, text: defaultValue};
401 404 callback(data);
402 405 }
403 406 };
404 407
405 408 initSelect2(defaultOptions);
406 409
407 410 element.select2('val', defaultSourceRepo);
408 411 if (readOnly === true) {
409 412 element.select2('readonly', true);
410 413 }
411 414 }
412 415 };
413 416 };
414 417
415 418 var initTargetRefs = function(refsData, selectedRef){
416 419 Select2Box($targetRef, {
417 420 query: function(query) {
418 421 queryTargetRefs(refsData, query);
419 422 },
420 423 initSelection : initRefSelection(selectedRef)
421 424 }).initRef();
422 425
423 426 if (!(selectedRef === undefined)) {
424 427 $targetRef.select2('val', selectedRef);
425 428 }
426 429 };
427 430
428 431 var targetRepoChanged = function(repoData) {
429 432 // generate new DESC of target repo displayed next to select
430 433 var prLink = pyroutes.url('pullrequest_new', {'repo_name': repoData['name']});
431 434 $('#target_repo_desc').html(
432 435 "<strong>${_('Target repository')}</strong>: {0}. <a href=\"{1}\">Use as source</a>".format(repoData['description'], prLink)
433 436 );
434 437
435 438 // generate dynamic select2 for refs.
436 439 initTargetRefs(repoData['refs']['select2_refs'],
437 440 repoData['refs']['selected_ref']);
438 441
439 442 };
440 443
441 444 var sourceRefSelect2 = Select2Box($sourceRef, {
442 445 placeholder: "${_('Select commit reference')}",
443 446 query: function(query) {
444 447 var initialData = defaultSourceRepoData['refs']['select2_refs'];
445 448 queryTargetRefs(initialData, query)
446 449 },
447 450 initSelection: initRefSelection()
448 451 }
449 452 );
450 453
451 454 var sourceRepoSelect2 = Select2Box($sourceRepo, {
452 455 query: function(query) {}
453 456 });
454 457
455 458 var targetRepoSelect2 = Select2Box($targetRepo, {
456 459 cachedDataSource: {},
457 460 query: $.debounce(250, function(query) {
458 461 queryTargetRepo(this, query);
459 462 }),
460 463 formatResult: formatResult
461 464 });
462 465
463 466 sourceRefSelect2.initRef();
464 467
465 468 sourceRepoSelect2.initRepo(defaultSourceRepo, true);
466 469
467 470 targetRepoSelect2.initRepo(defaultTargetRepo, false);
468 471
469 472 $sourceRef.on('change', function(e){
470 473 loadRepoRefDiffPreview();
471 474 reviewersController.loadDefaultReviewers(
472 475 sourceRepo(), sourceRef(), targetRepo(), targetRef());
473 476 });
474 477
475 478 $targetRef.on('change', function(e){
476 479 loadRepoRefDiffPreview();
477 480 reviewersController.loadDefaultReviewers(
478 481 sourceRepo(), sourceRef(), targetRepo(), targetRef());
479 482 });
480 483
481 484 $targetRepo.on('change', function(e){
482 485 var repoName = $(this).val();
483 486 calculateContainerWidth();
484 487 $targetRef.select2('destroy');
485 488 $('#target_ref_loading').show();
486 489
487 490 $.ajax({
488 491 url: pyroutes.url('pullrequest_repo_refs',
489 492 {'repo_name': templateContext.repo_name, 'target_repo_name':repoName}),
490 493 data: {},
491 494 dataType: 'json',
492 495 type: 'GET',
493 496 success: function(data) {
494 497 $('#target_ref_loading').hide();
495 498 targetRepoChanged(data);
496 499 loadRepoRefDiffPreview();
497 500 },
498 501 error: function(data, textStatus, errorThrown) {
499 502 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
500 503 }
501 504 })
502 505
503 506 });
504 507
505 508 prButtonLock(true, "${_('Please select source and target')}", 'all');
506 509
507 510 // auto-load on init, the target refs select2
508 511 calculateContainerWidth();
509 512 targetRepoChanged(defaultTargetRepoData);
510 513
511 514 $('#pullrequest_title').on('keyup', function(e){
512 515 $(this).removeClass('autogenerated-title');
513 516 });
514 517
515 518 % if c.default_source_ref:
516 519 // in case we have a pre-selected value, use it now
517 520 $sourceRef.select2('val', '${c.default_source_ref}');
518 521 loadRepoRefDiffPreview();
519 522 reviewersController.loadDefaultReviewers(
520 523 sourceRepo(), sourceRef(), targetRepo(), targetRef());
521 524 % endif
522 525
523 526 ReviewerAutoComplete('#user');
524 527 });
525 528 </script>
526 529
527 530 </%def>
General Comments 0
You need to be logged in to leave comments. Login now