@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Resolve relationship edit conflicts more naturally

Summary:
Ref T11179. Ref T4768. Currently, on `master`, if two users open "Edit Revisions" at the same time, then add revisions A and B, only the last state wins (just "B").

Instead, apply these as "add A" and "add B" so they merge in a natural way.

Test Plan:
- Opened edit dialog in two windows.
- Added "A" in one, "B" in the other.
- Saved both.
- Saw "Added A" and "Added B" transactions, instead of "Added A" and "Removed A, added B".

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4768, T11179

Differential Revision: https://secure.phabricator.com/D16164

+45 -15
+9 -9
resources/celerity/map.php
··· 11 11 'core.pkg.js' => 'f2139810', 12 12 'darkconsole.pkg.js' => 'e7393ebb', 13 13 'differential.pkg.css' => 'b3eea3f5', 14 - 'differential.pkg.js' => '4b7d8f19', 14 + 'differential.pkg.js' => '01a010d6', 15 15 'diffusion.pkg.css' => '91c5d3a6', 16 16 'diffusion.pkg.js' => '3a9a8bfa', 17 17 'maniphest.pkg.css' => '4845691a', ··· 495 495 'rsrc/js/core/behavior-lightbox-attachments.js' => 'f8ba29d7', 496 496 'rsrc/js/core/behavior-line-linker.js' => '1499a8cb', 497 497 'rsrc/js/core/behavior-more.js' => 'a80d0378', 498 - 'rsrc/js/core/behavior-object-selector.js' => '49b73b36', 498 + 'rsrc/js/core/behavior-object-selector.js' => '9030ebef', 499 499 'rsrc/js/core/behavior-oncopy.js' => '2926fff2', 500 500 'rsrc/js/core/behavior-phabricator-nav.js' => '56a1ca03', 501 501 'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => '116cf19b', ··· 658 658 'javelin-behavior-phabricator-line-linker' => '1499a8cb', 659 659 'javelin-behavior-phabricator-nav' => '56a1ca03', 660 660 'javelin-behavior-phabricator-notification-example' => '8ce821c5', 661 - 'javelin-behavior-phabricator-object-selector' => '49b73b36', 661 + 'javelin-behavior-phabricator-object-selector' => '9030ebef', 662 662 'javelin-behavior-phabricator-oncopy' => '2926fff2', 663 663 'javelin-behavior-phabricator-remarkup-assist' => '116cf19b', 664 664 'javelin-behavior-phabricator-reveal-content' => '60821bc7', ··· 1214 1214 'javelin-uri', 1215 1215 'phabricator-notification', 1216 1216 ), 1217 - '49b73b36' => array( 1218 - 'javelin-behavior', 1219 - 'javelin-dom', 1220 - 'javelin-request', 1221 - 'javelin-util', 1222 - ), 1223 1217 '4b700e9e' => array( 1224 1218 'javelin-behavior', 1225 1219 'javelin-dom', ··· 1607 1601 'javelin-behavior', 1608 1602 'javelin-dom', 1609 1603 'javelin-request', 1604 + ), 1605 + '9030ebef' => array( 1606 + 'javelin-behavior', 1607 + 'javelin-dom', 1608 + 'javelin-request', 1609 + 'javelin-util', 1610 1610 ), 1611 1611 '9196fb06' => array( 1612 1612 'javelin-install',
+8 -5
src/applications/search/controller/PhabricatorSearchRelationshipController.php
··· 44 44 $src_handle = $handles[$src_phid]; 45 45 46 46 $done_uri = $src_handle->getURI(); 47 + $initial_phids = $dst_phids; 47 48 48 49 if ($request->isFormPost()) { 49 50 $phids = explode(';', $request->getStr('phids')); 50 51 $phids = array_filter($phids); 51 52 $phids = array_values($phids); 52 53 53 - // TODO: Embed these in the form instead, to gracefully resolve 54 - // concurrent edits like we do for subscribers and projects. 55 - $old_phids = $dst_phids; 54 + $initial_phids = $request->getStrList('initialPHIDs'); 56 55 57 - $add_phids = $phids; 58 - $rem_phids = array_diff($old_phids, $add_phids); 56 + // Apply the changes as adds and removes relative to the original state 57 + // of the object when the dialog was rendered so that two users adding 58 + // relationships at the same time don't race and overwrite one another. 59 + $add_phids = array_diff($phids, $initial_phids); 60 + $rem_phids = array_diff($initial_phids, $phids); 59 61 60 62 if ($add_phids) { 61 63 $dst_objects = id(new PhabricatorObjectQuery()) ··· 149 151 150 152 return id(new PhabricatorObjectSelectorDialog()) 151 153 ->setUser($viewer) 154 + ->setInitialPHIDs($initial_phids) 152 155 ->setHandles($handles) 153 156 ->setFilters($filters) 154 157 ->setSelectedFilter('created')
+16
src/view/control/PhabricatorObjectSelectorDialog.php
··· 10 10 private $searchURI; 11 11 private $selectedFilter; 12 12 private $excluded; 13 + private $initialPHIDs; 13 14 14 15 private $title; 15 16 private $header; ··· 75 76 public function setInstructions($instructions) { 76 77 $this->instructions = $instructions; 77 78 return $this; 79 + } 80 + 81 + public function setInitialPHIDs(array $initial_phids) { 82 + $this->initialPHIDs = $initial_phids; 83 + return $this; 84 + } 85 + 86 + public function getInitialPHIDs() { 87 + return $this->initialPHIDs; 78 88 } 79 89 80 90 public function buildDialog() { ··· 171 181 $view = new PhabricatorHandleObjectSelectorDataView($handle); 172 182 $handle_views[$phid] = $view->renderData(); 173 183 } 184 + 174 185 $dialog->addHiddenInput('phids', implode(';', array_keys($this->handles))); 175 186 187 + $initial_phids = $this->getInitialPHIDs(); 188 + if ($initial_phids) { 189 + $initial_phids = implode(', ', $initial_phids); 190 + $dialog->addHiddenInput('initialPHIDs', $initial_phids); 191 + } 176 192 177 193 Javelin::initBehavior( 178 194 'phabricator-object-selector',
+12 -1
webroot/rsrc/js/core/behavior-object-selector.js
··· 18 18 var query_timer = null; 19 19 var query_delay = 50; 20 20 21 - var phid_input = JX.DOM.find( 21 + // TODO: This is fairly grotesque, but the dialog has two different forms 22 + // inside it and there's no way to sigil the inputs in the "real" form right 23 + // now. Clean this up when the dialog as a whole gets cleaned up. 24 + 25 + var inputs = JX.DOM.scry( 22 26 JX.$(config.form), 23 27 'input', 24 28 'aphront-dialog-application-input'); 29 + var phid_input; 30 + for (var ii = 0; ii < inputs.length; ii++) { 31 + if (inputs[ii].name == 'phids') { 32 + phid_input = inputs[ii]; 33 + break; 34 + } 35 + } 25 36 26 37 var last_value = JX.$(config.query).value; 27 38