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

Ponder - make feed stories and emails a bit better

Summary: Also some random cleanup now and again. Note reply handler stuff is kind of bojangles bad right now. It didn't work before though either so hey.

Test Plan: asked questions, answered questions, edited answers... the feed pleased my eye

Reviewers: epriestley

Reviewed By: epriestley

CC: Korvin, aran

Maniphest Tasks: T3653

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

+323 -106
+6 -4
src/__phutil_library_map__.php
··· 1938 1938 'PonderAnswerTransaction' => 'applications/ponder/storage/PonderAnswerTransaction.php', 1939 1939 'PonderAnswerTransactionComment' => 'applications/ponder/storage/PonderAnswerTransactionComment.php', 1940 1940 'PonderAnswerTransactionQuery' => 'applications/ponder/query/PonderAnswerTransactionQuery.php', 1941 - 'PonderAnswerViewController' => 'applications/ponder/controller/PonderAnswerViewController.php', 1942 1941 'PonderComment' => 'applications/ponder/storage/PonderComment.php', 1943 1942 'PonderCommentQuery' => 'applications/ponder/query/PonderCommentQuery.php', 1944 1943 'PonderConstants' => 'applications/ponder/constants/PonderConstants.php', 1945 1944 'PonderController' => 'applications/ponder/controller/PonderController.php', 1946 1945 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', 1946 + 'PonderEditor' => 'applications/ponder/editor/PonderEditor.php', 1947 1947 'PonderPHIDTypeAnswer' => 'applications/ponder/phid/PonderPHIDTypeAnswer.php', 1948 1948 'PonderPHIDTypeQuestion' => 'applications/ponder/phid/PonderPHIDTypeQuestion.php', 1949 1949 'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php', ··· 1964 1964 'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php', 1965 1965 'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php', 1966 1966 'PonderSearchIndexer' => 'applications/ponder/search/PonderSearchIndexer.php', 1967 + 'PonderTransactionFeedStory' => 'applications/ponder/feed/PonderTransactionFeedStory.php', 1967 1968 'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php', 1968 1969 'PonderVotableView' => 'applications/ponder/view/PonderVotableView.php', 1969 1970 'PonderVote' => 'applications/ponder/constants/PonderVote.php', ··· 4149 4150 ), 4150 4151 'PonderAnswerCommentController' => 'PonderController', 4151 4152 'PonderAnswerEditController' => 'PonderController', 4152 - 'PonderAnswerEditor' => 'PhabricatorApplicationTransactionEditor', 4153 + 'PonderAnswerEditor' => 'PonderEditor', 4153 4154 'PonderAnswerHistoryController' => 'PonderController', 4154 4155 'PonderAnswerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4155 4156 'PonderAnswerSaveController' => 'PonderController', 4156 4157 'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction', 4157 4158 'PonderAnswerTransactionComment' => 'PhabricatorApplicationTransactionComment', 4158 4159 'PonderAnswerTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 4159 - 'PonderAnswerViewController' => 'PonderController', 4160 4160 'PonderComment' => 4161 4161 array( 4162 4162 0 => 'PonderDAO', ··· 4165 4165 'PonderCommentQuery' => 'PhabricatorQuery', 4166 4166 'PonderController' => 'PhabricatorController', 4167 4167 'PonderDAO' => 'PhabricatorLiskDAO', 4168 + 'PonderEditor' => 'PhabricatorApplicationTransactionEditor', 4168 4169 'PonderPHIDTypeAnswer' => 'PhabricatorPHIDType', 4169 4170 'PonderPHIDTypeQuestion' => 'PhabricatorPHIDType', 4170 4171 'PonderQuestion' => ··· 4178 4179 ), 4179 4180 'PonderQuestionCommentController' => 'PonderController', 4180 4181 'PonderQuestionEditController' => 'PonderController', 4181 - 'PonderQuestionEditor' => 'PhabricatorApplicationTransactionEditor', 4182 + 'PonderQuestionEditor' => 'PonderEditor', 4182 4183 'PonderQuestionHistoryController' => 'PonderController', 4183 4184 'PonderQuestionListController' => 4184 4185 array( ··· 4197 4198 'PonderQuestionViewController' => 'PonderController', 4198 4199 'PonderRemarkupRule' => 'PhabricatorRemarkupRuleObject', 4199 4200 'PonderSearchIndexer' => 'PhabricatorSearchDocumentIndexer', 4201 + 'PonderTransactionFeedStory' => 'PhabricatorApplicationTransactionFeedStory', 4200 4202 'PonderVotableView' => 'AphrontView', 4201 4203 'PonderVote' => 'PonderConstants', 4202 4204 'PonderVoteEditor' => 'PhabricatorEditor',
+2 -2
src/applications/macro/storage/PhabricatorMacroTransaction.php
··· 92 92 return parent::getTitle(); 93 93 } 94 94 95 - public function getTitleForFeed() { 95 + public function getTitleForFeed(PhabricatorFeedStory $story) { 96 96 $author_phid = $this->getAuthorPHID(); 97 97 $object_phid = $this->getObjectPHID(); 98 98 ··· 133 133 } 134 134 } 135 135 136 - return parent::getTitleForFeed(); 136 + return parent::getTitleForFeed($story); 137 137 } 138 138 139 139 public function getActionName() {
+2 -2
src/applications/paste/storage/PhabricatorPasteTransaction.php
··· 88 88 return parent::getTitle(); 89 89 } 90 90 91 - public function getTitleForFeed() { 91 + public function getTitleForFeed(PhabricatorFeedStory $story) { 92 92 $author_phid = $this->getAuthorPHID(); 93 93 $object_phid = $this->getObjectPHID(); 94 94 ··· 117 117 break; 118 118 } 119 119 120 - return parent::getTitleForFeed(); 120 + return parent::getTitleForFeed($story); 121 121 } 122 122 123 123 public function getColor() {
+2 -2
src/applications/pholio/storage/PholioTransaction.php
··· 180 180 return parent::getTitle(); 181 181 } 182 182 183 - public function getTitleForFeed() { 183 + public function getTitleForFeed(PhabricatorFeedStory $story) { 184 184 $author_phid = $this->getAuthorPHID(); 185 185 $object_phid = $this->getObjectPHID(); 186 186 ··· 243 243 break; 244 244 } 245 245 246 - return parent::getTitleForFeed(); 246 + return parent::getTitleForFeed($story); 247 247 } 248 248 249 249 public function getBodyForFeed(PhabricatorFeedStory $story) {
+4 -5
src/applications/ponder/controller/PonderAnswerEditController.php
··· 31 31 32 32 $question = $answer->getQuestion(); 33 33 $qid = $question->getID(); 34 - $aid = $answer->getID(); 35 34 36 - $question_uri = "/Q{$qid}#A{$aid}"; 35 + $answer_uri = $answer->getURI(); 37 36 38 37 $errors = array(); 39 38 if ($request->isFormPost()) { ··· 58 57 $editor->applyTransactions($answer, $xactions); 59 58 60 59 return id(new AphrontRedirectResponse()) 61 - ->setURI($question_uri); 60 + ->setURI($answer_uri); 62 61 } 63 62 } 64 63 ··· 84 83 ->appendChild( 85 84 id(new AphrontFormSubmitControl()) 86 85 ->setValue(pht('Update Answer')) 87 - ->addCancelButton($question_uri)); 86 + ->addCancelButton($answer_uri)); 88 87 89 88 $crumbs = $this->buildApplicationCrumbs(); 90 89 $crumbs->addCrumb( 91 90 id(new PhabricatorCrumbView()) 92 91 ->setName("Q{$qid}") 93 - ->setHref($question_uri)); 92 + ->setHref($answer_uri)); 94 93 $crumbs->addCrumb( 95 94 id(new PhabricatorCrumbView()) 96 95 ->setName(pht('Edit Answer')));
-25
src/applications/ponder/controller/PonderAnswerViewController.php
··· 1 - <?php 2 - 3 - final class PonderAnswerViewController extends PonderController { 4 - 5 - private $answerID; 6 - 7 - public function willProcessRequest(array $data) { 8 - $this->answerID = $data['id']; 9 - } 10 - 11 - public function processRequest() { 12 - $request = $this->getRequest(); 13 - $answer = id(new PonderAnswer())->load($this->answerID); 14 - 15 - if (!$answer) { 16 - return new Aphront404Response(); 17 - } 18 - 19 - $question_id = $answer->getQuestionID(); 20 - 21 - return id(new AphrontRedirectResponse()) 22 - ->setURI('/Q'.$question_id . '#A' . $answer->getID()); 23 - } 24 - 25 - }
+2
src/applications/ponder/controller/PonderQuestionViewController.php
··· 251 251 252 252 $out[] = phutil_tag('br'); 253 253 $out[] = phutil_tag('br'); 254 + $out[] = id(new PhabricatorAnchorView()) 255 + ->setAnchorName("A$id"); 254 256 $out[] = id(new PHUIHeaderView()) 255 257 ->setHeader($this->getHandle($author_phid)->getFullName()) 256 258 ->setImage($this->getHandle($author_phid)->getImageURI());
+35 -5
src/applications/ponder/editor/PonderAnswerEditor.php
··· 1 1 <?php 2 2 3 - final class PonderAnswerEditor 4 - extends PhabricatorApplicationTransactionEditor { 3 + final class PonderAnswerEditor extends PonderEditor { 5 4 6 5 public function getTransactionTypes() { 7 6 $types = parent::getTransactionTypes(); ··· 62 61 return parent::mergeTransactions($u, $v); 63 62 } 64 63 65 - protected function supportsFeed() { 64 + protected function shouldSendMail( 65 + PhabricatorLiskDAO $object, 66 + array $xactions) { 66 67 return true; 67 68 } 68 69 69 - protected function getMailTo(PhabricatorLiskDAO $object) { 70 - return array($object->getAuthorPHID()); 70 + protected function buildReplyHandler(PhabricatorLiskDAO $object) { 71 + $question = $object->getQuestion(); 72 + return id(new PonderQuestionReplyHandler()) 73 + ->setMailReceiver($question); 74 + } 75 + 76 + protected function buildMailTemplate(PhabricatorLiskDAO $object) { 77 + $question = $object->getQuestion(); 78 + return parent::buildMailTemplate($question); 71 79 } 72 80 81 + 82 + protected function buildMailBody( 83 + PhabricatorLiskDAO $object, 84 + array $xactions) { 85 + 86 + $body = parent::buildMailBody($object, $xactions); 87 + 88 + // If the user just gave the answer, add the answer text. 89 + foreach ($xactions as $xaction) { 90 + $type = $xaction->getTransactionType(); 91 + $new = $xaction->getNewValue(); 92 + if ($type == PonderAnswerTransaction::TYPE_CONTENT) { 93 + $body->addRawSection($new); 94 + } 95 + } 96 + 97 + $body->addTextSection( 98 + pht('ANSWER DETAIL'), 99 + PhabricatorEnv::getProductionURI($object->getURI())); 100 + 101 + return $body; 102 + } 73 103 74 104 }
+32
src/applications/ponder/editor/PonderEditor.php
··· 1 + <?php 2 + 3 + abstract class PonderEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + protected function supportsFeed() { 7 + return true; 8 + } 9 + 10 + protected function buildMailTemplate(PhabricatorLiskDAO $object) { 11 + $id = $object->getID(); 12 + $title = $object->getTitle(); 13 + $original_title = $object->getOriginalTitle(); 14 + 15 + return id(new PhabricatorMetaMTAMail()) 16 + ->setSubject("Q{$id}: {$title}") 17 + ->addHeader('Thread-Topic', "Q{$id}: {$original_title}"); 18 + } 19 + 20 + 21 + protected function getMailTo(PhabricatorLiskDAO $object) { 22 + return array( 23 + $object->getAuthorPHID(), 24 + $this->requireActor()->getPHID(), 25 + ); 26 + } 27 + 28 + protected function getMailSubjectPrefix() { 29 + return '[Ponder]'; 30 + } 31 + 32 + }
+50 -28
src/applications/ponder/editor/PonderQuestionEditor.php
··· 1 1 <?php 2 2 3 3 final class PonderQuestionEditor 4 - extends PhabricatorApplicationTransactionEditor { 4 + extends PonderEditor { 5 + 6 + private $answer; 7 + 8 + /** 9 + * This is used internally on @{method:applyInitialEffects} if a transaction 10 + * of type PonderQuestionTransaction::TYPE_ANSWERS is in the mix. The value 11 + * is set to the //last// answer in the transactions. Practically, one 12 + * answer is given at a time in the application, though theoretically 13 + * this is buggy. 14 + * 15 + * The answer is used in emails to generate proper links. 16 + */ 17 + private function setAnswer(PonderAnswer $answer) { 18 + $this->answer = $answer; 19 + return $this; 20 + } 21 + private function getAnswer() { 22 + return $this->answer; 23 + } 5 24 6 25 protected function shouldApplyInitialEffects( 7 26 PhabricatorLiskDAO $object, ··· 32 51 continue; 33 52 } 34 53 $answer->save(); 54 + $this->setAnswer($answer); 35 55 } 36 56 break; 37 57 } ··· 145 165 return parent::mergeTransactions($u, $v); 146 166 } 147 167 148 - protected function supportsFeed() { 168 + protected function supportsSearch() { 149 169 return true; 150 170 } 151 171 152 - protected function supportsSearch() { 153 - return true; 172 + protected function getFeedStoryType() { 173 + return 'PonderTransactionFeedStory'; 154 174 } 155 175 176 + protected function getFeedStoryData( 177 + PhabricatorLiskDAO $object, 178 + array $xactions) { 179 + 180 + $data = parent::getFeedStoryData($object, $xactions); 181 + $answer = $this->getAnswer(); 182 + if ($answer) { 183 + $data['answerPHID'] = $answer->getPHID(); 184 + } 185 + 186 + return $data; 187 + } 188 + 156 189 protected function shouldImplyCC( 157 190 PhabricatorLiskDAO $object, 158 191 PhabricatorApplicationTransaction $xaction) { ··· 176 209 ->setMailReceiver($object); 177 210 } 178 211 179 - protected function buildMailTemplate(PhabricatorLiskDAO $object) { 180 - $id = $object->getID(); 181 - $title = $object->getTitle(); 182 - $original_title = $object->getOriginalTitle(); 183 - 184 - return id(new PhabricatorMetaMTAMail()) 185 - ->setSubject("Q{$id}: {$title}") 186 - ->addHeader('Thread-Topic', "Q{$id}: {$original_title}"); 187 - } 188 - 189 - protected function getMailTo(PhabricatorLiskDAO $object) { 190 - return array( 191 - $object->getAuthorPHID(), 192 - $this->requireActor()->getPHID(), 193 - ); 194 - } 195 - 196 212 protected function buildMailBody( 197 213 PhabricatorLiskDAO $object, 198 214 array $xactions) { 199 215 200 216 $body = parent::buildMailBody($object, $xactions); 201 217 202 - // If the user just asked the question, add the question text. 218 + $header = pht('QUESTION DETAIL'); 219 + $uri = '/Q'.$object->getID(); 203 220 foreach ($xactions as $xaction) { 204 221 $type = $xaction->getTransactionType(); 205 222 $old = $xaction->getOldValue(); 206 223 $new = $xaction->getNewValue(); 224 + // If the user just asked the question, add the question text. 207 225 if ($type == PonderQuestionTransaction::TYPE_CONTENT) { 208 226 if ($old === null) { 209 227 $body->addRawSection($new); 210 228 } 211 229 } 230 + // If the user gave an answer, add the answer text. Also update 231 + // the header and uri to be more answer-specific. 232 + if ($type == PonderQuestionTransaction::TYPE_ANSWERS) { 233 + $answer = $this->getAnswer(); 234 + $body->addRawSection($answer->getContent()); 235 + $header = pht('ANSWER DETAIL'); 236 + $uri = $answer->getURI(); 237 + } 212 238 } 213 239 214 240 $body->addTextSection( 215 - pht('QUESTION DETAIL'), 216 - PhabricatorEnv::getProductionURI('/Q'.$object->getID())); 241 + $header, 242 + PhabricatorEnv::getProductionURI($uri)); 217 243 218 244 return $body; 219 - } 220 - 221 - protected function getMailSubjectPrefix() { 222 - return '[Ponder]'; 223 245 } 224 246 225 247 }
+14
src/applications/ponder/feed/PonderTransactionFeedStory.php
··· 1 + <?php 2 + 3 + final class PonderTransactionFeedStory 4 + extends PhabricatorApplicationTransactionFeedStory { 5 + 6 + public function getRequiredObjectPHIDs() { 7 + $phids = parent::getRequiredObjectPHIDs(); 8 + $answer_phid = $this->getValue('answerPHID'); 9 + if ($answer_phid) { 10 + $phids[] = $answer_phid; 11 + } 12 + return $phids; 13 + } 14 + }
-5
src/applications/ponder/mail/PonderQuestionReplyHandler.php
··· 17 17 return $this->getDefaultPublicReplyHandlerEmailAddress('Q'); 18 18 } 19 19 20 - public function getReplyHandlerDomain() { 21 - return PhabricatorEnv::getEnvConfig( 22 - 'metamta.maniphest.reply-handler-domain'); 23 - } 24 - 25 20 public function getReplyHandlerInstructions() { 26 21 return null; 27 22 }
+1 -2
src/applications/ponder/phid/PonderPHIDTypeAnswer.php
··· 36 36 $answer = $objects[$phid]; 37 37 38 38 $id = $answer->getID(); 39 - $qid = $answer->getQuestionID(); 40 39 41 40 $handle->setName("Answer {$id}"); 42 - $handle->setURI("/Q{$qid}#A{$id}"); 41 + $handle->setURI($answer->getURI()); 43 42 } 44 43 } 45 44
+1 -2
src/applications/ponder/phid/PonderPHIDTypeQuestion.php
··· 36 36 $question = $objects[$phid]; 37 37 38 38 $id = $question->getID(); 39 - $title = $question->getTitle(); 40 39 41 40 $handle->setName("Q{$id}"); 42 41 $handle->setURI("/Q{$id}"); 43 - $handle->setFullName("Q{$id}: {$title}"); 42 + $handle->setFullName($question->getFullTitle()); 44 43 } 45 44 } 46 45
+2 -1
src/applications/ponder/query/PonderQuestionQuery.php
··· 151 151 $answers = mgroup($answers, 'getQuestionID'); 152 152 153 153 foreach ($questions as $question) { 154 - $question->attachAnswers(idx($answers, $question->getID(), array())); 154 + $question_answers = idx($answers, $question->getID(), array()); 155 + $question->attachAnswers(mpull($question_answers, null, 'getPHID')); 155 156 } 156 157 } 157 158
+4 -5
src/applications/ponder/storage/PonderAnswer.php
··· 23 23 24 24 private $userVotes = array(); 25 25 26 - // TODO: Get rid of this method. 27 - public function setQuestion($question) { 28 - return $this->attachQuestion($question); 29 - } 30 - 31 26 public function attachQuestion(PonderQuestion $question = null) { 32 27 $this->question = $question; 33 28 return $this; ··· 35 30 36 31 public function getQuestion() { 37 32 return $this->assertAttached($this->question); 33 + } 34 + 35 + public function getURI() { 36 + return '/Q'.$this->getQuestionID().'#A'.$this->getID(); 38 37 } 39 38 40 39 public function setUserVote($vote) {
+73 -7
src/applications/ponder/storage/PonderAnswerTransaction.php
··· 21 21 return new PonderAnswerTransactionComment(); 22 22 } 23 23 24 - public function getTitleForFeed() { 24 + public function getRequiredHandlePHIDs() { 25 + $phids = parent::getRequiredHandlePHIDs(); 26 + 27 + switch ($this->getTransactionType()) { 28 + case self::TYPE_CONTENT: 29 + $phids[] = $this->getObjectPHID(); 30 + break; 31 + } 32 + 33 + return $phids; 34 + } 35 + 36 + public function getTitle() { 25 37 $author_phid = $this->getAuthorPHID(); 26 38 $object_phid = $this->getObjectPHID(); 27 39 28 - $old = $this->getOldValue(); 29 - $new = $this->getNewValue(); 30 - 31 40 switch ($this->getTransactionType()) { 32 41 case self::TYPE_CONTENT: 33 - // TODO: This is not so good. 34 42 return pht( 35 - '%s edited their answer to %s', 43 + '%s edited %s.', 36 44 $this->renderHandleLink($author_phid), 37 45 $this->renderHandleLink($object_phid)); 38 46 } 39 47 40 - return $this->getTitle(); 48 + return parent::getTitle(); 49 + } 50 + 51 + public function getTitleForFeed(PhabricatorFeedStory $story) { 52 + $author_phid = $this->getAuthorPHID(); 53 + $object_phid = $this->getObjectPHID(); 54 + 55 + switch ($this->getTransactionType()) { 56 + case self::TYPE_CONTENT: 57 + $answer = $story->getObject($object_phid); 58 + $question = $answer->getQuestion(); 59 + $answer_handle = $this->getHandle($object_phid); 60 + $link = $answer_handle->renderLink( 61 + $question->getFullTitle()); 62 + 63 + return pht( 64 + '%s updated their answer to %s', 65 + $this->renderHandleLink($author_phid), 66 + $link); 67 + } 68 + 69 + return parent::getTitleForFeed($story); 70 + } 71 + 72 + public function getBodyForFeed(PhabricatorFeedStory $story) { 73 + $new = $this->getNewValue(); 74 + 75 + $body = null; 76 + 77 + switch ($this->getTransactionType()) { 78 + case self::TYPE_CONTENT: 79 + return phutil_escape_html_newlines( 80 + phutil_utf8_shorten($new, 128)); 81 + break; 82 + } 83 + return parent::getBodyForFeed($story); 84 + } 85 + 86 + 87 + public function hasChangeDetails() { 88 + $old = $this->getOldValue(); 89 + 90 + switch ($this->getTransactionType()) { 91 + case self::TYPE_CONTENT: 92 + return $old !== null; 93 + } 94 + return parent::hasChangeDetails(); 95 + } 96 + 97 + public function renderChangeDetails(PhabricatorUser $viewer) { 98 + $old = $this->getOldValue(); 99 + $new = $this->getNewValue(); 100 + 101 + $view = id(new PhabricatorApplicationTransactionTextDiffDetailView()) 102 + ->setUser($viewer) 103 + ->setOldText($old) 104 + ->setNewText($new); 105 + 106 + return $view->render(); 41 107 } 42 108 43 109 }
+7 -1
src/applications/ponder/storage/PonderQuestion.php
··· 62 62 63 63 $this->setComments(idx($comments, $this->getPHID(), array())); 64 64 foreach ($this->answers as $answer) { 65 - $answer->setQuestion($this); 65 + $answer->attachQuestion($this); 66 66 $answer->setComments(idx($comments, $answer->getPHID(), array())); 67 67 } 68 68 } ··· 206 206 public function getOriginalTitle() { 207 207 // TODO: Make this actually save/return the original title. 208 208 return $this->getTitle(); 209 + } 210 + 211 + public function getFullTitle() { 212 + $id = $this->getID(); 213 + $title = $this->getTitle(); 214 + return "Q{$id}: {$title}"; 209 215 } 210 216 211 217
+84 -8
src/applications/ponder/storage/PonderQuestionTransaction.php
··· 24 24 return new PonderQuestionTransactionComment(); 25 25 } 26 26 27 + public function getRequiredHandlePHIDs() { 28 + $phids = parent::getRequiredHandlePHIDs(); 29 + 30 + switch ($this->getTransactionType()) { 31 + case self::TYPE_ANSWERS: 32 + $phids[] = $this->getNewAnswerPHID(); 33 + $phids[] = $this->getObjectPHID(); 34 + break; 35 + } 36 + 37 + return $phids; 38 + } 39 + 27 40 public function getTitle() { 28 41 $author_phid = $this->getAuthorPHID(); 42 + $object_phid = $this->getObjectPHID(); 29 43 30 44 $old = $this->getOldValue(); 31 45 $new = $this->getNewValue(); ··· 48 62 '%s edited the question description.', 49 63 $this->renderHandleLink($author_phid)); 50 64 case self::TYPE_ANSWERS: 51 - // TODO: This could be richer. 65 + $answer_handle = $this->getHandle($this->getNewAnswerPHID()); 66 + $question_handle = $this->getHandle($object_phid); 52 67 return pht( 53 - '%s added an answer.', 54 - $this->renderHandleLink($author_phid)); 68 + '%s answered %s', 69 + $this->renderHandleLink($author_phid), 70 + $answer_handle->renderLink($question_handle->getFullName())); 55 71 case self::TYPE_STATUS: 56 72 switch ($new) { 57 73 case PonderQuestionStatus::STATUS_OPEN: ··· 178 194 return parent::shouldHide(); 179 195 } 180 196 181 - public function getTitleForFeed() { 197 + public function getTitleForFeed(PhabricatorFeedStory $story) { 182 198 $author_phid = $this->getAuthorPHID(); 183 199 $object_phid = $this->getObjectPHID(); 184 200 ··· 205 221 $this->renderHandleLink($author_phid), 206 222 $this->renderHandleLink($object_phid)); 207 223 case self::TYPE_ANSWERS: 208 - // TODO: This could be richer, too. 224 + $answer_handle = $this->getHandle($this->getNewAnswerPHID()); 225 + $question_handle = $this->getHandle($object_phid); 209 226 return pht( 210 227 '%s answered %s', 211 228 $this->renderHandleLink($author_phid), 212 - $this->renderHandleLink($object_phid)); 229 + $answer_handle->renderLink($question_handle->getFullName())); 213 230 case self::TYPE_STATUS: 214 231 switch ($new) { 215 232 case PonderQuestionStatus::STATUS_OPEN: ··· 225 242 } 226 243 } 227 244 228 - return $this->getTitle(); 245 + return parent::getTitleForFeed($story); 229 246 } 230 247 231 - } 248 + public function getBodyForFeed(PhabricatorFeedStory $story) { 249 + $new = $this->getNewValue(); 250 + $old = $this->getOldValue(); 232 251 252 + $body = null; 253 + 254 + switch ($this->getTransactionType()) { 255 + case self::TYPE_TITLE: 256 + if ($old === null) { 257 + $question = $story->getObject($this->getObjectPHID()); 258 + return phutil_escape_html_newlines( 259 + phutil_utf8_shorten($question->getContent(), 128)); 260 + } 261 + case self::TYPE_ANSWERS: 262 + $answer = $this->getNewAnswerObject($story); 263 + if ($answer) { 264 + return phutil_escape_html_newlines( 265 + phutil_utf8_shorten($answer->getContent(), 128)); 266 + } 267 + } 268 + return parent::getBodyForFeed($story); 269 + } 270 + 271 + /** 272 + * Currently the application only supports adding answers one at a time. 273 + * This data is stored as a list of phids. Use this function to get the 274 + * new phid. 275 + */ 276 + private function getNewAnswerPHID() { 277 + $new = $this->getNewValue(); 278 + $old = $this->getOldValue(); 279 + $add = array_diff($new, $old); 280 + 281 + if (count($add) != 1) { 282 + throw new Exception( 283 + 'There should be only one answer added at a time.'); 284 + } 285 + 286 + return reset($add); 287 + } 288 + 289 + /** 290 + * Generally, the answer object is only available if the transaction 291 + * type is self::TYPE_ANSWERS. 292 + * 293 + * Some stories - notably ones made before D7027 - will be of the more 294 + * generic @{class:PhabricatorApplicationTransactionFeedStory}. These 295 + * poor stories won't have the PonderAnswer loaded, and thus will have 296 + * less cool information. 297 + */ 298 + private function getNewAnswerObject(PhabricatorFeedStory $story) { 299 + if ($story instanceof PonderTransactionFeedStory) { 300 + $answer_phid = $this->getNewAnswerPHID(); 301 + if ($answer_phid) { 302 + return $story->getObject($answer_phid); 303 + } 304 + } 305 + return null; 306 + } 307 + 308 + }
+1 -1
src/applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php
··· 43 43 $xaction = $this->getObject(head($xaction_phids)); 44 44 45 45 $xaction->setHandles($this->getHandles()); 46 - $view->setTitle($xaction->getTitleForFeed()); 46 + $view->setTitle($xaction->getTitleForFeed($this)); 47 47 $body = $xaction->getBodyForFeed($this); 48 48 if (nonempty($body)) { 49 49 $view->appendChild($body);
+1 -1
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 355 355 } 356 356 } 357 357 358 - public function getTitleForFeed() { 358 + public function getTitleForFeed(PhabricatorFeedStory $story) { 359 359 $author_phid = $this->getAuthorPHID(); 360 360 $object_phid = $this->getObjectPHID(); 361 361