@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

Update Macro for EditEngine

Summary: Updates Macro to use EditEngine. Also removes "URL" field for adding a Macro, which I think it's worth pursuing.

Test Plan:
- Create a Macro
- Forget to name it
- Try a PDF
- Use a Macro
- Edit a macro (not working)

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin

Differential Revision: https://secure.phabricator.com/D17821

+181 -305
+3 -1
src/__phutil_library_map__.php
··· 2982 2982 'PhabricatorMacroDisableController' => 'applications/macro/controller/PhabricatorMacroDisableController.php', 2983 2983 'PhabricatorMacroDisabledTransaction' => 'applications/macro/xaction/PhabricatorMacroDisabledTransaction.php', 2984 2984 'PhabricatorMacroEditController' => 'applications/macro/controller/PhabricatorMacroEditController.php', 2985 + 'PhabricatorMacroEditEngine' => 'applications/macro/editor/PhabricatorMacroEditEngine.php', 2985 2986 'PhabricatorMacroEditor' => 'applications/macro/editor/PhabricatorMacroEditor.php', 2986 2987 'PhabricatorMacroFileTransaction' => 'applications/macro/xaction/PhabricatorMacroFileTransaction.php', 2987 2988 'PhabricatorMacroListController' => 'applications/macro/controller/PhabricatorMacroListController.php', ··· 8187 8188 'PhabricatorMacroDatasource' => 'PhabricatorTypeaheadDatasource', 8188 8189 'PhabricatorMacroDisableController' => 'PhabricatorMacroController', 8189 8190 'PhabricatorMacroDisabledTransaction' => 'PhabricatorMacroTransactionType', 8190 - 'PhabricatorMacroEditController' => 'PhabricatorMacroController', 8191 + 'PhabricatorMacroEditController' => 'PhameBlogController', 8192 + 'PhabricatorMacroEditEngine' => 'PhabricatorEditEngine', 8191 8193 'PhabricatorMacroEditor' => 'PhabricatorApplicationTransactionEditor', 8192 8194 'PhabricatorMacroFileTransaction' => 'PhabricatorMacroTransactionType', 8193 8195 'PhabricatorMacroListController' => 'PhabricatorMacroController',
+2 -1
src/applications/macro/application/PhabricatorMacroApplication.php
··· 33 33 'create/' => 'PhabricatorMacroEditController', 34 34 'view/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroViewController', 35 35 'comment/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroCommentController', 36 - 'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroEditController', 36 + $this->getEditRoutePattern('edit/') 37 + => 'PhabricatorMacroEditController', 37 38 'audio/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroAudioController', 38 39 'disable/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroDisableController', 39 40 'meme/' => 'PhabricatorMacroMemeController',
+4 -298
src/applications/macro/controller/PhabricatorMacroEditController.php
··· 1 1 <?php 2 2 3 - final class PhabricatorMacroEditController extends PhabricatorMacroController { 3 + final class PhabricatorMacroEditController extends PhameBlogController { 4 4 5 5 public function handleRequest(AphrontRequest $request) { 6 - $viewer = $request->getViewer(); 7 - $id = $request->getURIData('id'); 8 - 9 - $this->requireApplicationCapability( 10 - PhabricatorMacroManageCapability::CAPABILITY); 11 - 12 - if ($id) { 13 - $macro = id(new PhabricatorMacroQuery()) 14 - ->setViewer($viewer) 15 - ->withIDs(array($id)) 16 - ->needFiles(true) 17 - ->executeOne(); 18 - if (!$macro) { 19 - return new Aphront404Response(); 20 - } 21 - } else { 22 - $macro = new PhabricatorFileImageMacro(); 23 - $macro->setAuthorPHID($viewer->getPHID()); 24 - } 25 - 26 - $errors = array(); 27 - $e_name = true; 28 - $e_file = null; 29 - $file = null; 30 - 31 - if ($request->isFormPost()) { 32 - $original = clone $macro; 33 - 34 - $new_name = null; 35 - if ($request->getBool('name_form') || !$macro->getID()) { 36 - $new_name = $request->getStr('name'); 37 - 38 - $macro->setName($new_name); 39 - 40 - if (!strlen($macro->getName())) { 41 - $errors[] = pht('Macro name is required.'); 42 - $e_name = pht('Required'); 43 - } else if (!preg_match('/^[a-z0-9:_-]{3,}\z/', $macro->getName())) { 44 - $errors[] = pht( 45 - 'Macro must be at least three characters long and contain only '. 46 - 'lowercase letters, digits, hyphens, colons and underscores.'); 47 - $e_name = pht('Invalid'); 48 - } else { 49 - $e_name = null; 50 - } 51 - } 52 - 53 - $uri = $request->getStr('url'); 54 - 55 - $engine = new PhabricatorDestructionEngine(); 56 - 57 - $file = null; 58 - if ($request->getFileExists('file')) { 59 - $file = PhabricatorFile::newFromPHPUpload( 60 - $_FILES['file'], 61 - array( 62 - 'name' => $request->getStr('name'), 63 - 'authorPHID' => $viewer->getPHID(), 64 - 'isExplicitUpload' => true, 65 - 'canCDN' => true, 66 - )); 67 - } else if ($uri) { 68 - try { 69 - // Rate limit outbound fetches to make this mechanism less useful for 70 - // scanning networks and ports. 71 - PhabricatorSystemActionEngine::willTakeAction( 72 - array($viewer->getPHID()), 73 - new PhabricatorFilesOutboundRequestAction(), 74 - 1); 75 - 76 - $file = PhabricatorFile::newFromFileDownload( 77 - $uri, 78 - array( 79 - 'name' => $request->getStr('name'), 80 - 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 81 - 'isExplicitUpload' => true, 82 - 'canCDN' => true, 83 - )); 84 - 85 - if (!$file->isViewableInBrowser()) { 86 - $mime_type = $file->getMimeType(); 87 - $engine->destroyObject($file); 88 - $file = null; 89 - throw new Exception( 90 - pht( 91 - 'The URI "%s" does not correspond to a valid image file, got '. 92 - 'a file with MIME type "%s". You must specify the URI of a '. 93 - 'valid image file.', 94 - $uri, 95 - $mime_type)); 96 - } else { 97 - $file 98 - ->setAuthorPHID($viewer->getPHID()) 99 - ->save(); 100 - } 101 - } catch (HTTPFutureHTTPResponseStatus $status) { 102 - $errors[] = pht( 103 - 'The URI "%s" could not be loaded, got %s error.', 104 - $uri, 105 - $status->getStatusCode()); 106 - } catch (Exception $ex) { 107 - $errors[] = $ex->getMessage(); 108 - } 109 - } else if ($request->getStr('phid')) { 110 - $file = id(new PhabricatorFileQuery()) 111 - ->setViewer($viewer) 112 - ->withPHIDs(array($request->getStr('phid'))) 113 - ->executeOne(); 114 - } 115 - 116 - if ($file) { 117 - if (!$file->isViewableInBrowser()) { 118 - $errors[] = pht('You must upload an image.'); 119 - $e_file = pht('Invalid'); 120 - } else { 121 - $macro->setFilePHID($file->getPHID()); 122 - $macro->attachFile($file); 123 - $e_file = null; 124 - } 125 - } 126 - 127 - if (!$macro->getID() && !$file) { 128 - $errors[] = pht('You must upload an image to create a macro.'); 129 - $e_file = pht('Required'); 130 - } 131 - 132 - if (!$errors) { 133 - try { 134 - $xactions = array(); 135 - 136 - if ($new_name !== null) { 137 - $xactions[] = id(new PhabricatorMacroTransaction()) 138 - ->setTransactionType( 139 - PhabricatorMacroNameTransaction::TRANSACTIONTYPE) 140 - ->setNewValue($new_name); 141 - } 142 - 143 - if ($file) { 144 - $xactions[] = id(new PhabricatorMacroTransaction()) 145 - ->setTransactionType( 146 - PhabricatorMacroFileTransaction::TRANSACTIONTYPE) 147 - ->setNewValue($file->getPHID()); 148 - } 149 - 150 - $editor = id(new PhabricatorMacroEditor()) 151 - ->setActor($viewer) 152 - ->setContinueOnNoEffect(true) 153 - ->setContentSourceFromRequest($request); 154 - 155 - $xactions = $editor->applyTransactions($original, $xactions); 156 - 157 - $view_uri = $this->getApplicationURI('/view/'.$original->getID().'/'); 158 - return id(new AphrontRedirectResponse())->setURI($view_uri); 159 - } catch (AphrontDuplicateKeyQueryException $ex) { 160 - throw $ex; 161 - $errors[] = pht('Macro name is not unique!'); 162 - $e_name = pht('Duplicate'); 163 - } 164 - } 165 - } 166 - 167 - $current_file = null; 168 - if ($macro->getFilePHID()) { 169 - $current_file = $macro->getFile(); 170 - } 171 - 172 - $form = new AphrontFormView(); 173 - $form->addHiddenInput('name_form', 1); 174 - $form->setUser($request->getUser()); 175 - 176 - $form 177 - ->setEncType('multipart/form-data') 178 - ->appendChild( 179 - id(new AphrontFormTextControl()) 180 - ->setLabel(pht('Name')) 181 - ->setName('name') 182 - ->setValue($macro->getName()) 183 - ->setCaption( 184 - pht('This word or phrase will be replaced with the image.')) 185 - ->setError($e_name)); 186 - 187 - if (!$macro->getID()) { 188 - if ($current_file) { 189 - $current_file_view = id(new PhabricatorFileLinkView()) 190 - ->setViewer($viewer) 191 - ->setFilePHID($current_file->getPHID()) 192 - ->setFileName($current_file->getName()) 193 - ->setFileViewable(true) 194 - ->setFileViewURI($current_file->getBestURI()) 195 - ->render(); 196 - $form->addHiddenInput('phid', $current_file->getPHID()); 197 - $form->appendChild( 198 - id(new AphrontFormMarkupControl()) 199 - ->setLabel(pht('Selected File')) 200 - ->setValue($current_file_view)); 201 - 202 - $other_label = pht('Change File'); 203 - } else { 204 - $other_label = pht('File'); 205 - } 206 - 207 - $form->appendChild( 208 - id(new AphrontFormTextControl()) 209 - ->setLabel(pht('URL')) 210 - ->setName('url') 211 - ->setValue($request->getStr('url')) 212 - ->setError($request->getFileExists('file') ? false : $e_file)); 213 - 214 - $form->appendChild( 215 - id(new AphrontFormFileControl()) 216 - ->setLabel($other_label) 217 - ->setName('file') 218 - ->setError($request->getStr('url') ? false : $e_file)); 219 - } 220 - 221 - 222 - $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/'); 223 - 224 - if ($macro->getID()) { 225 - $cancel_uri = $view_uri; 226 - } else { 227 - $cancel_uri = $this->getApplicationURI(); 228 - } 229 - 230 - $form 231 - ->appendChild( 232 - id(new AphrontFormSubmitControl()) 233 - ->setValue(pht('Save Image Macro')) 234 - ->addCancelButton($cancel_uri)); 235 - 236 - $crumbs = $this->buildApplicationCrumbs(); 237 - 238 - if ($macro->getID()) { 239 - $title = pht('Edit Macro: %s', $macro->getName()); 240 - $crumb = pht('Edit Macro'); 241 - $header_icon = 'fa-pencil'; 242 - 243 - $crumbs->addTextCrumb(pht('Macro: %s', $macro->getName()), $view_uri); 244 - } else { 245 - $title = pht('Create Image Macro'); 246 - $crumb = pht('Create Macro'); 247 - $header_icon = 'fa-plus-square'; 248 - } 249 - 250 - $crumbs->addTextCrumb($crumb, $request->getRequestURI()); 251 - $crumbs->setBorder(true); 252 - 253 - $upload = null; 254 - if ($macro->getID()) { 255 - $upload_form = id(new AphrontFormView()) 256 - ->setEncType('multipart/form-data') 257 - ->setUser($request->getUser()); 258 - 259 - $upload_form->appendChild( 260 - id(new AphrontFormTextControl()) 261 - ->setLabel(pht('URL')) 262 - ->setName('url') 263 - ->setValue($request->getStr('url'))); 264 - 265 - $upload_form 266 - ->appendChild( 267 - id(new AphrontFormFileControl()) 268 - ->setLabel(pht('File')) 269 - ->setName('file')) 270 - ->appendChild( 271 - id(new AphrontFormSubmitControl()) 272 - ->setValue(pht('Upload File'))); 273 - 274 - $upload = id(new PHUIObjectBoxView()) 275 - ->setHeaderText(pht('Upload New File')) 276 - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 277 - ->setForm($upload_form); 278 - } 279 - 280 - $form_box = id(new PHUIObjectBoxView()) 281 - ->setHeaderText(pht('Macro')) 282 - ->setFormErrors($errors) 283 - ->setBackground(PHUIObjectBoxView::BLUE_PROPERTY) 284 - ->setForm($form); 285 - 286 - $header = id(new PHUIHeaderView()) 287 - ->setHeader($title) 288 - ->setHeaderIcon($header_icon); 289 - 290 - $view = id(new PHUITwoColumnView()) 291 - ->setHeader($header) 292 - ->setFooter(array( 293 - $form_box, 294 - $upload, 295 - )); 296 - 297 - return $this->newPage() 298 - ->setTitle($title) 299 - ->setCrumbs($crumbs) 300 - ->appendChild($view); 301 - 6 + return id(new PhabricatorMacroEditEngine()) 7 + ->setController($this) 8 + ->buildResponse(); 302 9 } 303 - 304 10 }
+88
src/applications/macro/editor/PhabricatorMacroEditEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroEditEngine 4 + extends PhabricatorEditEngine { 5 + 6 + const ENGINECONST = 'macro.image'; 7 + 8 + public function getEngineName() { 9 + return pht('Macro Imagea'); 10 + } 11 + 12 + public function getSummaryHeader() { 13 + return pht('Configure Macro Image Forms'); 14 + } 15 + 16 + public function getSummaryText() { 17 + return pht('Configure creation and editing of Macro images.'); 18 + } 19 + 20 + public function getEngineApplicationClass() { 21 + return 'PhabricatorMacroApplication'; 22 + } 23 + 24 + protected function newEditableObject() { 25 + $viewer = $this->getViewer(); 26 + return PhabricatorFileImageMacro::initializeNewFileImageMacro($viewer); 27 + } 28 + 29 + protected function newObjectQuery() { 30 + return new PhabricatorMacroQuery(); 31 + } 32 + 33 + protected function getObjectCreateTitleText($object) { 34 + return pht('Create New Macro'); 35 + } 36 + 37 + protected function getObjectEditTitleText($object) { 38 + return pht('Edit %s', $object->getName()); 39 + } 40 + 41 + protected function getObjectEditShortText($object) { 42 + return $object->getName(); 43 + } 44 + 45 + protected function getObjectCreateShortText() { 46 + return pht('Create Macro'); 47 + } 48 + 49 + protected function getObjectName() { 50 + return pht('Macro'); 51 + } 52 + 53 + protected function getObjectViewURI($object) { 54 + return $object->getViewURI(); 55 + } 56 + 57 + protected function getEditorURI() { 58 + return $this->getApplication()->getApplicationURI('edit/'); 59 + } 60 + 61 + protected function getCreateNewObjectPolicy() { 62 + return $this->getApplication()->getPolicy( 63 + PhabricatorMacroManageCapability::CAPABILITY); 64 + } 65 + 66 + protected function buildCustomEditFields($object) { 67 + 68 + return array( 69 + id(new PhabricatorTextEditField()) 70 + ->setKey('name') 71 + ->setLabel(pht('Name')) 72 + ->setDescription(pht('Macro name.')) 73 + ->setConduitDescription(pht('Rename the macro.')) 74 + ->setConduitTypeDescription(pht('New macro name.')) 75 + ->setTransactionType(PhabricatorMacroNameTransaction::TRANSACTIONTYPE) 76 + ->setValue($object->getName()), 77 + id(new PhabricatorFileEditField()) 78 + ->setKey('filePHID') 79 + ->setLabel(pht('Image File')) 80 + ->setDescription(pht('Image file to import.')) 81 + ->setTransactionType(PhabricatorMacroFileTransaction::TRANSACTIONTYPE) 82 + ->setConduitDescription(pht('File PHID to import.')) 83 + ->setConduitTypeDescription(pht('File PHID.')), 84 + ); 85 + 86 + } 87 + 88 + }
+5 -4
src/applications/macro/editor/PhabricatorMacroEditor.php
··· 11 11 return pht('Macros'); 12 12 } 13 13 14 - public function getTransactionTypes() { 15 - $types = parent::getTransactionTypes(); 16 - $types[] = PhabricatorTransactions::TYPE_COMMENT; 14 + public function getCreateObjectTitle($author, $object) { 15 + return pht('%s created this macro.', $author); 16 + } 17 17 18 - return $types; 18 + public function getCreateObjectTitleForFeed($author, $object) { 19 + return pht('%s created %s.', $author, $object); 19 20 } 20 21 21 22 protected function applyCustomExternalTransaction(
+19 -1
src/applications/macro/storage/PhabricatorFileImageMacro.php
··· 41 41 return $this->assertAttached($this->audio); 42 42 } 43 43 44 + public static function initializeNewFileImageMacro(PhabricatorUser $actor) { 45 + $macro = id(new self()) 46 + ->setAuthorPHID($actor->getPHID()); 47 + return $macro; 48 + } 49 + 44 50 protected function getConfiguration() { 45 51 return array( 46 52 self::CONFIG_AUX_PHID => true, ··· 78 84 $this->setMailKey(Filesystem::readRandomCharacters(20)); 79 85 } 80 86 return parent::save(); 87 + } 88 + 89 + public function getViewURI() { 90 + return '/macro/view/'.$this->getID().'/'; 81 91 } 82 92 83 93 ··· 128 138 public function getCapabilities() { 129 139 return array( 130 140 PhabricatorPolicyCapability::CAN_VIEW, 141 + PhabricatorPolicyCapability::CAN_EDIT, 131 142 ); 132 143 } 133 144 134 145 public function getPolicy($capability) { 135 - return PhabricatorPolicies::getMostOpenPolicy(); 146 + switch ($capability) { 147 + case PhabricatorPolicyCapability::CAN_VIEW: 148 + return PhabricatorPolicies::getMostOpenPolicy(); 149 + case PhabricatorPolicyCapability::CAN_EDIT: 150 + $app = PhabricatorApplication::getByClass( 151 + 'PhabricatorMacroApplication'); 152 + return $app->getPolicy(PhabricatorMacroManageCapability::CAPABILITY); 153 + } 136 154 } 137 155 138 156 public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+35
src/applications/macro/xaction/PhabricatorMacroFileTransaction.php
··· 26 26 $this->renderObject()); 27 27 } 28 28 29 + public function validateTransactions($object, array $xactions) { 30 + $errors = array(); 31 + $viewer = $this->getActor(); 32 + 33 + foreach ($xactions as $xaction) { 34 + $file_phid = $xaction->getNewValue(); 35 + 36 + if ($this->isEmptyTextTransaction($file_phid, $xactions)) { 37 + $errors[] = $this->newRequiredError( 38 + pht('Image macros must have a file.')); 39 + } 40 + 41 + $file = id(new PhabricatorFileQuery()) 42 + ->setViewer($viewer) 43 + ->withPHIDs(array($file_phid)) 44 + ->executeOne(); 45 + 46 + if (!$file) { 47 + $errors[] = $this->newInvalidError( 48 + pht('"%s" is not a valid file PHID.', 49 + $file_phid)); 50 + } else { 51 + if (!$file->isViewableInBrowser()) { 52 + $mime_type = $file->getMimeType(); 53 + $errors[] = $this->newInvalidError( 54 + pht('File mime type of "%s" is not a valid viewable image.', 55 + $mime_type)); 56 + } 57 + } 58 + 59 + } 60 + 61 + return $errors; 62 + } 63 + 29 64 public function getIcon() { 30 65 return 'fa-file-image-o'; 31 66 }
+25
src/applications/macro/xaction/PhabricatorMacroNameTransaction.php
··· 32 32 33 33 public function validateTransactions($object, array $xactions) { 34 34 $errors = array(); 35 + $viewer = $this->getActor(); 35 36 36 37 if ($this->isEmptyTextTransaction($object->getName(), $xactions)) { 37 38 $errors[] = $this->newRequiredError( ··· 40 41 41 42 $max_length = $object->getColumnMaximumByteLength('name'); 42 43 foreach ($xactions as $xaction) { 44 + $old_value = $this->generateOldValue($object); 43 45 $new_value = $xaction->getNewValue(); 46 + 44 47 $new_length = strlen($new_value); 45 48 if ($new_length > $max_length) { 46 49 $errors[] = $this->newInvalidError( 47 50 pht('The name can be no longer than %s characters.', 48 51 new PhutilNumber($max_length))); 49 52 } 53 + 54 + if (!preg_match('/^[a-z0-9:_-]{3,}\z/', $new_value)) { 55 + $errors[] = $this->newInvalidError( 56 + pht('Macro name "%s" be at least three characters long and contain '. 57 + 'only lowercase letters, digits, hyphens, colons and '. 58 + 'underscores.', 59 + $new_value)); 60 + } 61 + 62 + // Check name is unique when updating / creating 63 + if ($old_value != $new_value) { 64 + $macro = id(new PhabricatorMacroQuery()) 65 + ->setViewer($viewer) 66 + ->withNames(array($new_value)) 67 + ->executeOne(); 68 + 69 + if ($macro) { 70 + $errors[] = $this->newInvalidError( 71 + pht('Macro "%s" already exists.', $new_value)); 72 + } 73 + } 74 + 50 75 } 51 76 52 77 return $errors;