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
This commit is contained in:
Bob Trahan
2013-09-18 15:15:25 -07:00
parent 77143d5600
commit b1dfbda741
21 changed files with 326 additions and 109 deletions

View File

@@ -1938,12 +1938,12 @@ phutil_register_library_map(array(
'PonderAnswerTransaction' => 'applications/ponder/storage/PonderAnswerTransaction.php', 'PonderAnswerTransaction' => 'applications/ponder/storage/PonderAnswerTransaction.php',
'PonderAnswerTransactionComment' => 'applications/ponder/storage/PonderAnswerTransactionComment.php', 'PonderAnswerTransactionComment' => 'applications/ponder/storage/PonderAnswerTransactionComment.php',
'PonderAnswerTransactionQuery' => 'applications/ponder/query/PonderAnswerTransactionQuery.php', 'PonderAnswerTransactionQuery' => 'applications/ponder/query/PonderAnswerTransactionQuery.php',
'PonderAnswerViewController' => 'applications/ponder/controller/PonderAnswerViewController.php',
'PonderComment' => 'applications/ponder/storage/PonderComment.php', 'PonderComment' => 'applications/ponder/storage/PonderComment.php',
'PonderCommentQuery' => 'applications/ponder/query/PonderCommentQuery.php', 'PonderCommentQuery' => 'applications/ponder/query/PonderCommentQuery.php',
'PonderConstants' => 'applications/ponder/constants/PonderConstants.php', 'PonderConstants' => 'applications/ponder/constants/PonderConstants.php',
'PonderController' => 'applications/ponder/controller/PonderController.php', 'PonderController' => 'applications/ponder/controller/PonderController.php',
'PonderDAO' => 'applications/ponder/storage/PonderDAO.php', 'PonderDAO' => 'applications/ponder/storage/PonderDAO.php',
'PonderEditor' => 'applications/ponder/editor/PonderEditor.php',
'PonderPHIDTypeAnswer' => 'applications/ponder/phid/PonderPHIDTypeAnswer.php', 'PonderPHIDTypeAnswer' => 'applications/ponder/phid/PonderPHIDTypeAnswer.php',
'PonderPHIDTypeQuestion' => 'applications/ponder/phid/PonderPHIDTypeQuestion.php', 'PonderPHIDTypeQuestion' => 'applications/ponder/phid/PonderPHIDTypeQuestion.php',
'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php', 'PonderQuestion' => 'applications/ponder/storage/PonderQuestion.php',
@@ -1964,6 +1964,7 @@ phutil_register_library_map(array(
'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php', 'PonderQuestionViewController' => 'applications/ponder/controller/PonderQuestionViewController.php',
'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php', 'PonderRemarkupRule' => 'applications/ponder/remarkup/PonderRemarkupRule.php',
'PonderSearchIndexer' => 'applications/ponder/search/PonderSearchIndexer.php', 'PonderSearchIndexer' => 'applications/ponder/search/PonderSearchIndexer.php',
'PonderTransactionFeedStory' => 'applications/ponder/feed/PonderTransactionFeedStory.php',
'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php', 'PonderVotableInterface' => 'applications/ponder/storage/PonderVotableInterface.php',
'PonderVotableView' => 'applications/ponder/view/PonderVotableView.php', 'PonderVotableView' => 'applications/ponder/view/PonderVotableView.php',
'PonderVote' => 'applications/ponder/constants/PonderVote.php', 'PonderVote' => 'applications/ponder/constants/PonderVote.php',
@@ -4149,14 +4150,13 @@ phutil_register_library_map(array(
), ),
'PonderAnswerCommentController' => 'PonderController', 'PonderAnswerCommentController' => 'PonderController',
'PonderAnswerEditController' => 'PonderController', 'PonderAnswerEditController' => 'PonderController',
'PonderAnswerEditor' => 'PhabricatorApplicationTransactionEditor', 'PonderAnswerEditor' => 'PonderEditor',
'PonderAnswerHistoryController' => 'PonderController', 'PonderAnswerHistoryController' => 'PonderController',
'PonderAnswerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PonderAnswerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PonderAnswerSaveController' => 'PonderController', 'PonderAnswerSaveController' => 'PonderController',
'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction', 'PonderAnswerTransaction' => 'PhabricatorApplicationTransaction',
'PonderAnswerTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PonderAnswerTransactionComment' => 'PhabricatorApplicationTransactionComment',
'PonderAnswerTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PonderAnswerTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PonderAnswerViewController' => 'PonderController',
'PonderComment' => 'PonderComment' =>
array( array(
0 => 'PonderDAO', 0 => 'PonderDAO',
@@ -4165,6 +4165,7 @@ phutil_register_library_map(array(
'PonderCommentQuery' => 'PhabricatorQuery', 'PonderCommentQuery' => 'PhabricatorQuery',
'PonderController' => 'PhabricatorController', 'PonderController' => 'PhabricatorController',
'PonderDAO' => 'PhabricatorLiskDAO', 'PonderDAO' => 'PhabricatorLiskDAO',
'PonderEditor' => 'PhabricatorApplicationTransactionEditor',
'PonderPHIDTypeAnswer' => 'PhabricatorPHIDType', 'PonderPHIDTypeAnswer' => 'PhabricatorPHIDType',
'PonderPHIDTypeQuestion' => 'PhabricatorPHIDType', 'PonderPHIDTypeQuestion' => 'PhabricatorPHIDType',
'PonderQuestion' => 'PonderQuestion' =>
@@ -4178,7 +4179,7 @@ phutil_register_library_map(array(
), ),
'PonderQuestionCommentController' => 'PonderController', 'PonderQuestionCommentController' => 'PonderController',
'PonderQuestionEditController' => 'PonderController', 'PonderQuestionEditController' => 'PonderController',
'PonderQuestionEditor' => 'PhabricatorApplicationTransactionEditor', 'PonderQuestionEditor' => 'PonderEditor',
'PonderQuestionHistoryController' => 'PonderController', 'PonderQuestionHistoryController' => 'PonderController',
'PonderQuestionListController' => 'PonderQuestionListController' =>
array( array(
@@ -4197,6 +4198,7 @@ phutil_register_library_map(array(
'PonderQuestionViewController' => 'PonderController', 'PonderQuestionViewController' => 'PonderController',
'PonderRemarkupRule' => 'PhabricatorRemarkupRuleObject', 'PonderRemarkupRule' => 'PhabricatorRemarkupRuleObject',
'PonderSearchIndexer' => 'PhabricatorSearchDocumentIndexer', 'PonderSearchIndexer' => 'PhabricatorSearchDocumentIndexer',
'PonderTransactionFeedStory' => 'PhabricatorApplicationTransactionFeedStory',
'PonderVotableView' => 'AphrontView', 'PonderVotableView' => 'AphrontView',
'PonderVote' => 'PonderConstants', 'PonderVote' => 'PonderConstants',
'PonderVoteEditor' => 'PhabricatorEditor', 'PonderVoteEditor' => 'PhabricatorEditor',

View File

@@ -92,7 +92,7 @@ final class PhabricatorMacroTransaction
return parent::getTitle(); return parent::getTitle();
} }
public function getTitleForFeed() { public function getTitleForFeed(PhabricatorFeedStory $story) {
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID(); $object_phid = $this->getObjectPHID();
@@ -133,7 +133,7 @@ final class PhabricatorMacroTransaction
} }
} }
return parent::getTitleForFeed(); return parent::getTitleForFeed($story);
} }
public function getActionName() { public function getActionName() {

View File

@@ -88,7 +88,7 @@ final class PhabricatorPasteTransaction
return parent::getTitle(); return parent::getTitle();
} }
public function getTitleForFeed() { public function getTitleForFeed(PhabricatorFeedStory $story) {
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID(); $object_phid = $this->getObjectPHID();
@@ -117,7 +117,7 @@ final class PhabricatorPasteTransaction
break; break;
} }
return parent::getTitleForFeed(); return parent::getTitleForFeed($story);
} }
public function getColor() { public function getColor() {

View File

@@ -180,7 +180,7 @@ final class PholioTransaction extends PhabricatorApplicationTransaction {
return parent::getTitle(); return parent::getTitle();
} }
public function getTitleForFeed() { public function getTitleForFeed(PhabricatorFeedStory $story) {
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID(); $object_phid = $this->getObjectPHID();
@@ -243,7 +243,7 @@ final class PholioTransaction extends PhabricatorApplicationTransaction {
break; break;
} }
return parent::getTitleForFeed(); return parent::getTitleForFeed($story);
} }
public function getBodyForFeed(PhabricatorFeedStory $story) { public function getBodyForFeed(PhabricatorFeedStory $story) {

View File

@@ -31,9 +31,8 @@ final class PonderAnswerEditController extends PonderController {
$question = $answer->getQuestion(); $question = $answer->getQuestion();
$qid = $question->getID(); $qid = $question->getID();
$aid = $answer->getID();
$question_uri = "/Q{$qid}#A{$aid}"; $answer_uri = $answer->getURI();
$errors = array(); $errors = array();
if ($request->isFormPost()) { if ($request->isFormPost()) {
@@ -58,7 +57,7 @@ final class PonderAnswerEditController extends PonderController {
$editor->applyTransactions($answer, $xactions); $editor->applyTransactions($answer, $xactions);
return id(new AphrontRedirectResponse()) return id(new AphrontRedirectResponse())
->setURI($question_uri); ->setURI($answer_uri);
} }
} }
@@ -84,13 +83,13 @@ final class PonderAnswerEditController extends PonderController {
->appendChild( ->appendChild(
id(new AphrontFormSubmitControl()) id(new AphrontFormSubmitControl())
->setValue(pht('Update Answer')) ->setValue(pht('Update Answer'))
->addCancelButton($question_uri)); ->addCancelButton($answer_uri));
$crumbs = $this->buildApplicationCrumbs(); $crumbs = $this->buildApplicationCrumbs();
$crumbs->addCrumb( $crumbs->addCrumb(
id(new PhabricatorCrumbView()) id(new PhabricatorCrumbView())
->setName("Q{$qid}") ->setName("Q{$qid}")
->setHref($question_uri)); ->setHref($answer_uri));
$crumbs->addCrumb( $crumbs->addCrumb(
id(new PhabricatorCrumbView()) id(new PhabricatorCrumbView())
->setName(pht('Edit Answer'))); ->setName(pht('Edit Answer')));

View File

@@ -1,25 +0,0 @@
<?php
final class PonderAnswerViewController extends PonderController {
private $answerID;
public function willProcessRequest(array $data) {
$this->answerID = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$answer = id(new PonderAnswer())->load($this->answerID);
if (!$answer) {
return new Aphront404Response();
}
$question_id = $answer->getQuestionID();
return id(new AphrontRedirectResponse())
->setURI('/Q'.$question_id . '#A' . $answer->getID());
}
}

View File

@@ -251,6 +251,8 @@ final class PonderQuestionViewController extends PonderController {
$out[] = phutil_tag('br'); $out[] = phutil_tag('br');
$out[] = phutil_tag('br'); $out[] = phutil_tag('br');
$out[] = id(new PhabricatorAnchorView())
->setAnchorName("A$id");
$out[] = id(new PHUIHeaderView()) $out[] = id(new PHUIHeaderView())
->setHeader($this->getHandle($author_phid)->getFullName()) ->setHeader($this->getHandle($author_phid)->getFullName())
->setImage($this->getHandle($author_phid)->getImageURI()); ->setImage($this->getHandle($author_phid)->getImageURI());

View File

@@ -1,7 +1,6 @@
<?php <?php
final class PonderAnswerEditor final class PonderAnswerEditor extends PonderEditor {
extends PhabricatorApplicationTransactionEditor {
public function getTransactionTypes() { public function getTransactionTypes() {
$types = parent::getTransactionTypes(); $types = parent::getTransactionTypes();
@@ -62,13 +61,44 @@ final class PonderAnswerEditor
return parent::mergeTransactions($u, $v); return parent::mergeTransactions($u, $v);
} }
protected function supportsFeed() { protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
return true; return true;
} }
protected function getMailTo(PhabricatorLiskDAO $object) { protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return array($object->getAuthorPHID()); $question = $object->getQuestion();
return id(new PonderQuestionReplyHandler())
->setMailReceiver($question);
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$question = $object->getQuestion();
return parent::buildMailTemplate($question);
} }
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$body = parent::buildMailBody($object, $xactions);
// If the user just gave the answer, add the answer text.
foreach ($xactions as $xaction) {
$type = $xaction->getTransactionType();
$new = $xaction->getNewValue();
if ($type == PonderAnswerTransaction::TYPE_CONTENT) {
$body->addRawSection($new);
}
}
$body->addTextSection(
pht('ANSWER DETAIL'),
PhabricatorEnv::getProductionURI($object->getURI()));
return $body;
}
} }

View File

@@ -0,0 +1,32 @@
<?php
abstract class PonderEditor
extends PhabricatorApplicationTransactionEditor {
protected function supportsFeed() {
return true;
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$title = $object->getTitle();
$original_title = $object->getOriginalTitle();
return id(new PhabricatorMetaMTAMail())
->setSubject("Q{$id}: {$title}")
->addHeader('Thread-Topic', "Q{$id}: {$original_title}");
}
protected function getMailTo(PhabricatorLiskDAO $object) {
return array(
$object->getAuthorPHID(),
$this->requireActor()->getPHID(),
);
}
protected function getMailSubjectPrefix() {
return '[Ponder]';
}
}

View File

@@ -1,7 +1,26 @@
<?php <?php
final class PonderQuestionEditor final class PonderQuestionEditor
extends PhabricatorApplicationTransactionEditor { extends PonderEditor {
private $answer;
/**
* This is used internally on @{method:applyInitialEffects} if a transaction
* of type PonderQuestionTransaction::TYPE_ANSWERS is in the mix. The value
* is set to the //last// answer in the transactions. Practically, one
* answer is given at a time in the application, though theoretically
* this is buggy.
*
* The answer is used in emails to generate proper links.
*/
private function setAnswer(PonderAnswer $answer) {
$this->answer = $answer;
return $this;
}
private function getAnswer() {
return $this->answer;
}
protected function shouldApplyInitialEffects( protected function shouldApplyInitialEffects(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
@@ -32,6 +51,7 @@ final class PonderQuestionEditor
continue; continue;
} }
$answer->save(); $answer->save();
$this->setAnswer($answer);
} }
break; break;
} }
@@ -145,12 +165,25 @@ final class PonderQuestionEditor
return parent::mergeTransactions($u, $v); return parent::mergeTransactions($u, $v);
} }
protected function supportsFeed() { protected function supportsSearch() {
return true; return true;
} }
protected function supportsSearch() { protected function getFeedStoryType() {
return true; 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( protected function shouldImplyCC(
@@ -176,50 +209,39 @@ final class PonderQuestionEditor
->setMailReceiver($object); ->setMailReceiver($object);
} }
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$title = $object->getTitle();
$original_title = $object->getOriginalTitle();
return id(new PhabricatorMetaMTAMail())
->setSubject("Q{$id}: {$title}")
->addHeader('Thread-Topic', "Q{$id}: {$original_title}");
}
protected function getMailTo(PhabricatorLiskDAO $object) {
return array(
$object->getAuthorPHID(),
$this->requireActor()->getPHID(),
);
}
protected function buildMailBody( protected function buildMailBody(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
array $xactions) { array $xactions) {
$body = parent::buildMailBody($object, $xactions); $body = parent::buildMailBody($object, $xactions);
// If the user just asked the question, add the question text. $header = pht('QUESTION DETAIL');
$uri = '/Q'.$object->getID();
foreach ($xactions as $xaction) { foreach ($xactions as $xaction) {
$type = $xaction->getTransactionType(); $type = $xaction->getTransactionType();
$old = $xaction->getOldValue(); $old = $xaction->getOldValue();
$new = $xaction->getNewValue(); $new = $xaction->getNewValue();
// If the user just asked the question, add the question text.
if ($type == PonderQuestionTransaction::TYPE_CONTENT) { if ($type == PonderQuestionTransaction::TYPE_CONTENT) {
if ($old === null) { if ($old === null) {
$body->addRawSection($new); $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->addTextSection( $body->addTextSection(
pht('QUESTION DETAIL'), $header,
PhabricatorEnv::getProductionURI('/Q'.$object->getID())); PhabricatorEnv::getProductionURI($uri));
return $body; return $body;
} }
protected function getMailSubjectPrefix() {
return '[Ponder]';
}
} }

View File

@@ -0,0 +1,14 @@
<?php
final class PonderTransactionFeedStory
extends PhabricatorApplicationTransactionFeedStory {
public function getRequiredObjectPHIDs() {
$phids = parent::getRequiredObjectPHIDs();
$answer_phid = $this->getValue('answerPHID');
if ($answer_phid) {
$phids[] = $answer_phid;
}
return $phids;
}
}

View File

@@ -17,11 +17,6 @@ final class PonderQuestionReplyHandler extends PhabricatorMailReplyHandler {
return $this->getDefaultPublicReplyHandlerEmailAddress('Q'); return $this->getDefaultPublicReplyHandlerEmailAddress('Q');
} }
public function getReplyHandlerDomain() {
return PhabricatorEnv::getEnvConfig(
'metamta.maniphest.reply-handler-domain');
}
public function getReplyHandlerInstructions() { public function getReplyHandlerInstructions() {
return null; return null;
} }

View File

@@ -36,10 +36,9 @@ final class PonderPHIDTypeAnswer extends PhabricatorPHIDType {
$answer = $objects[$phid]; $answer = $objects[$phid];
$id = $answer->getID(); $id = $answer->getID();
$qid = $answer->getQuestionID();
$handle->setName("Answer {$id}"); $handle->setName("Answer {$id}");
$handle->setURI("/Q{$qid}#A{$id}"); $handle->setURI($answer->getURI());
} }
} }

View File

@@ -36,11 +36,10 @@ final class PonderPHIDTypeQuestion extends PhabricatorPHIDType {
$question = $objects[$phid]; $question = $objects[$phid];
$id = $question->getID(); $id = $question->getID();
$title = $question->getTitle();
$handle->setName("Q{$id}"); $handle->setName("Q{$id}");
$handle->setURI("/Q{$id}"); $handle->setURI("/Q{$id}");
$handle->setFullName("Q{$id}: {$title}"); $handle->setFullName($question->getFullTitle());
} }
} }

