From 946a5cd5ceb7516bd2f3a5ec8b08e791b92ccfe3 Mon Sep 17 00:00:00 2001 From: epriestley Date: Sun, 28 Jul 2013 17:23:04 -0700 Subject: [PATCH] Allow ponder answers to be edited Summary: Ref T3373. Use applicationtransactions to edit ponder answers. Also enable tokens and subscriptions. Test Plan: edited an answer Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T3373 Differential Revision: https://secure.phabricator.com/D6607 --- src/__phutil_library_map__.php | 6 +- .../PhabricatorApplicationPonder.php | 1 + .../controller/PonderAnswerEditController.php | 108 +++++++++++++ .../PonderQuestionViewController.php | 8 +- .../ponder/editor/PonderAnswerEditor.php | 145 +++++++----------- .../ponder/storage/PonderAnswer.php | 23 ++- .../storage/PonderAnswerTransaction.php | 2 + .../ponder/storage/PonderQuestion.php | 4 +- 8 files changed, 199 insertions(+), 98 deletions(-) create mode 100644 src/applications/ponder/controller/PonderAnswerEditController.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 8c8c26b5e2..e0190a9f67 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1870,6 +1870,7 @@ phutil_register_library_map(array( 'PonderAddAnswerView' => 'applications/ponder/view/PonderAddAnswerView.php', 'PonderAddCommentView' => 'applications/ponder/view/PonderAddCommentView.php', 'PonderAnswer' => 'applications/ponder/storage/PonderAnswer.php', + 'PonderAnswerEditController' => 'applications/ponder/controller/PonderAnswerEditController.php', 'PonderAnswerEditor' => 'applications/ponder/editor/PonderAnswerEditor.php', 'PonderAnswerListView' => 'applications/ponder/view/PonderAnswerListView.php', 'PonderAnswerPreviewController' => 'applications/ponder/controller/PonderAnswerPreviewController.php', @@ -3990,8 +3991,11 @@ phutil_register_library_map(array( 1 => 'PhabricatorMarkupInterface', 2 => 'PonderVotableInterface', 3 => 'PhabricatorPolicyInterface', + 4 => 'PhabricatorSubscribableInterface', + 5 => 'PhabricatorTokenReceiverInterface', ), - 'PonderAnswerEditor' => 'PhabricatorEditor', + 'PonderAnswerEditController' => 'PonderController', + 'PonderAnswerEditor' => 'PhabricatorApplicationTransactionEditor', 'PonderAnswerListView' => 'AphrontView', 'PonderAnswerPreviewController' => 'PonderController', 'PonderAnswerQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', diff --git a/src/applications/ponder/application/PhabricatorApplicationPonder.php b/src/applications/ponder/application/PhabricatorApplicationPonder.php index 38b9c3d9ef..738b947285 100644 --- a/src/applications/ponder/application/PhabricatorApplicationPonder.php +++ b/src/applications/ponder/application/PhabricatorApplicationPonder.php @@ -51,6 +51,7 @@ final class PhabricatorApplicationPonder extends PhabricatorApplication { '/ponder/' => array( '(?:query/(?P[^/]+)/)?' => 'PonderQuestionListController', 'answer/add/' => 'PonderAnswerSaveController', + 'answer/edit/(?P\d+)/' => 'PonderAnswerEditController', 'answer/preview/' => 'PonderAnswerPreviewController', 'question/edit/(?:(?P\d+)/)?' => 'PonderQuestionEditController', 'question/preview/' => 'PonderQuestionPreviewController', diff --git a/src/applications/ponder/controller/PonderAnswerEditController.php b/src/applications/ponder/controller/PonderAnswerEditController.php new file mode 100644 index 0000000000..c01893f8aa --- /dev/null +++ b/src/applications/ponder/controller/PonderAnswerEditController.php @@ -0,0 +1,108 @@ +id = $data['id']; + } + + public function processRequest() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + + $answer = id(new PonderAnswerQuery()) + ->setViewer($viewer) + ->withIDs(array($this->id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$answer) { + return new Aphront404Response(); + } + + $v_content = $answer->getContent(); + $e_content = true; + + + $question = $answer->getQuestion(); + $qid = $question->getID(); + $aid = $answer->getID(); + + $question_uri = "/Q{$qid}#A{$aid}"; + + $errors = array(); + if ($request->isFormPost()) { + $v_content = $request->getStr('content'); + + if (!strlen($v_content)) { + $errors[] = pht('You must provide some substance in your answer.'); + $e_content = pht('Required'); + } + + if (!$errors) { + $xactions = array(); + $xactions[] = id(new PonderAnswerTransaction()) + ->setTransactionType(PonderAnswerTransaction::TYPE_CONTENT) + ->setNewValue($v_content); + + $editor = id(new PonderAnswerEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true); + + $editor->applyTransactions($answer, $xactions); + + return id(new AphrontRedirectResponse()) + ->setURI($question_uri); + } + } + + if ($errors) { + $errors = id(new AphrontErrorView())->setErrors($errors); + } + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->appendChild( + id(new AphrontFormStaticControl()) + ->setLabel(pht('Question')) + ->setValue($question->getTitle())) + ->appendChild( + id(new PhabricatorRemarkupControl()) + ->setLabel(pht('Answer')) + ->setName('content') + ->setValue($v_content) + ->setError($e_content)) + ->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Update Answer')) + ->addCancelButton($question_uri)); + + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName("Q{$qid}") + ->setHref($question_uri)); + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName(pht('Edit Answer'))); + + return $this->buildApplicationPage( + array( + $crumbs, + $errors, + $form, + ), + array( + 'title' => pht('Edit Answer'), + 'dust' => true, + 'device' => true, + )); + + } +} diff --git a/src/applications/ponder/controller/PonderQuestionViewController.php b/src/applications/ponder/controller/PonderQuestionViewController.php index a8b34d5d8f..93d4065a4e 100644 --- a/src/applications/ponder/controller/PonderQuestionViewController.php +++ b/src/applications/ponder/controller/PonderQuestionViewController.php @@ -237,6 +237,8 @@ final class PonderQuestionViewController extends PonderController { $request = $this->getRequest(); $viewer = $request->getUser(); + $id = $answer->getID(); + $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $answer, @@ -247,10 +249,6 @@ final class PonderQuestionViewController extends PonderController { ->setObject($answer) ->setObjectURI($request->getRequestURI()); -/* - - TODO - $view->addAction( id(new PhabricatorActionView()) ->setIcon('edit') @@ -259,8 +257,6 @@ final class PonderQuestionViewController extends PonderController { ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); -*/ - return $view; } diff --git a/src/applications/ponder/editor/PonderAnswerEditor.php b/src/applications/ponder/editor/PonderAnswerEditor.php index 5ccbde79f8..e7c03d7688 100644 --- a/src/applications/ponder/editor/PonderAnswerEditor.php +++ b/src/applications/ponder/editor/PonderAnswerEditor.php @@ -1,99 +1,66 @@ question = $question; - return $this; + $types[] = PhabricatorTransactions::TYPE_COMMENT; + $types[] = PonderAnswerTransaction::TYPE_CONTENT; + + return $types; } - public function setAnswer($answer) { - $this->answer = $answer; - return $this; - } + protected function getCustomTransactionOldValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { - public function saveAnswer() { - $actor = $this->requireActor(); - if (!$this->question) { - throw new Exception("Must set question before saving answer"); - } - if (!$this->answer) { - throw new Exception("Must set answer before saving it"); - } - - $question = $this->question; - $answer = $this->answer; - $conn = $answer->establishConnection('w'); - $trans = $conn->openTransaction(); - $trans->beginReadLocking(); - - $question->reload(); - - queryfx($conn, - 'UPDATE %T as t - SET t.`answerCount` = t.`answerCount` + 1 - WHERE t.`PHID` = %s', - $question->getTableName(), - $question->getPHID()); - - $answer->setQuestionID($question->getID()); - $answer->save(); - - $trans->endReadLocking(); - $trans->saveTransaction(); - - $question->attachRelated(); - id(new PhabricatorSearchIndexer()) - ->indexDocumentByPHID($question->getPHID()); - - // subscribe author and @mentions - $subeditor = id(new PhabricatorSubscriptionsEditor()) - ->setObject($question) - ->setActor($actor); - - $subeditor->subscribeExplicit(array($answer->getAuthorPHID())); - - $content = $answer->getContent(); - $at_mention_phids = PhabricatorMarkupEngine::extractPHIDsFromMentions( - array($content)); - $subeditor->subscribeImplicit($at_mention_phids); - $subeditor->save(); - - if ($this->shouldEmail) { - // now load subscribers, including implicitly-added @mention victims - $subscribers = PhabricatorSubscribersQuery - ::loadSubscribersForPHID($question->getPHID()); - - - // @mention emails (but not for anyone who has explicitly unsubscribed) - if (array_intersect($at_mention_phids, $subscribers)) { - id(new PonderMentionMail( - $question, - $answer, - $actor)) - ->setToPHIDs($at_mention_phids) - ->send(); - } - - $other_subs = - array_diff( - $subscribers, - $at_mention_phids); - - // 'Answered' emails for subscribers who are not @mentiond (and excluding - // author depending on their MetaMTA settings). - if ($other_subs) { - id(new PonderAnsweredMail( - $question, - $answer, - $actor)) - ->setToPHIDs($other_subs) - ->send(); - } + switch ($xaction->getTransactionType()) { + case PonderAnswerTransaction::TYPE_CONTENT: + return $object->getContent(); } } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PonderAnswerTransaction::TYPE_CONTENT: + return $xaction->getNewValue(); + } + } + + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PonderAnswerTransaction::TYPE_CONTENT: + $object->setContent($xaction->getNewValue()); + break; + } + } + + protected function applyCustomExternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + return; + } + + protected function mergeTransactions( + PhabricatorApplicationTransaction $u, + PhabricatorApplicationTransaction $v) { + + $type = $u->getTransactionType(); + switch ($type) { + case PonderAnswerTransaction::TYPE_CONTENT: + return $v; + } + + return parent::mergeTransactions($u, $v); + } + + } diff --git a/src/applications/ponder/storage/PonderAnswer.php b/src/applications/ponder/storage/PonderAnswer.php index a825723e34..cfddc46b5d 100644 --- a/src/applications/ponder/storage/PonderAnswer.php +++ b/src/applications/ponder/storage/PonderAnswer.php @@ -4,7 +4,9 @@ final class PonderAnswer extends PonderDAO implements PhabricatorMarkupInterface, PonderVotableInterface, - PhabricatorPolicyInterface { + PhabricatorPolicyInterface, + PhabricatorSubscribableInterface, + PhabricatorTokenReceiverInterface { const MARKUP_FIELD_CONTENT = 'markup:content'; @@ -145,4 +147,23 @@ final class PonderAnswer extends PonderDAO } } + +/* -( PhabricatorTokenReceiverInterface )---------------------------------- */ + + + public function getUsersToNotifyOfTokenGiven() { + return array( + $this->getAuthorPHID(), + ); + } + + +/* -( PhabricatorSubscribableInterface )----------------------------------- */ + + + public function isAutomaticallySubscribed($phid) { + return ($phid == $this->getAuthorPHID()); + } + + } diff --git a/src/applications/ponder/storage/PonderAnswerTransaction.php b/src/applications/ponder/storage/PonderAnswerTransaction.php index 70135bfa6d..288c803774 100644 --- a/src/applications/ponder/storage/PonderAnswerTransaction.php +++ b/src/applications/ponder/storage/PonderAnswerTransaction.php @@ -3,6 +3,8 @@ final class PonderAnswerTransaction extends PhabricatorApplicationTransaction { + const TYPE_CONTENT = 'ponder.answer:content'; + public function getApplicationName() { return 'ponder'; } diff --git a/src/applications/ponder/storage/PonderQuestion.php b/src/applications/ponder/storage/PonderQuestion.php index 83cdfb0a61..3f98ee870f 100644 --- a/src/applications/ponder/storage/PonderQuestion.php +++ b/src/applications/ponder/storage/PonderQuestion.php @@ -165,7 +165,7 @@ final class PonderQuestion extends PonderDAO } public function isAutomaticallySubscribed($phid) { - return false; + return ($phid == $this->getAuthorPHID()); } public function save() { @@ -198,8 +198,10 @@ final class PonderQuestion extends PonderDAO return ($viewer->getPHID() == $this->getAuthorPHID()); } + /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ + public function getUsersToNotifyOfTokenGiven() { return array( $this->getAuthorPHID(),