@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.

Workboards - add transactions for column changes

Summary:
adds ManiphestTransaction::TYPE_PROJECT_COLUMN and makes it work. Had to clean up the Javascript ever so slightly as it was sending up the string "null" when it should just omit the data in these cases. Ref T4422.

NOTE: this is overall a bit buggy - e.g. move a task Foo from column A to top of column B; refresh; task Foo is at bottom of column B and should be at top of column B - but I plan on additional diff or three to clean this up.

Test Plan: dragged tasks around columns. clicked on those tasks and saw some nice transactions.

Reviewers: epriestley

CC: Korvin, epriestley, aran

Maniphest Tasks: T4422

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

+141 -67
+60
src/applications/maniphest/editor/ManiphestTransactionEditor.php
··· 17 17 $types[] = ManiphestTransaction::TYPE_ATTACH; 18 18 $types[] = ManiphestTransaction::TYPE_EDGE; 19 19 $types[] = ManiphestTransaction::TYPE_SUBPRIORITY; 20 + $types[] = ManiphestTransaction::TYPE_PROJECT_COLUMN; 20 21 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 21 22 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 22 23 ··· 57 58 case ManiphestTransaction::TYPE_ATTACH: 58 59 return $object->getAttached(); 59 60 case ManiphestTransaction::TYPE_EDGE: 61 + case ManiphestTransaction::TYPE_PROJECT_COLUMN: 60 62 // These are pre-populated. 61 63 return $xaction->getOldValue(); 62 64 case ManiphestTransaction::TYPE_SUBPRIORITY: ··· 83 85 case ManiphestTransaction::TYPE_ATTACH: 84 86 case ManiphestTransaction::TYPE_EDGE: 85 87 case ManiphestTransaction::TYPE_SUBPRIORITY: 88 + case ManiphestTransaction::TYPE_PROJECT_COLUMN: 86 89 return $xaction->getNewValue(); 87 90 } 88 91 } ··· 101 104 sort($old); 102 105 sort($new); 103 106 return ($old !== $new); 107 + case ManiphestTransaction::TYPE_PROJECT_COLUMN: 108 + $new_column_phids = $new['columnPHIDs']; 109 + $old_column_phids = $old['columnPHIDs']; 110 + sort($new_column_phids); 111 + sort($old_column_phids); 112 + return ($old !== $new); 104 113 } 105 114 106 115 return parent::transactionHasEffect($object, $xaction); ··· 157 166 $data['newSubpriorityBase']); 158 167 $object->setSubpriority($new_sub); 159 168 return; 169 + case ManiphestTransaction::TYPE_PROJECT_COLUMN: 170 + // these do external (edge) updates 171 + return; 160 172 } 161 173 162 174 } ··· 186 198 protected function applyCustomExternalTransaction( 187 199 PhabricatorLiskDAO $object, 188 200 PhabricatorApplicationTransaction $xaction) { 201 + 202 + switch ($xaction->getTransactionType()) { 203 + case ManiphestTransaction::TYPE_PROJECT_COLUMN: 204 + $new = $xaction->getNewValue(); 205 + $old = $xaction->getOldValue(); 206 + $src = $object->getPHID(); 207 + $dst = head($new['columnPHIDs']); 208 + $edges = $old['columnPHIDs']; 209 + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; 210 + // NOTE: Normally, we expect only one edge to exist, but this works in 211 + // a general way so it will repair any stray edges. 212 + $remove = array(); 213 + $edge_missing = true; 214 + foreach ($edges as $phid) { 215 + if ($phid == $dst) { 216 + $edge_missing = false; 217 + } else { 218 + $remove[] = $phid; 219 + } 220 + } 221 + 222 + $add = array(); 223 + if ($edge_missing) { 224 + $add[] = $dst; 225 + } 226 + 227 + // This should never happen because of the code in 228 + // transactionHasEffect, but keep it for maximum conservativeness 229 + if (!$add && !$remove) { 230 + return; 231 + } 232 + 233 + $editor = id(new PhabricatorEdgeEditor()) 234 + ->setActor($this->getActor()) 235 + ->setSuppressEvents(true); 236 + 237 + foreach ($add as $phid) { 238 + $editor->addEdge($src, $edge_type, $phid); 239 + } 240 + foreach ($remove as $phid) { 241 + $editor->removeEdge($src, $edge_type, $phid); 242 + } 243 + $editor->save(); 244 + break; 245 + default: 246 + break; 247 + } 189 248 } 190 249 191 250 protected function shouldSendMail( 192 251 PhabricatorLiskDAO $object, 193 252 array $xactions) { 253 + 194 254 $should_mail = true; 195 255 if (count($xactions) == 1) { 196 256 $xaction = head($xactions);
+44
src/applications/maniphest/storage/ManiphestTransaction.php
··· 13 13 const TYPE_EDGE = 'edge'; 14 14 const TYPE_ATTACH = 'attach'; 15 15 const TYPE_SUBPRIORITY = 'subpriority'; 16 + const TYPE_PROJECT_COLUMN = 'projectcolumn'; 16 17 17 18 public function getApplicationName() { 18 19 return 'maniphest'; ··· 26 27 return new ManiphestTransactionComment(); 27 28 } 28 29 30 + public function shouldGenerateOldValue() { 31 + $generate = true; 32 + switch ($this->getTransactionType()) { 33 + case self::TYPE_PROJECT_COLUMN: 34 + case self::TYPE_EDGE: 35 + $generate = false; 36 + break; 37 + default: 38 + $generate = true; 39 + break; 40 + } 41 + return $generate; 42 + } 43 + 29 44 public function getRequiredHandlePHIDs() { 30 45 $phids = parent::getRequiredHandlePHIDs(); 31 46 ··· 50 65 nonempty($old, array()), 51 66 nonempty($new, array()), 52 67 )); 68 + break; 69 + case self::TYPE_PROJECT_COLUMN: 70 + $phids[] = $new['projectPHID']; 71 + $phids[] = head($new['columnPHIDs']); 53 72 break; 54 73 case self::TYPE_EDGE: 55 74 $phids = array_mergev( ··· 187 206 188 207 case self::TYPE_PROJECTS: 189 208 return pht('Changed Projects'); 209 + 210 + case self::TYPE_PROJECT_COLUMN: 211 + return pht('Changed Project Column'); 190 212 191 213 case self::TYPE_PRIORITY: 192 214 if ($old == ManiphestTaskPriority::getDefaultPriority()) { ··· 237 259 case self::TYPE_PROJECTS: 238 260 return 'project'; 239 261 262 + case self::TYPE_PROJECT_COLUMN: 263 + return 'workboard'; 264 + 240 265 case self::TYPE_PRIORITY: 241 266 if ($old == ManiphestTaskPriority::getDefaultPriority()) { 242 267 return 'normal-priority'; ··· 428 453 $this->renderHandleList($removed)); 429 454 } 430 455 456 + case self::TYPE_PROJECT_COLUMN: 457 + $project_phid = $new['projectPHID']; 458 + $column_phid = head($new['columnPHIDs']); 459 + return pht( 460 + '%s moved this task to %s on the %s workboard.', 461 + $this->renderHandleLink($author_phid), 462 + $this->renderHandleLink($column_phid), 463 + $this->renderHandleLink($project_phid)); 464 + break; 465 + 431 466 432 467 } 433 468 ··· 620 655 $this->renderHandleList($removed)); 621 656 } 622 657 658 + case self::TYPE_PROJECT_COLUMN: 659 + $project_phid = $new['projectPHID']; 660 + $column_phid = head($new['columnPHIDs']); 661 + return pht( 662 + '%s moved this task to %s on the %s workboard.', 663 + $this->renderHandleLink($author_phid), 664 + $this->renderHandleLink($column_phid), 665 + $this->renderHandleLink($project_phid)); 666 + break; 623 667 } 624 668 625 669 return parent::getTitleForFeed($story);
+20 -52
src/applications/project/controller/PhabricatorProjectMoveController.php
··· 49 49 ->execute(); 50 50 51 51 $columns = mpull($columns, null, 'getPHID'); 52 - if (empty($columns[$column_phid])) { 52 + $column = idx($columns, $column_phid); 53 + if (!$column) { 53 54 // User is trying to drop this object into a nonexistent column, just kick 54 55 // them out. 55 56 return new Aphront404Response(); 56 57 } 58 + 59 + $xactions = array(); 57 60 58 61 $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_COLUMN; 59 62 ··· 66 69 67 70 $edge_phids = $query->getDestinationPHIDs(); 68 71 69 - $this->rewriteEdges( 70 - $object->getPHID(), 71 - $edge_type, 72 - $column_phid, 73 - $edge_phids); 72 + $xactions[] = id(new ManiphestTransaction()) 73 + ->setTransactionType(ManiphestTransaction::TYPE_PROJECT_COLUMN) 74 + ->setNewValue(array( 75 + 'columnPHIDs' => array($column->getPHID()), 76 + 'projectPHID' => $column->getProjectPHID())) 77 + ->setOldValue(array( 78 + 'columnPHIDs' => $edge_phids, 79 + 'projectPHID' => $column->getProjectPHID())); 74 80 75 81 if ($after_phid) { 76 82 $after_task = id(new ManiphestTaskQuery()) ··· 84 90 $after_pri = $after_task->getPriority(); 85 91 $after_sub = $after_task->getSubpriority(); 86 92 87 - $xactions = array(id(new ManiphestTransaction()) 93 + $xactions[] = id(new ManiphestTransaction()) 88 94 ->setTransactionType(ManiphestTransaction::TYPE_SUBPRIORITY) 89 95 ->setNewValue(array( 90 96 'newPriority' => $after_pri, 91 - 'newSubpriorityBase' => $after_sub))); 92 - $editor = id(new ManiphestTransactionEditor()) 93 - ->setActor($viewer) 94 - ->setContinueOnMissingFields(true) 95 - ->setContinueOnNoEffect(true) 96 - ->setContentSourceFromRequest($request); 97 - 98 - $editor->applyTransactions($object, $xactions); 99 - } 100 - 101 - return id(new AphrontAjaxResponse())->setContent(array()); 102 - } 103 - 104 - private function rewriteEdges($src, $edge_type, $dst, array $edges) { 105 - $viewer = $this->getRequest()->getUser(); 106 - 107 - // NOTE: Normally, we expect only one edge to exist, but this works in a 108 - // general way so it will repair any stray edges. 109 - 110 - $remove = array(); 111 - $edge_missing = true; 112 - foreach ($edges as $phid) { 113 - if ($phid == $dst) { 114 - $edge_missing = false; 115 - } else { 116 - $remove[] = $phid; 117 - } 118 - } 119 - 120 - $add = array(); 121 - if ($edge_missing) { 122 - $add[] = $dst; 123 - } 124 - 125 - if (!$add && !$remove) { 126 - return; 97 + 'newSubpriorityBase' => $after_sub)); 127 98 } 128 99 129 - $editor = id(new PhabricatorEdgeEditor()) 100 + $editor = id(new ManiphestTransactionEditor()) 130 101 ->setActor($viewer) 131 - ->setSuppressEvents(true); 102 + ->setContinueOnMissingFields(true) 103 + ->setContinueOnNoEffect(true) 104 + ->setContentSourceFromRequest($request); 132 105 133 - foreach ($add as $phid) { 134 - $editor->addEdge($src, $edge_type, $phid); 135 - } 136 - foreach ($remove as $phid) { 137 - $editor->removeEdge($src, $edge_type, $phid); 138 - } 106 + $editor->applyTransactions($object, $xactions); 139 107 140 - $editor->save(); 108 + return id(new AphrontAjaxResponse())->setContent(array()); 141 109 } 142 110 143 111 }
+4 -13
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 743 743 "You can not apply transactions which already have commentVersions!"); 744 744 } 745 745 746 - $exempt_types = array( 747 - // CustomField logic currently prefills these before we enter the 748 - // transaction editor. 749 - PhabricatorTransactions::TYPE_CUSTOMFIELD => true, 750 - 751 - // TODO: Remove this, this edge type is encumbered with a bunch of 752 - // legacy nonsense. 753 - ManiphestTransaction::TYPE_EDGE => true, 754 - ); 755 - 756 - if (empty($exempt_types[$xaction->getTransactionType()])) { 757 - if ($xaction->getOldValue() !== null) { 746 + if (!$xaction->shouldGenerateOldValue()) { 747 + if ($xaction->getOldValue() === null) { 758 748 throw new Exception( 759 - "You can not apply transactions which already have oldValue!"); 749 + 'You can not apply transactions which should already have '. 750 + 'oldValue but do not!'); 760 751 } 761 752 } 762 753
+8
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 49 49 return $this->ignoreOnNoEffect; 50 50 } 51 51 52 + public function shouldGenerateOldValue() { 53 + switch ($this->getTransactionType()) { 54 + case PhabricatorTransactions::TYPE_CUSTOMFIELD: 55 + return false; 56 + } 57 + return true; 58 + } 59 + 52 60 abstract public function getApplicationTransactionType(); 53 61 54 62 private function getApplicationObjectTypeName() {
+5 -2
webroot/rsrc/js/application/projects/behavior-project-boards.js
··· 28 28 29 29 var data = { 30 30 objectPHID: JX.Stratcom.getData(item).objectPHID, 31 - columnPHID: JX.Stratcom.getData(list.getRootNode()).columnPHID, 32 - afterPHID: after && JX.Stratcom.getData(after).objectPHID 31 + columnPHID: JX.Stratcom.getData(list.getRootNode()).columnPHID 33 32 }; 33 + 34 + if (after) { 35 + data.afterPHID = JX.Stratcom.getData(after).objectPHID; 36 + } 34 37 35 38 var workflow = new JX.Workflow(config.moveURI, data) 36 39 .setHandler(function(response) {