View File

@@ -151,7 +151,8 @@ final class PonderQuestionQuery
$answers = mgroup($answers, 'getQuestionID'); $answers = mgroup($answers, 'getQuestionID');
foreach ($questions as $question) { foreach ($questions as $question) {
$question->attachAnswers(idx($answers, $question->getID(), array())); $question_answers = idx($answers, $question->getID(), array());
$question->attachAnswers(mpull($question_answers, null, 'getPHID'));
} }
} }

View File

@@ -23,11 +23,6 @@ final class PonderAnswer extends PonderDAO
private $userVotes = array(); private $userVotes = array();
// TODO: Get rid of this method.
public function setQuestion($question) {
return $this->attachQuestion($question);
}
public function attachQuestion(PonderQuestion $question = null) { public function attachQuestion(PonderQuestion $question = null) {
$this->question = $question; $this->question = $question;
return $this; return $this;
@@ -37,6 +32,10 @@ final class PonderAnswer extends PonderDAO
return $this->assertAttached($this->question); return $this->assertAttached($this->question);
} }
public function getURI() {
return '/Q'.$this->getQuestionID().'#A'.$this->getID();
}
public function setUserVote($vote) { public function setUserVote($vote) {
$this->vote = $vote['data']; $this->vote = $vote['data'];
if (!$this->vote) { if (!$this->vote) {

View File

@@ -21,23 +21,89 @@ final class PonderAnswerTransaction
return new PonderAnswerTransactionComment(); return new PonderAnswerTransactionComment();
} }
public function getTitleForFeed() { public function getRequiredHandlePHIDs() {
$author_phid = $this->getAuthorPHID(); $phids = parent::getRequiredHandlePHIDs();
$object_phid = $this->getObjectPHID();
switch ($this->getTransactionType()) {
$old = $this->getOldValue(); case self::TYPE_CONTENT:
$new = $this->getNewValue(); $phids[] = $this->getObjectPHID();
break;
}
return $phids;
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
switch ($this->getTransactionType()) { switch ($this->getTransactionType()) {
case self::TYPE_CONTENT: case self::TYPE_CONTENT:
// TODO: This is not so good.
return pht( return pht(
'%s edited their answer to %s', '%s edited %s.',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid)); $this->renderHandleLink($object_phid));
} }
return $this->getTitle(); return parent::getTitle();
}
public function getTitleForFeed(PhabricatorFeedStory $story) {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
switch ($this->getTransactionType()) {
case self::TYPE_CONTENT:
$answer = $story->getObject($object_phid);
$question = $answer->getQuestion();
$answer_handle = $this->getHandle($object_phid);
$link = $answer_handle->renderLink(
$question->getFullTitle());
return pht(
'%s updated their answer to %s',
$this->renderHandleLink($author_phid),
$link);
}
return parent::getTitleForFeed($story);
}
public function getBodyForFeed(PhabricatorFeedStory $story) {
$new = $this->getNewValue();
$body = null;
switch ($this->getTransactionType()) {
case self::TYPE_CONTENT:
return phutil_escape_html_newlines(
phutil_utf8_shorten($new, 128));
break;
}
return parent::getBodyForFeed($story);
}
public function hasChangeDetails() {
$old = $this->getOldValue();
switch ($this->getTransactionType()) {
case self::TYPE_CONTENT:
return $old !== null;
}
return parent::hasChangeDetails();
}
public function renderChangeDetails(PhabricatorUser $viewer) {
$old = $this->getOldValue();
$new = $this->getNewValue();
$view = id(new PhabricatorApplicationTransactionTextDiffDetailView())
->setUser($viewer)
->setOldText($old)
->setNewText($new);
return $view->render();
} }
} }

