From e0a97c88dbecf6872afd2072bf2d17d7ba495cbc Mon Sep 17 00:00:00 2001 From: epriestley Date: Mon, 28 Dec 2015 05:46:22 -0800 Subject: [PATCH] Provide phame.post.edit Conduit API method Summary: Ref T9897. This one is a little more involved because of how getting a post on a blog works. I also changed moving posts to be a real transaction (which shows up in history, now). Test Plan: Created posts from web UI and conduit. Reviewers: chad Reviewed By: chad Maniphest Tasks: T9897 Differential Revision: https://secure.phabricator.com/D14902 --- src/__phutil_library_map__.php | 4 +- .../ConduitPHIDParameterType.php | 2 +- .../conduit/PhamePostEditConduitAPIMethod.php | 18 +++++++ .../post/PhamePostEditController.php | 28 +++++++++- .../post/PhamePostMoveController.php | 48 ++++++++--------- .../phame/editor/PhamePostEditEngine.php | 24 ++++++--- .../phame/editor/PhamePostEditor.php | 54 +++++++++++++++++++ .../phame/storage/PhamePostTransaction.php | 40 ++++++++++++++ .../editengine/PhabricatorEditEngine.php | 11 +++- .../PhabricatorPHIDListEditField.php | 6 ++- 10 files changed, 197 insertions(+), 38 deletions(-) create mode 100644 src/applications/phame/conduit/PhamePostEditConduitAPIMethod.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index b43ecd7be5..00c9712668 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3429,6 +3429,7 @@ phutil_register_library_map(array( 'PhamePost' => 'applications/phame/storage/PhamePost.php', 'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php', 'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php', + 'PhamePostEditConduitAPIMethod' => 'applications/phame/conduit/PhamePostEditConduitAPIMethod.php', 'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php', 'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php', 'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php', @@ -4159,7 +4160,7 @@ phutil_register_library_map(array( 'ConduitMethodDoesNotExistException' => 'ConduitMethodNotFoundException', 'ConduitMethodNotFoundException' => 'ConduitException', 'ConduitPHIDListParameterType' => 'ConduitListParameterType', - 'ConduitPHIDParameterType' => 'ConduitListParameterType', + 'ConduitPHIDParameterType' => 'ConduitParameterType', 'ConduitParameterType' => 'Phobject', 'ConduitPingConduitAPIMethod' => 'ConduitAPIMethod', 'ConduitProjectListParameterType' => 'ConduitListParameterType', @@ -7891,6 +7892,7 @@ phutil_register_library_map(array( ), 'PhamePostCommentController' => 'PhamePostController', 'PhamePostController' => 'PhameController', + 'PhamePostEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'PhamePostEditController' => 'PhamePostController', 'PhamePostEditEngine' => 'PhabricatorEditEngine', 'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor', diff --git a/src/applications/conduit/parametertype/ConduitPHIDParameterType.php b/src/applications/conduit/parametertype/ConduitPHIDParameterType.php index c10aa2a791..f182758071 100644 --- a/src/applications/conduit/parametertype/ConduitPHIDParameterType.php +++ b/src/applications/conduit/parametertype/ConduitPHIDParameterType.php @@ -1,7 +1,7 @@ blog = $blog; + return $this; + } + + public function getBlog() { + return $this->blog; + } + public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $id = $request->getURIData('id'); @@ -12,6 +23,7 @@ final class PhamePostEditController extends PhamePostController { ->withIDs(array($id)) ->requireCapabilities( array( + PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); @@ -32,15 +44,29 @@ final class PhamePostEditController extends PhamePostController { PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); - if (!$blog) { return new Aphront404Response(); } + $this->setBlog($blog); + return id(new PhamePostEditEngine()) ->setController($this) ->setBlog($blog) ->buildResponse(); } + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $blog = $this->getBlog(); + + $crumbs->addTextCrumb( + $blog->getName(), + $blog->getViewURI()); + + return $crumbs; + } + + } diff --git a/src/applications/phame/controller/post/PhamePostMoveController.php b/src/applications/phame/controller/post/PhamePostMoveController.php index 3ce2586e58..3e09a9ba2d 100644 --- a/src/applications/phame/controller/post/PhamePostMoveController.php +++ b/src/applications/phame/controller/post/PhamePostMoveController.php @@ -20,59 +20,57 @@ final class PhamePostMoveController extends PhamePostController { return new Aphront404Response(); } - $view_uri = '/post/view/'.$post->getID().'/'; - $view_uri = $this->getApplicationURI($view_uri); + $view_uri = $post->getViewURI(); + $v_blog = $post->getBlog()->getPHID(); if ($request->isFormPost()) { - $blog = id(new PhameBlogQuery()) - ->setViewer($viewer) - ->withIDs(array($request->getInt('blog'))) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); + $v_blog = $request->getStr('blogPHID'); - if ($blog) { - $post->setBlogPHID($blog->getPHID()); - $post->save(); + $xactions = array(); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_BLOG) + ->setNewValue($v_blog); - return id(new AphrontRedirectResponse()) - ->setURI($view_uri.'?moved=1'); - } + $editor = id(new PhamePostEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnMissingFields(true) + ->setContinueOnNoEffect(true); + + $editor->applyTransactions($post, $xactions); + + $view_uri = $post->getViewURI(); + + return id(new AphrontRedirectResponse()) + ->setURI($view_uri.'?moved=1'); } $blogs = id(new PhameBlogQuery()) ->setViewer($viewer) ->requireCapabilities( array( + PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->execute(); - $options = mpull($blogs, 'getName', 'getID'); + $options = mpull($blogs, 'getName', 'getPHID'); asort($options); - $selected_value = null; - if ($post && $post->getBlog()) { - $selected_value = $post->getBlog()->getID(); - } - $form = id(new PHUIFormLayoutView()) ->setUser($viewer) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Blog')) - ->setName('blog') + ->setName('blogPHID') ->setOptions($options) - ->setValue($selected_value)); + ->setValue($v_blog)); return $this->newDialog() ->setTitle(pht('Move Post')) ->appendChild($form) ->addSubmitButton(pht('Move Post')) ->addCancelButton($view_uri); - } } diff --git a/src/applications/phame/editor/PhamePostEditEngine.php b/src/applications/phame/editor/PhamePostEditEngine.php index 22e4f50922..652922ea56 100644 --- a/src/applications/phame/editor/PhamePostEditEngine.php +++ b/src/applications/phame/editor/PhamePostEditEngine.php @@ -65,16 +65,24 @@ final class PhamePostEditEngine } protected function buildCustomEditFields($object) { - - if ($this->blog) { - $blog_title = pht('Blog: %s', $this->blog->getName()); - } else { - $blog_title = pht('Sample Blog Title'); - } + $blog_phid = $object->getBlog()->getPHID(); return array( - id(new PhabricatorInstructionsEditField()) - ->setValue($blog_title), + id(new PhabricatorHandlesEditField()) + ->setKey('blog') + ->setLabel(pht('Blog')) + ->setDescription(pht('Blog to publish this post to.')) + ->setConduitDescription( + pht('Choose a blog to create a post on (or move a post to).')) + ->setConduitTypeDescription(pht('PHID of the blog.')) + ->setAliases(array('blogPHID')) + ->setTransactionType(PhamePostTransaction::TYPE_BLOG) + ->setHandleParameterType(new AphrontPHIDListHTTPParameterType()) + ->setSingleValue($blog_phid) + ->setIsReorderable(false) + ->setIsDefaultable(false) + ->setIsLockable(false) + ->setIsLocked(true), id(new PhabricatorTextEditField()) ->setKey('title') ->setLabel(pht('Title')) diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index 4ca1710340..8978be89b6 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -14,6 +14,7 @@ final class PhamePostEditor public function getTransactionTypes() { $types = parent::getTransactionTypes(); + $types[] = PhamePostTransaction::TYPE_BLOG; $types[] = PhamePostTransaction::TYPE_TITLE; $types[] = PhamePostTransaction::TYPE_BODY; $types[] = PhamePostTransaction::TYPE_VISIBILITY; @@ -27,6 +28,8 @@ final class PhamePostEditor PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { + case PhamePostTransaction::TYPE_BLOG: + return $object->getBlogPHID(); case PhamePostTransaction::TYPE_TITLE: return $object->getTitle(); case PhamePostTransaction::TYPE_BODY: @@ -44,6 +47,7 @@ final class PhamePostEditor case PhamePostTransaction::TYPE_TITLE: case PhamePostTransaction::TYPE_BODY: case PhamePostTransaction::TYPE_VISIBILITY: + case PhamePostTransaction::TYPE_BLOG: return $xaction->getNewValue(); } } @@ -57,6 +61,8 @@ final class PhamePostEditor return $object->setTitle($xaction->getNewValue()); case PhamePostTransaction::TYPE_BODY: return $object->setBody($xaction->getNewValue()); + case PhamePostTransaction::TYPE_BLOG: + return $object->setBlogPHID($xaction->getNewValue()); case PhamePostTransaction::TYPE_VISIBILITY: if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) { $object->setDatePublished(0); @@ -77,6 +83,7 @@ final class PhamePostEditor case PhamePostTransaction::TYPE_TITLE: case PhamePostTransaction::TYPE_BODY: case PhamePostTransaction::TYPE_VISIBILITY: + case PhamePostTransaction::TYPE_BLOG: return; } @@ -106,6 +113,53 @@ final class PhamePostEditor $error->setIsMissingFieldError(true); $errors[] = $error; } + break; + case PhamePostTransaction::TYPE_BLOG: + if ($this->getIsNewObject()) { + if (!$xactions) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht( + 'When creating a post, you must specify which blog it '. + 'should belong to.'), + null); + + $error->setIsMissingFieldError(true); + + $errors[] = $error; + break; + } + } + + foreach ($xactions as $xaction) { + $new_phid = $xaction->getNewValue(); + + $blog = id(new PhameBlogQuery()) + ->setViewer($this->getActor()) + ->withPHIDs(array($new_phid)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->execute(); + + if ($blog) { + continue; + } + + $errors[] = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + pht( + 'The specified blog PHID ("%s") is not valid. You can only '. + 'create a post on (or move a post into) a blog which you '. + 'have permission to see and edit.', + $new_phid), + $xaction); + } + break; } return $errors; diff --git a/src/applications/phame/storage/PhamePostTransaction.php b/src/applications/phame/storage/PhamePostTransaction.php index ed341e17db..fcdb7265f8 100644 --- a/src/applications/phame/storage/PhamePostTransaction.php +++ b/src/applications/phame/storage/PhamePostTransaction.php @@ -7,6 +7,7 @@ final class PhamePostTransaction const TYPE_PHAME_TITLE = 'phame.post.phame.title'; const TYPE_BODY = 'phame.post.body'; const TYPE_VISIBILITY = 'phame.post.visibility'; + const TYPE_BLOG = 'phame.post.blog'; const MAILTAG_CONTENT = 'phame-post-content'; const MAILTAG_SUBSCRIBERS = 'phame-post-subscribers'; @@ -47,6 +48,28 @@ final class PhamePostTransaction return parent::shouldHide(); } + public function getRequiredHandlePHIDs() { + $phids = parent::getRequiredHandlePHIDs(); + + switch ($this->getTransactionType()) { + case self::TYPE_BLOG: + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + if ($old) { + $phids[] = $old; + } + + if ($new) { + $phids[] = $new; + } + break; + } + + return $phids; + } + + public function getIcon() { $old = $this->getOldValue(); switch ($this->getTransactionType()) { @@ -98,6 +121,16 @@ final class PhamePostTransaction $type = $this->getTransactionType(); switch ($type) { + case PhabricatorTransactions::TYPE_CREATE: + return pht( + '%s created this post.', + $this->renderHandleLink($author_phid)); + case self::TYPE_BLOG: + return pht( + '%s moved this post from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($old), + $this->renderHandleLink($new)); case self::TYPE_TITLE: if ($old === null) { return pht( @@ -146,6 +179,13 @@ final class PhamePostTransaction $type = $this->getTransactionType(); switch ($type) { + case self::TYPE_BLOG: + return pht( + '%s moved post "%s" from "%s" to "%s".', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid), + $this->renderHandleLink($old), + $this->renderHandleLink($new)); case self::TYPE_TITLE: if ($old === null) { return pht( diff --git a/src/applications/transactions/editengine/PhabricatorEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php index 041bb05393..e44a64f66e 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -1688,7 +1688,16 @@ abstract class PhabricatorEditEngine // Let the parameter type interpret the value. This allows you to // use usernames in list fields, for example. $parameter_type = $type->getConduitParameterType(); - $xaction['value'] = $parameter_type->getValue($xaction, 'value'); + + try { + $xaction['value'] = $parameter_type->getValue($xaction, 'value'); + } catch (Exception $ex) { + throw new PhutilProxyException( + pht( + 'Exception when processing transaction of type "%s".', + $xaction['type']), + $ex); + } $type_xactions = $type->generateTransactions( clone $template, diff --git a/src/applications/transactions/editfield/PhabricatorPHIDListEditField.php b/src/applications/transactions/editfield/PhabricatorPHIDListEditField.php index b0eee2d465..4968aef5b2 100644 --- a/src/applications/transactions/editfield/PhabricatorPHIDListEditField.php +++ b/src/applications/transactions/editfield/PhabricatorPHIDListEditField.php @@ -35,7 +35,11 @@ abstract class PhabricatorPHIDListEditField } protected function newConduitParameterType() { - return new ConduitPHIDListParameterType(); + if ($this->getIsSingleValue()) { + return new ConduitPHIDParameterType(); + } else { + return new ConduitPHIDListParameterType(); + } } protected function getValueFromRequest(AphrontRequest $request, $key) {