diff --git a/src/applications/ponder/editor/PonderQuestionEditor.php b/src/applications/ponder/editor/PonderQuestionEditor.php index d9f77fad94..eeac4b3414 100644 --- a/src/applications/ponder/editor/PonderQuestionEditor.php +++ b/src/applications/ponder/editor/PonderQuestionEditor.php @@ -1,269 +1,282 @@ answer = $answer; return $this; } private function getAnswer() { return $this->answer; } protected function shouldApplyInitialEffects( PhabricatorLiskDAO $object, array $xactions) { foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PonderQuestionTransaction::TYPE_ANSWERS: return true; } } return false; } protected function applyInitialEffects( PhabricatorLiskDAO $object, array $xactions) { foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PonderQuestionTransaction::TYPE_ANSWERS: $new_value = $xaction->getNewValue(); $new = idx($new_value, '+', array()); foreach ($new as $new_answer) { $answer = idx($new_answer, 'answer'); if (!$answer) { continue; } $answer->save(); $this->setAnswer($answer); } break; } } } public function getTransactionTypes() { $types = parent::getTransactionTypes(); $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; $types[] = PhabricatorTransactions::TYPE_SPACE; $types[] = PonderQuestionTransaction::TYPE_TITLE; $types[] = PonderQuestionTransaction::TYPE_CONTENT; $types[] = PonderQuestionTransaction::TYPE_ANSWERS; $types[] = PonderQuestionTransaction::TYPE_STATUS; return $types; } protected function getCustomTransactionOldValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PonderQuestionTransaction::TYPE_TITLE: return $object->getTitle(); case PonderQuestionTransaction::TYPE_CONTENT: return $object->getContent(); case PonderQuestionTransaction::TYPE_ANSWERS: return mpull($object->getAnswers(), 'getPHID'); case PonderQuestionTransaction::TYPE_STATUS: return $object->getStatus(); } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PonderQuestionTransaction::TYPE_TITLE: case PonderQuestionTransaction::TYPE_CONTENT: case PonderQuestionTransaction::TYPE_STATUS: return $xaction->getNewValue(); case PonderQuestionTransaction::TYPE_ANSWERS: $raw_new_value = $xaction->getNewValue(); $new_value = array(); foreach ($raw_new_value as $key => $answers) { $phids = array(); foreach ($answers as $answer) { $obj = idx($answer, 'answer'); if (!$answer) { continue; } $phids[] = $obj->getPHID(); } $new_value[$key] = $phids; } $xaction->setNewValue($new_value); return $this->getPHIDTransactionNewValue($xaction); } } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PonderQuestionTransaction::TYPE_TITLE: $object->setTitle($xaction->getNewValue()); break; case PonderQuestionTransaction::TYPE_CONTENT: $object->setContent($xaction->getNewValue()); break; case PonderQuestionTransaction::TYPE_STATUS: $object->setStatus($xaction->getNewValue()); break; case PonderQuestionTransaction::TYPE_ANSWERS: $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $add = array_diff_key($new, $old); $rem = array_diff_key($old, $new); $count = $object->getAnswerCount(); $count += count($add); $count -= count($rem); $object->setAnswerCount($count); break; } } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { return; } protected function mergeTransactions( PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $v) { $type = $u->getTransactionType(); switch ($type) { case PonderQuestionTransaction::TYPE_TITLE: case PonderQuestionTransaction::TYPE_CONTENT: case PonderQuestionTransaction::TYPE_STATUS: return $v; } return parent::mergeTransactions($u, $v); } protected function supportsSearch() { return true; } protected function getFeedStoryType() { return 'PonderTransactionFeedStory'; } protected function getFeedStoryData( PhabricatorLiskDAO $object, array $xactions) { $data = parent::getFeedStoryData($object, $xactions); $answer = $this->getAnswer(); if ($answer) { $data['answerPHID'] = $answer->getPHID(); } return $data; } protected function shouldImplyCC( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PonderQuestionTransaction::TYPE_ANSWERS: return true; } return parent::shouldImplyCC($object, $xaction); } protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { return true; } + public function getMailTagsMap() { + return array( + PonderQuestionTransaction::MAILTAG_DETAILS => + pht('Someone changes the questions details.'), + PonderQuestionTransaction::MAILTAG_ANSWERS => + pht('Someone adds a new answer.'), + PonderQuestionTransaction::MAILTAG_COMMENT => + pht('Someone comments on the question.'), + PonderQuestionTransaction::MAILTAG_OTHER => + pht('Other question activity not listed above occurs.'), + ); + } + protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new PonderQuestionReplyHandler()) ->setMailReceiver($object); } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); $header = pht('QUESTION DETAIL'); $uri = '/Q'.$object->getID(); foreach ($xactions as $xaction) { $type = $xaction->getTransactionType(); $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); // If the user just asked the question, add the question text. if ($type == PonderQuestionTransaction::TYPE_CONTENT) { if ($old === null) { $body->addRawSection($new); } } // If the user gave an answer, add the answer text. Also update // the header and uri to be more answer-specific. if ($type == PonderQuestionTransaction::TYPE_ANSWERS) { $answer = $this->getAnswer(); $body->addRawSection($answer->getContent()); $header = pht('ANSWER DETAIL'); $uri = $answer->getURI(); } } $body->addLinkSection( $header, PhabricatorEnv::getProductionURI($uri)); return $body; } protected function shouldApplyHeraldRules( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function buildHeraldAdapter( PhabricatorLiskDAO $object, array $xactions) { return id(new HeraldPonderQuestionAdapter()) ->setQuestion($object); } } diff --git a/src/applications/ponder/storage/PonderQuestionTransaction.php b/src/applications/ponder/storage/PonderQuestionTransaction.php index 80cb9f83f0..3c2734ea71 100644 --- a/src/applications/ponder/storage/PonderQuestionTransaction.php +++ b/src/applications/ponder/storage/PonderQuestionTransaction.php @@ -1,331 +1,358 @@ getTransactionType()) { case self::TYPE_ANSWERS: $phids[] = $this->getNewAnswerPHID(); $phids[] = $this->getObjectPHID(); break; } return $phids; } public function getRemarkupBlocks() { $blocks = parent::getRemarkupBlocks(); switch ($this->getTransactionType()) { case self::TYPE_CONTENT: $blocks[] = $this->getNewValue(); break; } return $blocks; } public function getTitle() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: if ($old === null) { return pht( '%s asked this question.', $this->renderHandleLink($author_phid)); } else { return pht( '%s edited the question title from "%s" to "%s".', $this->renderHandleLink($author_phid), $old, $new); } case self::TYPE_CONTENT: return pht( '%s edited the question description.', $this->renderHandleLink($author_phid)); case self::TYPE_ANSWERS: $answer_handle = $this->getHandle($this->getNewAnswerPHID()); $question_handle = $this->getHandle($object_phid); return pht( '%s answered %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case self::TYPE_STATUS: switch ($new) { case PonderQuestionStatus::STATUS_OPEN: return pht( '%s reopened this question.', $this->renderHandleLink($author_phid)); case PonderQuestionStatus::STATUS_CLOSED_RESOLVED: return pht( '%s closed this question as resolved.', $this->renderHandleLink($author_phid)); case PonderQuestionStatus::STATUS_CLOSED_OBSOLETE: return pht( '%s closed this question as obsolete.', $this->renderHandleLink($author_phid)); case PonderQuestionStatus::STATUS_CLOSED_DUPLICATE: return pht( '%s closed this question as a duplicate.', $this->renderHandleLink($author_phid)); } } return parent::getTitle(); } + public function getMailTags() { + $tags = parent::getMailTags(); + + switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_COMMENT: + $tags[] = self::MAILTAG_COMMENT; + break; + case self::TYPE_TITLE: + case self::TYPE_CONTENT: + case self::TYPE_STATUS: + $tags[] = self::MAILTAG_DETAILS; + break; + case self::TYPE_ANSWERS: + $tags[] = self::MAILTAG_ANSWERS; + break; + default: + $tags[] = self::MAILTAG_OTHER; + break; + } + return $tags; + } + public function getIcon() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: case self::TYPE_CONTENT: return 'fa-pencil'; case self::TYPE_STATUS: return PonderQuestionStatus::getQuestionStatusIcon($new); case self::TYPE_ANSWERS: return 'fa-plus'; } return parent::getIcon(); } public function getColor() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: case self::TYPE_CONTENT: return PhabricatorTransactions::COLOR_BLUE; case self::TYPE_ANSWERS: return PhabricatorTransactions::COLOR_GREEN; case self::TYPE_STATUS: return PonderQuestionStatus::getQuestionStatusTagColor($new); } } public function hasChangeDetails() { switch ($this->getTransactionType()) { case self::TYPE_CONTENT: return true; } return parent::hasChangeDetails(); } public function renderChangeDetails(PhabricatorUser $viewer) { return $this->renderTextCorpusChangeDetails( $viewer, $this->getOldValue(), $this->getNewValue()); } public function getActionStrength() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: if ($old === null) { return 3; } break; case self::TYPE_ANSWERS: return 2; } return parent::getActionStrength(); } public function getActionName() { $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: if ($old === null) { return pht('Asked'); } break; case self::TYPE_ANSWERS: return pht('Answered'); } return parent::getActionName(); } public function shouldHide() { switch ($this->getTransactionType()) { case self::TYPE_CONTENT: if ($this->getOldValue() === null) { return true; } else { return false; } break; } return parent::shouldHide(); } public function getTitleForFeed() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: if ($old === null) { return pht( '%s asked a question: %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } else { return pht( '%s edited the title of %s (was "%s")', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $old); } case self::TYPE_CONTENT: return pht( '%s edited the description of %s', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case self::TYPE_ANSWERS: $answer_handle = $this->getHandle($this->getNewAnswerPHID()); $question_handle = $this->getHandle($object_phid); return pht( '%s answered %s', $this->renderHandleLink($author_phid), $answer_handle->renderLink($question_handle->getFullName())); case self::TYPE_STATUS: switch ($new) { case PonderQuestionStatus::STATUS_OPEN: return pht( '%s reopened %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PonderQuestionStatus::STATUS_CLOSED_RESOLVED: return pht( '%s closed %s as resolved.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PonderQuestionStatus::STATUS_CLOSED_DUPLICATE: return pht( '%s closed %s as duplicate.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PonderQuestionStatus::STATUS_CLOSED_OBSOLETE: return pht( '%s closed %s as obsolete.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } } return parent::getTitleForFeed(); } public function getBodyForFeed(PhabricatorFeedStory $story) { $new = $this->getNewValue(); $old = $this->getOldValue(); $body = null; switch ($this->getTransactionType()) { case self::TYPE_TITLE: if ($old === null) { $question = $story->getObject($this->getObjectPHID()); return phutil_escape_html_newlines( id(new PhutilUTF8StringTruncator()) ->setMaximumGlyphs(128) ->truncateString($question->getContent())); } break; case self::TYPE_ANSWERS: $answer = $this->getNewAnswerObject($story); if ($answer) { return phutil_escape_html_newlines( id(new PhutilUTF8StringTruncator()) ->setMaximumGlyphs(128) ->truncateString($answer->getContent())); } break; } return parent::getBodyForFeed($story); } /** * Currently the application only supports adding answers one at a time. * This data is stored as a list of phids. Use this function to get the * new phid. */ private function getNewAnswerPHID() { $new = $this->getNewValue(); $old = $this->getOldValue(); $add = array_diff($new, $old); if (count($add) != 1) { throw new Exception( pht('There should be only one answer added at a time.')); } return reset($add); } /** * Generally, the answer object is only available if the transaction * type is `self::TYPE_ANSWERS`. * * Some stories - notably ones made before D7027 - will be of the more * generic @{class:PhabricatorApplicationTransactionFeedStory}. These * poor stories won't have the PonderAnswer loaded, and thus will have * less cool information. */ private function getNewAnswerObject(PhabricatorFeedStory $story) { if ($story instanceof PonderTransactionFeedStory) { $answer_phid = $this->getNewAnswerPHID(); if ($answer_phid) { return $story->getObject($answer_phid); } } return null; } }