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

Gut ManiphestTransactionEditor

Summary:
Ref T2217. Removes most of the code from ManiphestTransactionEditor.

- Provides mail tag support in ManiphestTransactionEditorPro.
- There was one more write (subscribe/unsubscribe button) that I'd missed; modernize that.

Test Plan:
- Clicked subscribe/unsubscribe.
- Made some edits, verified mail had appropriate mail tags.

Reviewers: btrahan, garoevans

Reviewed By: garoevans

CC: aran

Maniphest Tasks: T2217

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

+42 -478
+14 -6
src/applications/maniphest/controller/ManiphestSubscribeController.php
··· 20 20 return new Aphront404Response(); 21 21 } 22 22 23 + $ccs = $task->getCCPHIDs(); 23 24 switch ($this->action) { 24 25 case 'add': 25 - ManiphestTransactionEditor::addCC( 26 - $task, 27 - $user); 26 + $ccs[] = $user->getPHID(); 28 27 break; 29 28 case 'rem': 30 - ManiphestTransactionEditor::removeCC( 31 - $task, 32 - $user); 29 + $ccs = array_diff($ccs, array($user->getPHID())); 33 30 break; 34 31 default: 35 32 return new Aphront400Response(); 36 33 } 34 + 35 + $xaction = id(new ManiphestTransactionPro()) 36 + ->setTransactionType(ManiphestTransactionPro::TYPE_CCS) 37 + ->setNewValue($ccs); 38 + 39 + $editor = id(new ManiphestTransactionEditorPro()) 40 + ->setActor($user) 41 + ->setContentSourceFromRequest($request) 42 + ->setContinueOnNoEffect(true) 43 + ->setContinueOnMissingFields(true) 44 + ->applyTransactions($task, array($xaction)); 37 45 38 46 return id(new AphrontRedirectResponse())->setURI('/T'.$task->getID()); 39 47 }
-472
src/applications/maniphest/editor/ManiphestTransactionEditor.php
··· 5 5 */ 6 6 final class ManiphestTransactionEditor extends PhabricatorEditor { 7 7 8 - private $parentMessageID; 9 - private $auxiliaryFields = array(); 10 - 11 - public function setAuxiliaryFields(array $fields) { 12 - assert_instances_of($fields, 'ManiphestCustomField'); 13 - $this->auxiliaryFields = $fields; 14 - return $this; 15 - } 16 - 17 - public function setParentMessageID($parent_message_id) { 18 - $this->parentMessageID = $parent_message_id; 19 - return $this; 20 - } 21 - 22 - public function applyTransactions(ManiphestTask $task, array $transactions) { 23 - assert_instances_of($transactions, 'ManiphestTransaction'); 24 - 25 - $email_cc = $task->getCCPHIDs(); 26 - 27 - $email_to = array(); 28 - $email_to[] = $task->getOwnerPHID(); 29 - 30 - $pri_changed = $this->isCreate($transactions); 31 - $aux_writes = array(); 32 - 33 - foreach ($transactions as $key => $transaction) { 34 - $type = $transaction->getTransactionType(); 35 - $new = $transaction->getNewValue(); 36 - $email_to[] = $transaction->getAuthorPHID(); 37 - 38 - $value_is_phid_set = false; 39 - 40 - switch ($type) { 41 - case PhabricatorTransactions::TYPE_COMMENT: 42 - $old = null; 43 - break; 44 - case ManiphestTransactionType::TYPE_STATUS: 45 - $old = $task->getStatus(); 46 - break; 47 - case ManiphestTransactionType::TYPE_OWNER: 48 - $old = $task->getOwnerPHID(); 49 - break; 50 - case ManiphestTransactionType::TYPE_CCS: 51 - $old = $task->getCCPHIDs(); 52 - $value_is_phid_set = true; 53 - break; 54 - case ManiphestTransactionType::TYPE_PRIORITY: 55 - $old = $task->getPriority(); 56 - break; 57 - case ManiphestTransactionType::TYPE_EDGE: 58 - $old = $transaction->getOldValue(); 59 - break; 60 - case ManiphestTransactionType::TYPE_ATTACH: 61 - $old = $task->getAttached(); 62 - break; 63 - case ManiphestTransactionType::TYPE_TITLE: 64 - $old = $task->getTitle(); 65 - break; 66 - case ManiphestTransactionType::TYPE_DESCRIPTION: 67 - $old = $task->getDescription(); 68 - break; 69 - case ManiphestTransactionType::TYPE_PROJECTS: 70 - $old = $task->getProjectPHIDs(); 71 - $value_is_phid_set = true; 72 - break; 73 - case PhabricatorTransactions::TYPE_CUSTOMFIELD: 74 - $aux_key = $transaction->getMetadataValue('customfield:key'); 75 - if (!$aux_key) { 76 - throw new Exception( 77 - "Expected 'customfield:key' metadata on TYPE_CUSTOMFIELD ". 78 - "transaction."); 79 - } 80 - // This has already been populated. 81 - $old = $transaction->getOldValue(); 82 - break; 83 - default: 84 - throw new Exception('Unknown action type.'); 85 - } 86 - 87 - $old_cmp = $old; 88 - $new_cmp = $new; 89 - if ($value_is_phid_set) { 90 - 91 - // Normalize the old and new values if they are PHID sets so we don't 92 - // get any no-op transactions where the values differ only by keys, 93 - // order, duplicates, etc. 94 - 95 - if (is_array($old)) { 96 - $old = array_filter($old); 97 - $old = array_unique($old); 98 - sort($old); 99 - $old = array_values($old); 100 - $old_cmp = $old; 101 - } 102 - 103 - if (is_array($new)) { 104 - $new = array_filter($new); 105 - $new = array_unique($new); 106 - $transaction->setNewValue($new); 107 - 108 - $new_cmp = $new; 109 - sort($new_cmp); 110 - $new_cmp = array_values($new_cmp); 111 - } 112 - } 113 - 114 - if (($old !== null) && ($old_cmp == $new_cmp)) { 115 - if (count($transactions) > 1 && !$transaction->hasComments()) { 116 - // If we have at least one other transaction and this one isn't 117 - // doing anything and doesn't have any comments, just throw it 118 - // away. 119 - unset($transactions[$key]); 120 - continue; 121 - } else { 122 - $transaction->setOldValue(null); 123 - $transaction->setNewValue(null); 124 - $transaction->setTransactionType( 125 - PhabricatorTransactions::TYPE_COMMENT); 126 - } 127 - } else { 128 - switch ($type) { 129 - case PhabricatorTransactions::TYPE_COMMENT: 130 - break; 131 - case ManiphestTransactionType::TYPE_STATUS: 132 - $task->setStatus($new); 133 - break; 134 - case ManiphestTransactionType::TYPE_OWNER: 135 - if ($new) { 136 - $handle = id(new PhabricatorHandleQuery()) 137 - ->setViewer($this->getActor()) 138 - ->withPHIDs(array($new)) 139 - ->executeOne(); 140 - $task->setOwnerOrdering($handle->getName()); 141 - } else { 142 - $task->setOwnerOrdering(null); 143 - } 144 - $task->setOwnerPHID($new); 145 - break; 146 - case ManiphestTransactionType::TYPE_CCS: 147 - $task->setCCPHIDs($new); 148 - break; 149 - case ManiphestTransactionType::TYPE_PRIORITY: 150 - $task->setPriority($new); 151 - $pri_changed = true; 152 - break; 153 - case ManiphestTransactionType::TYPE_ATTACH: 154 - $task->setAttached($new); 155 - break; 156 - case ManiphestTransactionType::TYPE_TITLE: 157 - $task->setTitle($new); 158 - break; 159 - case ManiphestTransactionType::TYPE_DESCRIPTION: 160 - $task->setDescription($new); 161 - break; 162 - case ManiphestTransactionType::TYPE_PROJECTS: 163 - $task->setProjectPHIDs($new); 164 - break; 165 - case PhabricatorTransactions::TYPE_CUSTOMFIELD: 166 - $aux_key = $transaction->getMetadataValue('customfield:key'); 167 - $aux_writes[$aux_key] = $new; 168 - break; 169 - case ManiphestTransactionType::TYPE_EDGE: 170 - // Edge edits are accomplished through PhabricatorEdgeEditor, which 171 - // has authority. 172 - break; 173 - default: 174 - throw new Exception('Unknown action type.'); 175 - } 176 - 177 - $transaction->setOldValue($old); 178 - $transaction->setNewValue($new); 179 - } 180 - 181 - } 182 - 183 - if ($pri_changed) { 184 - $subpriority = ManiphestTransactionEditor::getNextSubpriority( 185 - $task->getPriority(), 186 - null); 187 - $task->setSubpriority($subpriority); 188 - } 189 - 190 - $task->save(); 191 - 192 - if ($aux_writes) { 193 - ManiphestAuxiliaryFieldSpecification::writeLegacyAuxiliaryUpdates( 194 - $task, 195 - $aux_writes); 196 - } 197 - 198 - foreach ($transactions as $transaction) { 199 - $transaction->setTransactionTask($task); 200 - $transaction->save(); 201 - } 202 - 203 - $email_to[] = $task->getOwnerPHID(); 204 - $email_cc = array_merge( 205 - $email_cc, 206 - $task->getCCPHIDs()); 207 - 208 - $mail = $this->sendEmail($task, $transactions, $email_to, $email_cc); 209 - 210 - $this->publishFeedStory( 211 - $task, 212 - $transactions, 213 - $mail->buildRecipientList()); 214 - 215 - id(new PhabricatorSearchIndexer()) 216 - ->indexDocumentByPHID($task->getPHID()); 217 - 218 - $fields = PhabricatorCustomField::getObjectFields( 219 - $task, 220 - PhabricatorCustomField::ROLE_APPLICATIONSEARCH); 221 - $fields->readFieldsFromStorage($task); 222 - $fields->rebuildIndexes($task); 223 - 224 - } 225 - 226 - protected function getSubjectPrefix() { 227 - return PhabricatorEnv::getEnvConfig('metamta.maniphest.subject-prefix'); 228 - } 229 - 230 - private function sendEmail($task, $transactions, $email_to, $email_cc) { 231 - $email_to = array_filter(array_unique($email_to)); 232 - $email_cc = array_filter(array_unique($email_cc)); 233 - 234 - $phids = array(); 235 - foreach ($transactions as $transaction) { 236 - foreach ($transaction->extractPHIDs() as $phid) { 237 - $phids[$phid] = true; 238 - } 239 - } 240 - foreach ($email_to as $phid) { 241 - $phids[$phid] = true; 242 - } 243 - foreach ($email_cc as $phid) { 244 - $phids[$phid] = true; 245 - } 246 - $phids = array_keys($phids); 247 - 248 - $handles = id(new PhabricatorHandleQuery()) 249 - ->setViewer($this->getActor()) 250 - ->withPHIDs($phids) 251 - ->execute(); 252 - 253 - $main_body = array(); 254 - foreach ($transactions as $transaction) { 255 - $main_body[] = id(clone $transaction->getModernTransaction()) 256 - ->attachViewer($this->getActor()) 257 - ->setHandles($handles) 258 - ->setRenderingTarget('text') 259 - ->getTitle(); 260 - } 261 - 262 - foreach ($transactions as $transaction) { 263 - if ($transaction->getComments()) { 264 - $main_body[] = null; 265 - $main_body[] = $transaction->getComments(); 266 - } 267 - } 268 - 269 - $main_body = implode("\n", $main_body); 270 - 271 - $action = head($transactions)->getModernTransaction()->getActionName(); 272 - 273 - $is_create = $this->isCreate($transactions); 274 - 275 - $task_uri = PhabricatorEnv::getProductionURI('/T'.$task->getID()); 276 - 277 - $reply_handler = $this->buildReplyHandler($task); 278 - 279 - $body = new PhabricatorMetaMTAMailBody(); 280 - $body->addRawSection($main_body); 281 - if ($is_create) { 282 - $body->addTextSection(pht('TASK DESCRIPTION'), $task->getDescription()); 283 - } 284 - $body->addTextSection(pht('TASK DETAIL'), $task_uri); 285 - $body->addReplySection($reply_handler->getReplyHandlerInstructions()); 286 - 287 - $thread_id = 'maniphest-task-'.$task->getPHID(); 288 - $task_id = $task->getID(); 289 - $title = $task->getTitle(); 290 - 291 - $mailtags = $this->getMailTags($transactions); 292 - 293 - $template = id(new PhabricatorMetaMTAMail()) 294 - ->setSubject("T{$task_id}: {$title}") 295 - ->setSubjectPrefix($this->getSubjectPrefix()) 296 - ->setVarySubjectPrefix("[{$action}]") 297 - ->setFrom($transaction->getAuthorPHID()) 298 - ->setParentMessageID($this->parentMessageID) 299 - ->addHeader('Thread-Topic', "T{$task_id}: ".$task->getOriginalTitle()) 300 - ->setThreadID($thread_id, $is_create) 301 - ->setRelatedPHID($task->getPHID()) 302 - ->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs()) 303 - ->setIsBulk(true) 304 - ->setMailTags($mailtags) 305 - ->setBody($body->render()); 306 - 307 - $mails = $reply_handler->multiplexMail( 308 - $template, 309 - array_select_keys($handles, $email_to), 310 - array_select_keys($handles, $email_cc)); 311 - 312 - foreach ($mails as $mail) { 313 - $mail->saveAndSend(); 314 - } 315 - 316 - $template->addTos($email_to); 317 - $template->addCCs($email_cc); 318 - return $template; 319 - } 320 - 321 8 public function buildReplyHandler(ManiphestTask $task) { 322 9 $handler_object = PhabricatorEnv::newObjectFromConfig( 323 10 'metamta.maniphest.reply-handler'); ··· 326 13 return $handler_object; 327 14 } 328 15 329 - private function publishFeedStory( 330 - ManiphestTask $task, 331 - array $transactions, 332 - array $mailed_phids) { 333 - assert_instances_of($transactions, 'ManiphestTransaction'); 334 - 335 - $actions = array(ManiphestAction::ACTION_UPDATE); 336 - $comments = null; 337 - foreach ($transactions as $transaction) { 338 - if ($transaction->hasComments()) { 339 - $comments = $transaction->getComments(); 340 - } 341 - $type = $transaction->getTransactionType(); 342 - switch ($type) { 343 - case ManiphestTransactionType::TYPE_OWNER: 344 - $actions[] = ManiphestAction::ACTION_ASSIGN; 345 - break; 346 - case ManiphestTransactionType::TYPE_STATUS: 347 - if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) { 348 - $actions[] = ManiphestAction::ACTION_CLOSE; 349 - } else if ($this->isCreate($transactions)) { 350 - $actions[] = ManiphestAction::ACTION_CREATE; 351 - } else { 352 - $actions[] = ManiphestAction::ACTION_REOPEN; 353 - } 354 - break; 355 - default: 356 - $actions[] = $type; 357 - break; 358 - } 359 - } 360 - 361 - $action_type = ManiphestAction::selectStrongestAction($actions); 362 - $owner_phid = $task->getOwnerPHID(); 363 - $actor_phid = head($transactions)->getAuthorPHID(); 364 - $author_phid = $task->getAuthorPHID(); 365 - 366 - id(new PhabricatorFeedStoryPublisher()) 367 - ->setStoryType('PhabricatorFeedStoryManiphest') 368 - ->setStoryData(array( 369 - 'taskPHID' => $task->getPHID(), 370 - 'transactionIDs' => mpull($transactions, 'getID'), 371 - 'ownerPHID' => $owner_phid, 372 - 'action' => $action_type, 373 - 'comments' => $comments, 374 - 'description' => $task->getDescription(), 375 - )) 376 - ->setStoryTime(time()) 377 - ->setStoryAuthorPHID($actor_phid) 378 - ->setRelatedPHIDs( 379 - array_merge( 380 - array_filter( 381 - array( 382 - $task->getPHID(), 383 - $author_phid, 384 - $actor_phid, 385 - $owner_phid, 386 - )), 387 - $task->getProjectPHIDs())) 388 - ->setPrimaryObjectPHID($task->getPHID()) 389 - ->setSubscribedPHIDs( 390 - array_merge( 391 - array_filter( 392 - array( 393 - $author_phid, 394 - $owner_phid, 395 - $actor_phid)), 396 - $task->getCCPHIDs())) 397 - ->setMailRecipientPHIDs($mailed_phids) 398 - ->publish(); 399 - } 400 - 401 - private function isCreate(array $transactions) { 402 - assert_instances_of($transactions, 'ManiphestTransaction'); 403 - $is_create = false; 404 - foreach ($transactions as $transaction) { 405 - $type = $transaction->getTransactionType(); 406 - if (($type == ManiphestTransactionType::TYPE_STATUS) && 407 - ($transaction->getOldValue() === null) && 408 - ($transaction->getNewValue() == ManiphestTaskStatus::STATUS_OPEN)) { 409 - $is_create = true; 410 - } 411 - } 412 - return $is_create; 413 - } 414 - 415 - private function getMailTags(array $transactions) { 416 - assert_instances_of($transactions, 'ManiphestTransaction'); 417 - 418 - $tags = array(); 419 - foreach ($transactions as $xaction) { 420 - switch ($xaction->getTransactionType()) { 421 - case ManiphestTransactionType::TYPE_STATUS: 422 - $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_STATUS; 423 - break; 424 - case ManiphestTransactionType::TYPE_OWNER: 425 - $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OWNER; 426 - break; 427 - case ManiphestTransactionType::TYPE_CCS: 428 - $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_CC; 429 - break; 430 - case ManiphestTransactionType::TYPE_PROJECTS: 431 - $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS; 432 - break; 433 - case ManiphestTransactionType::TYPE_PRIORITY: 434 - $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY; 435 - break; 436 - case PhabricatorTransactions::TYPE_COMMENT: 437 - // this is a comment which we will check separately below for 438 - // content 439 - break; 440 - default: 441 - $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OTHER; 442 - break; 443 - } 444 - 445 - if ($xaction->hasComments()) { 446 - $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_COMMENT; 447 - } 448 - } 449 - 450 - return array_unique($tags); 451 - } 452 - 453 16 public static function getNextSubpriority($pri, $sub) { 454 17 455 18 if ($sub === null) { ··· 472 35 return (double)(2 << 32); 473 36 } 474 37 475 - public static function addCC( 476 - ManiphestTask $task, 477 - PhabricatorUser $user) { 478 - $current_ccs = $task->getCCPHIDs(); 479 - $new_ccs = array_merge($current_ccs, array($user->getPHID())); 480 - 481 - $transaction = new ManiphestTransaction(); 482 - $transaction->setTransactionTask($task); 483 - $transaction->setAuthorPHID($user->getPHID()); 484 - $transaction->setTransactionType(ManiphestTransactionType::TYPE_CCS); 485 - $transaction->setNewValue(array_unique($new_ccs)); 486 - $transaction->setOldValue($current_ccs); 487 - 488 - id(new ManiphestTransactionEditor()) 489 - ->setActor($user) 490 - ->applyTransactions($task, array($transaction)); 491 - } 492 - 493 - public static function removeCC( 494 - ManiphestTask $task, 495 - PhabricatorUser $user) { 496 - $current_ccs = $task->getCCPHIDs(); 497 - $new_ccs = array_diff($current_ccs, array($user->getPHID())); 498 - 499 - $transaction = new ManiphestTransaction(); 500 - $transaction->setTransactionTask($task); 501 - $transaction->setAuthorPHID($user->getPHID()); 502 - $transaction->setTransactionType(ManiphestTransactionType::TYPE_CCS); 503 - $transaction->setNewValue(array_unique($new_ccs)); 504 - $transaction->setOldValue($current_ccs); 505 - 506 - id(new ManiphestTransactionEditor()) 507 - ->setActor($user) 508 - ->applyTransactions($task, array($transaction)); 509 - } 510 38 }
+28
src/applications/maniphest/storage/ManiphestTransactionPro.php
··· 414 414 return $view->render(); 415 415 } 416 416 417 + public function getMailTags() { 418 + $tags = array(); 419 + switch ($this->getTransactionType()) { 420 + case self::TYPE_STATUS: 421 + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_STATUS; 422 + break; 423 + case self::TYPE_OWNER: 424 + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OWNER; 425 + break; 426 + case self::TYPE_CCS: 427 + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_CC; 428 + break; 429 + case self::TYPE_PROJECTS: 430 + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PROJECTS; 431 + break; 432 + case self::TYPE_PRIORITY: 433 + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_PRIORITY; 434 + break; 435 + case self::TYPE_COMMENT: 436 + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_COMMENT; 437 + break; 438 + default: 439 + $tags[] = MetaMTANotificationType::TYPE_MANIPHEST_OTHER; 440 + break; 441 + } 442 + return $tags; 443 + } 444 + 417 445 418 446 } 419 447