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

Add mail support to generic transactions

Summary:
- Adds mail support to the generic transaction construct.
- Restores mail support to Pholio (now much improved; the mails are actually useful).

Test Plan: Updated a Pholio mock, got mail.

Reviewers: btrahan, chad, vrana

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T2104

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

+281 -84
+28 -75
src/applications/pholio/editor/PholioMockEditor.php
··· 19 19 protected function didApplyTransactions( 20 20 PhabricatorLiskDAO $object, 21 21 array $xactions) { 22 - // $this->sendMail($object, $xactions); 23 22 // PholioIndexer::indexMock($mock); 24 23 return; 25 24 } ··· 84 83 return parent::mergeTransactions($u, $v); 85 84 } 86 85 86 + protected function supportsMail() { 87 + return true; 88 + } 87 89 88 - private function sendMail( 89 - PholioMock $mock, 90 - array $xactions, 91 - $is_new, 92 - array $mentioned_phids) { 90 + protected function buildReplyHandler(PhabricatorLiskDAO $object) { 91 + return id(new PholioReplyHandler()) 92 + ->setMailReceiver($object); 93 + } 93 94 94 - $subscribed_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID( 95 - $mock->getPHID()); 95 + protected function buildMailTemplate(PhabricatorLiskDAO $object) { 96 + $id = $object->getID(); 97 + $name = $object->getName(); 98 + $original_name = $object->getOriginalName(); 99 + 100 + return id(new PhabricatorMetaMTAMail()) 101 + ->setSubject("M{$id}: {$name}") 102 + ->addHeader('Thread-Topic', "M{$id}: {$original_name}"); 103 + } 96 104 97 - $email_to = array( 98 - $mock->getAuthorPHID(), 105 + protected function getMailTo(PhabricatorLiskDAO $object) { 106 + return array( 107 + $object->getAuthorPHID(), 99 108 $this->requireActor()->getPHID(), 100 109 ); 101 - $email_cc = $subscribed_phids; 102 - 103 - $phids = array_merge($email_to, $email_cc); 104 - $handles = id(new PhabricatorObjectHandleData($phids)) 105 - ->setViewer($this->requireActor()) 106 - ->loadHandles(); 107 - 108 - $mock_id = $mock->getID(); 109 - $name = $mock->getName(); 110 - $original_name = $mock->getOriginalName(); 111 - 112 - $thread_id = 'pholio-mock-'.$mock->getPHID(); 113 - 114 - $mail_tags = $this->getMailTags($mock, $xactions); 115 - 116 - $body = new PhabricatorMetaMTAMailBody(); 117 - $body->addRawSection('lorem ipsum'); 118 - 119 - $mock_uri = PhabricatorEnv::getProductionURI('/M'.$mock->getID()); 120 - 121 - $body->addTextSection(pht('MOCK DETAIL'), $mock_uri); 122 - 123 - $reply_handler = $this->buildReplyHandler($mock); 124 - 125 - $template = id(new PhabricatorMetaMTAMail()) 126 - ->setSubject("M{$mock_id}: {$name}") 127 - ->setSubjectPrefix($this->getMailSubjectPrefix()) 128 - ->setVarySubjectPrefix('[edit/create?]') 129 - ->setFrom($this->requireActor()->getPHID()) 130 - ->addHeader('Thread-Topic', "M{$mock_id}: {$original_name}") 131 - ->setThreadID($thread_id, $is_new) 132 - ->setRelatedPHID($mock->getPHID()) 133 - ->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs()) 134 - ->setIsBulk(true) 135 - ->setMailTags($mail_tags) 136 - ->setBody($body->render()); 137 - 138 - // TODO 139 - // ->setParentMessageID(...) 140 - 141 - $mails = $reply_handler->multiplexMail( 142 - $template, 143 - array_select_keys($handles, $email_to), 144 - array_select_keys($handles, $email_cc)); 145 - 146 - foreach ($mails as $mail) { 147 - $mail->saveAndSend(); 148 - } 149 - 150 - $template->addTos($email_to); 151 - $template->addCCs($email_cc); 152 - 153 - return $template; 154 110 } 155 111 156 - private function getMailTags(PholioMock $mock, array $xactions) { 157 - assert_instances_of($xactions, 'PholioTransaction'); 158 - $tags = array(); 112 + protected function buildMailBody( 113 + PhabricatorLiskDAO $object, 114 + array $xactions) { 159 115 160 - return $tags; 161 - } 162 - 163 - public function buildReplyHandler(PholioMock $mock) { 164 - $handler_object = new PholioReplyHandler(); 165 - $handler_object->setMailReceiver($mock); 116 + $body = parent::buildMailBody($object, $xactions); 117 + $body->addTextSection( 118 + pht('MOCK DETAIL'), 119 + PhabricatorEnv::getProductionURI('/M'.$object->getID())); 166 120 167 - return $handler_object; 121 + return $body; 168 122 } 169 123 170 - private function getMailSubjectPrefix() { 124 + protected function getMailSubjectPrefix() { 171 125 return PhabricatorEnv::getEnvConfig('metamta.pholio.subject-prefix'); 172 126 } 173 - 174 127 175 128 }
+200 -1
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 1 1 <?php 2 2 3 + /** 4 + * @task mail Sending Mail 5 + */ 3 6 abstract class PhabricatorApplicationTransactionEditor 4 7 extends PhabricatorEditor { 5 8 ··· 202 205 } 203 206 } 204 207 208 + if (!$xactions) { 209 + return $this; 210 + } 211 + 205 212 $xactions = $this->sortTransactions($xactions); 206 213 207 214 $comment_editor = id(new PhabricatorApplicationTransactionCommentEditor()) ··· 230 237 } 231 238 $object->saveTransaction(); 232 239 233 - // TODO: Send mail. 240 + 241 + $this->loadHandles($xactions); 242 + 243 + 244 + $mail = null; 245 + if ($this->supportsMail()) { 246 + $mail = $this->sendMail($object, $xactions); 247 + } 248 + 234 249 // TODO: Index object. 235 250 // TODO: Publish feed/notifications. 236 251 ··· 239 254 return $this; 240 255 } 241 256 257 + private function loadHandles(array $xactions) { 258 + $phids = array(); 259 + foreach ($xactions as $xaction) { 260 + $phids[$xaction->getPHID()] = $xaction->getRequiredHandlePHIDs(); 261 + } 262 + $handles = array(); 263 + $merged = array_mergev($phids); 264 + if ($merged) { 265 + $handles = id(new PhabricatorObjectHandleData($merged)) 266 + ->setViewer($this->requireActor()) 267 + ->loadHandles(); 268 + } 269 + foreach ($xactions as $xaction) { 270 + $xaction->setHandles( 271 + array_select_keys( 272 + $handles, 273 + $phids[$xaction->getPHID()])); 274 + } 275 + } 276 + 242 277 private function validateEditParameters( 243 278 PhabricatorLiskDAO $object, 244 279 array $xactions) { ··· 481 516 } 482 517 483 518 return array_values(array_merge($head, $tail)); 519 + } 520 + 521 + 522 + /* -( Sending Mail )------------------------------------------------------- */ 523 + 524 + 525 + /** 526 + * @task mail 527 + */ 528 + protected function supportsMail() { 529 + return false; 530 + } 531 + 532 + 533 + /** 534 + * @task mail 535 + */ 536 + protected function sendMail( 537 + PhabricatorLiskDAO $object, 538 + array $xactions) { 539 + 540 + $email_to = $this->getMailTo($object); 541 + $email_cc = $this->getMailCC($object); 542 + 543 + $phids = array_merge($email_to, $email_cc); 544 + $handles = id(new PhabricatorObjectHandleData($phids)) 545 + ->setViewer($this->requireActor()) 546 + ->loadHandles(); 547 + 548 + $template = $this->buildMailTemplate($object); 549 + $body = $this->buildMailBody($object, $xactions); 550 + 551 + $mail_tags = $this->getMailTags($object, $xactions); 552 + 553 + $action = $this->getStrongestAction($object, $xactions); 554 + 555 + $template 556 + ->setFrom($this->requireActor()->getPHID()) 557 + ->setSubjectPrefix($this->getMailSubjectPrefix()) 558 + ->setVarySubjectPrefix('['.$action.']') 559 + ->setThreadID($object->getPHID(), $this->getIsNewObject()) 560 + ->setRelatedPHID($object->getPHID()) 561 + ->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs()) 562 + ->setMailTags($mail_tags) 563 + ->setIsBulk(true) 564 + ->setBody($body->render()); 565 + 566 + // TODO 567 + // ->setParentMessageID(...) 568 + 569 + $mails = $this 570 + ->buildReplyHandler($object) 571 + ->multiplexMail( 572 + $template, 573 + array_select_keys($handles, $email_to), 574 + array_select_keys($handles, $email_cc)); 575 + 576 + foreach ($mails as $mail) { 577 + $mail->saveAndSend(); 578 + } 579 + 580 + $template->addTos($email_to); 581 + $template->addCCs($email_cc); 582 + 583 + return $template; 584 + } 585 + 586 + 587 + /** 588 + * @task mail 589 + */ 590 + protected function getStrongestAction( 591 + PhabricatorLiskDAO $object, 592 + array $xactions) { 593 + return last(msort($xactions, 'getActionStrength'))->getActionName(); 594 + } 595 + 596 + 597 + /** 598 + * @task mail 599 + */ 600 + protected function buildReplyHandler(PhabricatorLiskDAO $object) { 601 + throw new Exception("Capability not supported."); 602 + } 603 + 604 + 605 + /** 606 + * @task mail 607 + */ 608 + protected function getMailSubjectPrefix() { 609 + throw new Exception("Capability not supported."); 610 + } 611 + 612 + 613 + /** 614 + * @task mail 615 + */ 616 + protected function getMailTags( 617 + PhabricatorLiskDAO $object, 618 + array $xactions) { 619 + $tags = array(); 620 + 621 + foreach ($xactions as $xaction) { 622 + $tags[] = $xaction->getMailTags(); 623 + } 624 + 625 + return array_mergev($tags); 626 + } 627 + 628 + 629 + /** 630 + * @task mail 631 + */ 632 + protected function buildMailTemplate(PhabricatorLiskDAO $object) { 633 + throw new Exception("Capability not supported."); 634 + } 635 + 636 + 637 + /** 638 + * @task mail 639 + */ 640 + protected function getMailTo(PhabricatorLiskDAO $object) { 641 + throw new Exception("Capability not supported."); 642 + } 643 + 644 + 645 + /** 646 + * @task mail 647 + */ 648 + protected function getMailCC(PhabricatorLiskDAO $object) { 649 + if ($object instanceof PhabricatorSubscribableInterface) { 650 + $phid = $object->getPHID(); 651 + return PhabricatorSubscribersQuery::loadSubscribersForPHID($phid); 652 + } 653 + throw new Exception("Capability not supported."); 654 + } 655 + 656 + 657 + /** 658 + * @task mail 659 + */ 660 + protected function buildMailBody( 661 + PhabricatorLiskDAO $object, 662 + array $xactions) { 663 + 664 + $headers = array(); 665 + $comments = array(); 666 + 667 + foreach ($xactions as $xaction) { 668 + $headers[] = id(clone $xaction)->setRenderingTarget('text')->getTitle(); 669 + $comment = $xaction->getComment(); 670 + if ($comment && strlen($comment->getContent())) { 671 + $comments[] = $comment->getContent(); 672 + } 673 + } 674 + 675 + $body = new PhabricatorMetaMTAMailBody(); 676 + $body->addRawSection(implode("\n", $headers)); 677 + 678 + foreach ($comments as $comment) { 679 + $body->addRawSection($comment); 680 + } 681 + 682 + return $body; 484 683 } 485 684 486 685
+44 -1
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 6 6 7 7 const MARKUP_FIELD_COMMENT = 'markup:comment'; 8 8 9 + const TARGET_TEXT = 'text'; 10 + const TARGET_HTML = 'html'; 11 + 9 12 protected $phid; 10 13 protected $objectPHID; 11 14 protected $authorPHID; ··· 25 28 private $commentNotLoaded; 26 29 27 30 private $handles; 31 + private $renderingTarget = self::TARGET_HTML; 28 32 29 33 abstract public function getApplicationTransactionType(); 30 34 abstract public function getApplicationTransactionCommentObject(); ··· 77 81 78 82 /* -( Rendering )---------------------------------------------------------- */ 79 83 84 + public function setRenderingTarget($rendering_target) { 85 + $this->renderingTarget = $rendering_target; 86 + return $this; 87 + } 88 + 89 + public function getRenderingTarget() { 90 + return $this->renderingTarget; 91 + } 92 + 80 93 public function getRequiredHandlePHIDs() { 81 94 $phids = array(); 82 95 ··· 108 121 } 109 122 110 123 protected function renderHandleLink($phid) { 111 - return $this->getHandle($phid)->renderLink(); 124 + if ($this->renderingTarget == self::TARGET_HTML) { 125 + return $this->getHandle($phid)->renderLink(); 126 + } else { 127 + return $this->getHandle($phid)->getName(); 128 + } 112 129 } 113 130 114 131 protected function renderHandleList(array $phids) { ··· 201 218 $this->renderHandleLink($author_phid), 202 219 $this->getApplicationObjectTypeName()); 203 220 } 221 + } 222 + 223 + public function getActionStrength() { 224 + switch ($this->getTransactionType()) { 225 + case PhabricatorTransactions::TYPE_COMMENT: 226 + return 0.5; 227 + } 228 + return 1.0; 229 + } 230 + 231 + public function getActionName() { 232 + switch ($this->getTransactionType()) { 233 + case PhabricatorTransactions::TYPE_COMMENT: 234 + return pht('Commented On'); 235 + case PhabricatorTransactions::TYPE_VIEW_POLICY: 236 + case PhabricatorTransactions::TYPE_EDIT_POLICY: 237 + return pht('Changed Policy'); 238 + case PhabricatorTransactions::TYPE_SUBSCRIBERS: 239 + return pht('Changed Subscribers'); 240 + default: 241 + return pht('Updated'); 242 + } 243 + } 244 + 245 + public function getMailTags() { 246 + return array(); 204 247 } 205 248 206 249
+9 -7
webroot/rsrc/js/application/core/behavior-phabricator-nav.js
··· 112 112 // When the user scrolls down on the desktop, we move the local nav up until 113 113 // it hits the top of the page. 114 114 115 - JX.Stratcom.listen(['scroll', 'resize'], null, function(e) { 116 - if (JX.Device.getDevice() != 'desktop') { 117 - return; 118 - } 115 + if (local) { 116 + JX.Stratcom.listen(['scroll', 'resize'], null, function(e) { 117 + if (JX.Device.getDevice() != 'desktop') { 118 + return; 119 + } 119 120 120 - var y = Math.max(0, config.menuSize - JX.Vector.getScroll().y); 121 - local.style.top = y + 'px'; 122 - }); 121 + var y = Math.max(0, config.menuSize - JX.Vector.getScroll().y); 122 + local.style.top = y + 'px'; 123 + }); 124 + } 123 125 124 126 125 127 // - Navigation Reset ----------------------------------------------------------