View File

@@ -62,7 +62,7 @@ final class PonderQuestion extends PonderDAO
$this->setComments(idx($comments, $this->getPHID(), array())); $this->setComments(idx($comments, $this->getPHID(), array()));
foreach ($this->answers as $answer) { foreach ($this->answers as $answer) {
$answer->setQuestion($this); $answer->attachQuestion($this);
$answer->setComments(idx($comments, $answer->getPHID(), array())); $answer->setComments(idx($comments, $answer->getPHID(), array()));
} }
} }
@@ -208,6 +208,12 @@ final class PonderQuestion extends PonderDAO
return $this->getTitle(); return $this->getTitle();
} }
public function getFullTitle() {
$id = $this->getID();
$title = $this->getTitle();
return "Q{$id}: {$title}";
}
/* -( PhabricatorTokenReceiverInterface )---------------------------------- */ /* -( PhabricatorTokenReceiverInterface )---------------------------------- */

View File

@@ -24,8 +24,22 @@ final class PonderQuestionTransaction
return new PonderQuestionTransactionComment(); return new PonderQuestionTransactionComment();
} }
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
switch ($this->getTransactionType()) {
case self::TYPE_ANSWERS:
$phids[] = $this->getNewAnswerPHID();
$phids[] = $this->getObjectPHID();
break;
}
return $phids;
}
public function getTitle() { public function getTitle() {
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue(); $old = $this->getOldValue();
$new = $this->getNewValue(); $new = $this->getNewValue();
@@ -48,10 +62,12 @@ final class PonderQuestionTransaction
'%s edited the question description.', '%s edited the question description.',
$this->renderHandleLink($author_phid)); $this->renderHandleLink($author_phid));
case self::TYPE_ANSWERS: case self::TYPE_ANSWERS:
// TODO: This could be richer. $answer_handle = $this->getHandle($this->getNewAnswerPHID());
$question_handle = $this->getHandle($object_phid);
return pht( return pht(
'%s added an answer.', '%s answered %s',
$this->renderHandleLink($author_phid)); $this->renderHandleLink($author_phid),
$answer_handle->renderLink($question_handle->getFullName()));
case self::TYPE_STATUS: case self::TYPE_STATUS:
switch ($new) { switch ($new) {
case PonderQuestionStatus::STATUS_OPEN: case PonderQuestionStatus::STATUS_OPEN:
@@ -178,7 +194,7 @@ final class PonderQuestionTransaction
return parent::shouldHide(); return parent::shouldHide();
} }
public function getTitleForFeed() { public function getTitleForFeed(PhabricatorFeedStory $story) {
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID(); $object_phid = $this->getObjectPHID();
@@ -205,11 +221,12 @@ final class PonderQuestionTransaction
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid)); $this->renderHandleLink($object_phid));
case self::TYPE_ANSWERS: case self::TYPE_ANSWERS:
// TODO: This could be richer, too. $answer_handle = $this->getHandle($this->getNewAnswerPHID());
$question_handle = $this->getHandle($object_phid);
return pht( return pht(
'%s answered %s', '%s answered %s',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid)); $answer_handle->renderLink($question_handle->getFullName()));
case self::TYPE_STATUS: case self::TYPE_STATUS:
switch ($new) { switch ($new) {
case PonderQuestionStatus::STATUS_OPEN: case PonderQuestionStatus::STATUS_OPEN:
@@ -225,8 +242,67 @@ final class PonderQuestionTransaction
} }
} }
return $this->getTitle(); return parent::getTitleForFeed($story);
}
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(
phutil_utf8_shorten($question->getContent(), 128));
}
case self::TYPE_ANSWERS:
$answer = $this->getNewAnswerObject($story);
if ($answer) {
return phutil_escape_html_newlines(
phutil_utf8_shorten($answer->getContent(), 128));
}
}
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(
'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;
} }
} }

View File

@@ -43,7 +43,7 @@ class PhabricatorApplicationTransactionFeedStory
$xaction = $this->getObject(head($xaction_phids)); $xaction = $this->getObject(head($xaction_phids));
$xaction->setHandles($this->getHandles()); $xaction->setHandles($this->getHandles());
$view->setTitle($xaction->getTitleForFeed()); $view->setTitle($xaction->getTitleForFeed($this));
$body = $xaction->getBodyForFeed($this); $body = $xaction->getBodyForFeed($this);
if (nonempty($body)) { if (nonempty($body)) {
$view->appendChild($body); $view->appendChild($body);

View File

@@ -355,7 +355,7 @@ abstract class PhabricatorApplicationTransaction
} }
} }
public function getTitleForFeed() { public function getTitleForFeed(PhabricatorFeedStory $story) {
$author_phid = $this->getAuthorPHID(); $author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID(); $object_phid = $this->getObjectPHID();