@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

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

+197 -38
+3 -1
src/__phutil_library_map__.php
··· 3429 3429 'PhamePost' => 'applications/phame/storage/PhamePost.php', 3430 3430 'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php', 3431 3431 'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php', 3432 + 'PhamePostEditConduitAPIMethod' => 'applications/phame/conduit/PhamePostEditConduitAPIMethod.php', 3432 3433 'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php', 3433 3434 'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php', 3434 3435 'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php', ··· 4159 4160 'ConduitMethodDoesNotExistException' => 'ConduitMethodNotFoundException', 4160 4161 'ConduitMethodNotFoundException' => 'ConduitException', 4161 4162 'ConduitPHIDListParameterType' => 'ConduitListParameterType', 4162 - 'ConduitPHIDParameterType' => 'ConduitListParameterType', 4163 + 'ConduitPHIDParameterType' => 'ConduitParameterType', 4163 4164 'ConduitParameterType' => 'Phobject', 4164 4165 'ConduitPingConduitAPIMethod' => 'ConduitAPIMethod', 4165 4166 'ConduitProjectListParameterType' => 'ConduitListParameterType', ··· 7891 7892 ), 7892 7893 'PhamePostCommentController' => 'PhamePostController', 7893 7894 'PhamePostController' => 'PhameController', 7895 + 'PhamePostEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 7894 7896 'PhamePostEditController' => 'PhamePostController', 7895 7897 'PhamePostEditEngine' => 'PhabricatorEditEngine', 7896 7898 'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor',
+1 -1
src/applications/conduit/parametertype/ConduitPHIDParameterType.php
··· 1 1 <?php 2 2 3 3 final class ConduitPHIDParameterType 4 - extends ConduitListParameterType { 4 + extends ConduitParameterType { 5 5 6 6 protected function getParameterValue(array $request, $key) { 7 7 $value = parent::getParameterValue($request, $key);
+18
src/applications/phame/conduit/PhamePostEditConduitAPIMethod.php
··· 1 + <?php 2 + 3 + final class PhamePostEditConduitAPIMethod 4 + extends PhabricatorEditEngineAPIMethod { 5 + 6 + public function getAPIMethodName() { 7 + return 'phame.post.edit'; 8 + } 9 + 10 + public function newEditEngine() { 11 + return new PhamePostEditEngine(); 12 + } 13 + 14 + public function getMethodSummary() { 15 + return pht('Create or edit blog posts in Phame.'); 16 + } 17 + 18 + }
+27 -1
src/applications/phame/controller/post/PhamePostEditController.php
··· 2 2 3 3 final class PhamePostEditController extends PhamePostController { 4 4 5 + private $blog; 6 + 7 + public function setBlog(PhameBlog $blog) { 8 + $this->blog = $blog; 9 + return $this; 10 + } 11 + 12 + public function getBlog() { 13 + return $this->blog; 14 + } 15 + 5 16 public function handleRequest(AphrontRequest $request) { 6 17 $viewer = $request->getViewer(); 7 18 $id = $request->getURIData('id'); ··· 12 23 ->withIDs(array($id)) 13 24 ->requireCapabilities( 14 25 array( 26 + PhabricatorPolicyCapability::CAN_VIEW, 15 27 PhabricatorPolicyCapability::CAN_EDIT, 16 28 )) 17 29 ->executeOne(); ··· 32 44 PhabricatorPolicyCapability::CAN_EDIT, 33 45 )) 34 46 ->executeOne(); 35 - 36 47 if (!$blog) { 37 48 return new Aphront404Response(); 38 49 } 39 50 51 + $this->setBlog($blog); 52 + 40 53 return id(new PhamePostEditEngine()) 41 54 ->setController($this) 42 55 ->setBlog($blog) 43 56 ->buildResponse(); 44 57 } 58 + 59 + protected function buildApplicationCrumbs() { 60 + $crumbs = parent::buildApplicationCrumbs(); 61 + 62 + $blog = $this->getBlog(); 63 + 64 + $crumbs->addTextCrumb( 65 + $blog->getName(), 66 + $blog->getViewURI()); 67 + 68 + return $crumbs; 69 + } 70 + 45 71 46 72 }
+23 -25
src/applications/phame/controller/post/PhamePostMoveController.php
··· 20 20 return new Aphront404Response(); 21 21 } 22 22 23 - $view_uri = '/post/view/'.$post->getID().'/'; 24 - $view_uri = $this->getApplicationURI($view_uri); 23 + $view_uri = $post->getViewURI(); 24 + $v_blog = $post->getBlog()->getPHID(); 25 25 26 26 if ($request->isFormPost()) { 27 - $blog = id(new PhameBlogQuery()) 28 - ->setViewer($viewer) 29 - ->withIDs(array($request->getInt('blog'))) 30 - ->requireCapabilities( 31 - array( 32 - PhabricatorPolicyCapability::CAN_EDIT, 33 - )) 34 - ->executeOne(); 27 + $v_blog = $request->getStr('blogPHID'); 35 28 36 - if ($blog) { 37 - $post->setBlogPHID($blog->getPHID()); 38 - $post->save(); 29 + $xactions = array(); 30 + $xactions[] = id(new PhamePostTransaction()) 31 + ->setTransactionType(PhamePostTransaction::TYPE_BLOG) 32 + ->setNewValue($v_blog); 39 33 40 - return id(new AphrontRedirectResponse()) 41 - ->setURI($view_uri.'?moved=1'); 42 - } 34 + $editor = id(new PhamePostEditor()) 35 + ->setActor($viewer) 36 + ->setContentSourceFromRequest($request) 37 + ->setContinueOnMissingFields(true) 38 + ->setContinueOnNoEffect(true); 39 + 40 + $editor->applyTransactions($post, $xactions); 41 + 42 + $view_uri = $post->getViewURI(); 43 + 44 + return id(new AphrontRedirectResponse()) 45 + ->setURI($view_uri.'?moved=1'); 43 46 } 44 47 45 48 $blogs = id(new PhameBlogQuery()) 46 49 ->setViewer($viewer) 47 50 ->requireCapabilities( 48 51 array( 52 + PhabricatorPolicyCapability::CAN_VIEW, 49 53 PhabricatorPolicyCapability::CAN_EDIT, 50 54 )) 51 55 ->execute(); 52 56 53 - $options = mpull($blogs, 'getName', 'getID'); 57 + $options = mpull($blogs, 'getName', 'getPHID'); 54 58 asort($options); 55 59 56 - $selected_value = null; 57 - if ($post && $post->getBlog()) { 58 - $selected_value = $post->getBlog()->getID(); 59 - } 60 - 61 60 $form = id(new PHUIFormLayoutView()) 62 61 ->setUser($viewer) 63 62 ->appendChild( 64 63 id(new AphrontFormSelectControl()) 65 64 ->setLabel(pht('Blog')) 66 - ->setName('blog') 65 + ->setName('blogPHID') 67 66 ->setOptions($options) 68 - ->setValue($selected_value)); 67 + ->setValue($v_blog)); 69 68 70 69 return $this->newDialog() 71 70 ->setTitle(pht('Move Post')) 72 71 ->appendChild($form) 73 72 ->addSubmitButton(pht('Move Post')) 74 73 ->addCancelButton($view_uri); 75 - 76 74 } 77 75 78 76 }
+16 -8
src/applications/phame/editor/PhamePostEditEngine.php
··· 65 65 } 66 66 67 67 protected function buildCustomEditFields($object) { 68 - 69 - if ($this->blog) { 70 - $blog_title = pht('Blog: %s', $this->blog->getName()); 71 - } else { 72 - $blog_title = pht('Sample Blog Title'); 73 - } 68 + $blog_phid = $object->getBlog()->getPHID(); 74 69 75 70 return array( 76 - id(new PhabricatorInstructionsEditField()) 77 - ->setValue($blog_title), 71 + id(new PhabricatorHandlesEditField()) 72 + ->setKey('blog') 73 + ->setLabel(pht('Blog')) 74 + ->setDescription(pht('Blog to publish this post to.')) 75 + ->setConduitDescription( 76 + pht('Choose a blog to create a post on (or move a post to).')) 77 + ->setConduitTypeDescription(pht('PHID of the blog.')) 78 + ->setAliases(array('blogPHID')) 79 + ->setTransactionType(PhamePostTransaction::TYPE_BLOG) 80 + ->setHandleParameterType(new AphrontPHIDListHTTPParameterType()) 81 + ->setSingleValue($blog_phid) 82 + ->setIsReorderable(false) 83 + ->setIsDefaultable(false) 84 + ->setIsLockable(false) 85 + ->setIsLocked(true), 78 86 id(new PhabricatorTextEditField()) 79 87 ->setKey('title') 80 88 ->setLabel(pht('Title'))
+54
src/applications/phame/editor/PhamePostEditor.php
··· 14 14 public function getTransactionTypes() { 15 15 $types = parent::getTransactionTypes(); 16 16 17 + $types[] = PhamePostTransaction::TYPE_BLOG; 17 18 $types[] = PhamePostTransaction::TYPE_TITLE; 18 19 $types[] = PhamePostTransaction::TYPE_BODY; 19 20 $types[] = PhamePostTransaction::TYPE_VISIBILITY; ··· 27 28 PhabricatorApplicationTransaction $xaction) { 28 29 29 30 switch ($xaction->getTransactionType()) { 31 + case PhamePostTransaction::TYPE_BLOG: 32 + return $object->getBlogPHID(); 30 33 case PhamePostTransaction::TYPE_TITLE: 31 34 return $object->getTitle(); 32 35 case PhamePostTransaction::TYPE_BODY: ··· 44 47 case PhamePostTransaction::TYPE_TITLE: 45 48 case PhamePostTransaction::TYPE_BODY: 46 49 case PhamePostTransaction::TYPE_VISIBILITY: 50 + case PhamePostTransaction::TYPE_BLOG: 47 51 return $xaction->getNewValue(); 48 52 } 49 53 } ··· 57 61 return $object->setTitle($xaction->getNewValue()); 58 62 case PhamePostTransaction::TYPE_BODY: 59 63 return $object->setBody($xaction->getNewValue()); 64 + case PhamePostTransaction::TYPE_BLOG: 65 + return $object->setBlogPHID($xaction->getNewValue()); 60 66 case PhamePostTransaction::TYPE_VISIBILITY: 61 67 if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) { 62 68 $object->setDatePublished(0); ··· 77 83 case PhamePostTransaction::TYPE_TITLE: 78 84 case PhamePostTransaction::TYPE_BODY: 79 85 case PhamePostTransaction::TYPE_VISIBILITY: 86 + case PhamePostTransaction::TYPE_BLOG: 80 87 return; 81 88 } 82 89 ··· 106 113 $error->setIsMissingFieldError(true); 107 114 $errors[] = $error; 108 115 } 116 + break; 117 + case PhamePostTransaction::TYPE_BLOG: 118 + if ($this->getIsNewObject()) { 119 + if (!$xactions) { 120 + $error = new PhabricatorApplicationTransactionValidationError( 121 + $type, 122 + pht('Required'), 123 + pht( 124 + 'When creating a post, you must specify which blog it '. 125 + 'should belong to.'), 126 + null); 127 + 128 + $error->setIsMissingFieldError(true); 129 + 130 + $errors[] = $error; 131 + break; 132 + } 133 + } 134 + 135 + foreach ($xactions as $xaction) { 136 + $new_phid = $xaction->getNewValue(); 137 + 138 + $blog = id(new PhameBlogQuery()) 139 + ->setViewer($this->getActor()) 140 + ->withPHIDs(array($new_phid)) 141 + ->requireCapabilities( 142 + array( 143 + PhabricatorPolicyCapability::CAN_VIEW, 144 + PhabricatorPolicyCapability::CAN_EDIT, 145 + )) 146 + ->execute(); 147 + 148 + if ($blog) { 149 + continue; 150 + } 151 + 152 + $errors[] = new PhabricatorApplicationTransactionValidationError( 153 + $type, 154 + pht('Invalid'), 155 + pht( 156 + 'The specified blog PHID ("%s") is not valid. You can only '. 157 + 'create a post on (or move a post into) a blog which you '. 158 + 'have permission to see and edit.', 159 + $new_phid), 160 + $xaction); 161 + } 162 + 109 163 break; 110 164 } 111 165 return $errors;
+40
src/applications/phame/storage/PhamePostTransaction.php
··· 7 7 const TYPE_PHAME_TITLE = 'phame.post.phame.title'; 8 8 const TYPE_BODY = 'phame.post.body'; 9 9 const TYPE_VISIBILITY = 'phame.post.visibility'; 10 + const TYPE_BLOG = 'phame.post.blog'; 10 11 11 12 const MAILTAG_CONTENT = 'phame-post-content'; 12 13 const MAILTAG_SUBSCRIBERS = 'phame-post-subscribers'; ··· 47 48 return parent::shouldHide(); 48 49 } 49 50 51 + public function getRequiredHandlePHIDs() { 52 + $phids = parent::getRequiredHandlePHIDs(); 53 + 54 + switch ($this->getTransactionType()) { 55 + case self::TYPE_BLOG: 56 + $old = $this->getOldValue(); 57 + $new = $this->getNewValue(); 58 + 59 + if ($old) { 60 + $phids[] = $old; 61 + } 62 + 63 + if ($new) { 64 + $phids[] = $new; 65 + } 66 + break; 67 + } 68 + 69 + return $phids; 70 + } 71 + 72 + 50 73 public function getIcon() { 51 74 $old = $this->getOldValue(); 52 75 switch ($this->getTransactionType()) { ··· 98 121 99 122 $type = $this->getTransactionType(); 100 123 switch ($type) { 124 + case PhabricatorTransactions::TYPE_CREATE: 125 + return pht( 126 + '%s created this post.', 127 + $this->renderHandleLink($author_phid)); 128 + case self::TYPE_BLOG: 129 + return pht( 130 + '%s moved this post from "%s" to "%s".', 131 + $this->renderHandleLink($author_phid), 132 + $this->renderHandleLink($old), 133 + $this->renderHandleLink($new)); 101 134 case self::TYPE_TITLE: 102 135 if ($old === null) { 103 136 return pht( ··· 146 179 147 180 $type = $this->getTransactionType(); 148 181 switch ($type) { 182 + case self::TYPE_BLOG: 183 + return pht( 184 + '%s moved post "%s" from "%s" to "%s".', 185 + $this->renderHandleLink($author_phid), 186 + $this->renderHandleLink($object_phid), 187 + $this->renderHandleLink($old), 188 + $this->renderHandleLink($new)); 149 189 case self::TYPE_TITLE: 150 190 if ($old === null) { 151 191 return pht(
+10 -1
src/applications/transactions/editengine/PhabricatorEditEngine.php
··· 1688 1688 // Let the parameter type interpret the value. This allows you to 1689 1689 // use usernames in list<user> fields, for example. 1690 1690 $parameter_type = $type->getConduitParameterType(); 1691 - $xaction['value'] = $parameter_type->getValue($xaction, 'value'); 1691 + 1692 + try { 1693 + $xaction['value'] = $parameter_type->getValue($xaction, 'value'); 1694 + } catch (Exception $ex) { 1695 + throw new PhutilProxyException( 1696 + pht( 1697 + 'Exception when processing transaction of type "%s".', 1698 + $xaction['type']), 1699 + $ex); 1700 + } 1692 1701 1693 1702 $type_xactions = $type->generateTransactions( 1694 1703 clone $template,
+5 -1
src/applications/transactions/editfield/PhabricatorPHIDListEditField.php
··· 35 35 } 36 36 37 37 protected function newConduitParameterType() { 38 - return new ConduitPHIDListParameterType(); 38 + if ($this->getIsSingleValue()) { 39 + return new ConduitPHIDParameterType(); 40 + } else { 41 + return new ConduitPHIDListParameterType(); 42 + } 39 43 } 40 44 41 45 protected function getValueFromRequest(AphrontRequest $request, $key) {