@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Modernize Macro application

Summary: Adds feed, email, notifications, comments, partial editing, subscriptions, enable/disable, flags and crumbs to Macro.

Test Plan:
{F26839}
{F26840}
{F26841}
{F26842}
{F26843}
{F26844}
{F26845}

Reviewers: vrana, btrahan, chad

Reviewed By: vrana

CC: aran

Maniphest Tasks: T2157, T175, T2104

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

+1231 -115
+8 -1
conf/default.conf.php
··· 383 383 // distinguish between testing and development installs, for example. 384 384 'metamta.maniphest.subject-prefix' => '[Maniphest]', 385 385 386 - // See 'metamta.pholio.reply-handler-domain'. This does the same thing, but 386 + // See 'metamta.maniphest.reply-handler-domain'. This does the same thing, but 387 387 // affects Pholio. 388 388 'metamta.pholio.reply-handler-domain' => null, 389 389 390 390 // Prefix prepended to mail sent by Pholio. 391 391 'metamta.pholio.subject-prefix' => '[Pholio]', 392 + 393 + // See 'metamta.maniphest.reply-handler-domain'. This does the same thing, but 394 + // affects Macro. 395 + 'metamta.macro.reply-handler-domain' => null, 396 + 397 + // Prefix prepended to mail sent by Macro. 398 + 'metamta.macro.subject-prefix' => '[Macro]', 392 399 393 400 // See 'metamta.maniphest.reply-handler-domain'. This does the same thing, 394 401 // but allows email replies via Differential.
+54
resources/sql/patches/20121209.xmacroadd.sql
··· 1 + CREATE TABLE {$NAMESPACE}_file.macro_transaction ( 2 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 3 + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, 4 + authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 5 + objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 6 + viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 7 + editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 8 + commentPHID VARCHAR(64) COLLATE utf8_bin, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) NOT NULL COLLATE utf8_bin, 11 + oldValue LONGTEXT NOT NULL COLLATE utf8_bin, 12 + newValue LONGTEXT NOT NULL COLLATE utf8_bin, 13 + contentSource LONGTEXT NOT NULL COLLATE utf8_bin, 14 + dateCreated INT UNSIGNED NOT NULL, 15 + dateModified INT UNSIGNED NOT NULL, 16 + 17 + UNIQUE KEY `key_phid` (phid), 18 + KEY `key_object` (objectPHID) 19 + 20 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 21 + 22 + CREATE TABLE {$NAMESPACE}_file.macro_transaction_comment ( 23 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 24 + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, 25 + transactionPHID VARCHAR(64) COLLATE utf8_bin, 26 + authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 27 + viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 28 + editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 29 + commentVersion INT UNSIGNED NOT NULL, 30 + content LONGTEXT NOT NULL COLLATE utf8_bin, 31 + contentSource LONGTEXT NOT NULL COLLATE utf8_bin, 32 + isDeleted BOOL NOT NULL, 33 + dateCreated INT UNSIGNED NOT NULL, 34 + dateModified INT UNSIGNED NOT NULL, 35 + 36 + UNIQUE KEY `key_phid` (phid), 37 + UNIQUE KEY `key_version` (transactionPHID, commentVersion) 38 + 39 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 40 + 41 + ALTER TABLE {$NAMESPACE}_file.file_imagemacro 42 + ADD dateCreated INT UNSIGNED NOT NULL; 43 + 44 + ALTER TABLE {$NAMESPACE}_file.file_imagemacro 45 + ADD dateModified INT UNSIGNED NOT NULL; 46 + 47 + ALTER TABLE {$NAMESPACE}_file.file_imagemacro 48 + ADD phid VARCHAR(64) NOT NULL COLLATE utf8_bin AFTER id; 49 + 50 + ALTER TABLE {$NAMESPACE}_file.file_imagemacro 51 + ADD isDisabled BOOL NOT NULL; 52 + 53 + ALTER TABLE {$NAMESPACE}_file.file_imagemacro 54 + ADD KEY `key_disabled` (isDisabled);
+18
resources/sql/patches/20121209.xmacromigrate.php
··· 1 + <?php 2 + 3 + echo "Giving image macros PHIDs"; 4 + foreach (new LiskMigrationIterator(new PhabricatorFileImageMacro()) as $macro) { 5 + if ($macro->getPHID()) { 6 + continue; 7 + } 8 + 9 + echo "."; 10 + 11 + queryfx( 12 + $macro->establishConnection('r'), 13 + 'UPDATE %T SET phid = %s WHERE id = %d', 14 + $macro->getTableName(), 15 + $macro->generatePHID(), 16 + $macro->getID()); 17 + } 18 + echo "\nDone.\n";
+2
resources/sql/patches/20121209.xmacromigratekey.sql
··· 1 + ALTER TABLE {$NAMESPACE}_file.file_imagemacro 2 + ADD UNIQUE KEY `key_phid` (phid);
+24 -3
src/__phutil_library_map__.php
··· 604 604 'PhabricatorApplicationTransactionEditor' => 'applications/transactions/editor/PhabricatorApplicationTransactionEditor.php', 605 605 'PhabricatorApplicationTransactionFeedStory' => 'applications/transactions/feed/PhabricatorApplicationTransactionFeedStory.php', 606 606 'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php', 607 + 'PhabricatorApplicationTransactionView' => 'applications/transactions/view/PhabricatorApplicationTransactionView.php', 607 608 'PhabricatorApplicationTransactions' => 'applications/transactions/application/PhabricatorApplicationTransactions.php', 608 609 'PhabricatorApplicationUIExamples' => 'applications/uiexample/application/PhabricatorApplicationUIExamples.php', 609 610 'PhabricatorApplicationsListController' => 'applications/meta/controller/PhabricatorApplicationsListController.php', ··· 829 830 'PhabricatorLoginController' => 'applications/auth/controller/PhabricatorLoginController.php', 830 831 'PhabricatorLoginValidateController' => 'applications/auth/controller/PhabricatorLoginValidateController.php', 831 832 'PhabricatorLogoutController' => 'applications/auth/controller/PhabricatorLogoutController.php', 833 + 'PhabricatorMacroCommentController' => 'applications/macro/controller/PhabricatorMacroCommentController.php', 832 834 'PhabricatorMacroController' => 'applications/macro/controller/PhabricatorMacroController.php', 833 - 'PhabricatorMacroDeleteController' => 'applications/macro/controller/PhabricatorMacroDeleteController.php', 835 + 'PhabricatorMacroDisableController' => 'applications/macro/controller/PhabricatorMacroDisableController.php', 834 836 'PhabricatorMacroEditController' => 'applications/macro/controller/PhabricatorMacroEditController.php', 837 + 'PhabricatorMacroEditor' => 'applications/macro/editor/PhabricatorMacroEditor.php', 835 838 'PhabricatorMacroListController' => 'applications/macro/controller/PhabricatorMacroListController.php', 839 + 'PhabricatorMacroReplyHandler' => 'applications/macro/mail/PhabricatorMacroReplyHandler.php', 840 + 'PhabricatorMacroTransaction' => 'applications/macro/storage/PhabricatorMacroTransaction.php', 841 + 'PhabricatorMacroTransactionComment' => 'applications/macro/storage/PhabricatorMacroTransactionComment.php', 842 + 'PhabricatorMacroTransactionQuery' => 'applications/macro/query/PhabricatorMacroTransactionQuery.php', 843 + 'PhabricatorMacroTransactionType' => 'applications/macro/constants/PhabricatorMacroTransactionType.php', 844 + 'PhabricatorMacroViewController' => 'applications/macro/controller/PhabricatorMacroViewController.php', 836 845 'PhabricatorMailImplementationAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAdapter.php', 837 846 'PhabricatorMailImplementationAmazonSESAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationAmazonSESAdapter.php', 838 847 'PhabricatorMailImplementationPHPMailerAdapter' => 'applications/metamta/adapter/PhabricatorMailImplementationPHPMailerAdapter.php', ··· 1869 1878 'PhabricatorApplicationTransactionEditor' => 'PhabricatorEditor', 1870 1879 'PhabricatorApplicationTransactionFeedStory' => 'PhabricatorFeedStory', 1871 1880 'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 1881 + 'PhabricatorApplicationTransactionView' => 'AphrontView', 1872 1882 'PhabricatorApplicationTransactions' => 'PhabricatorApplication', 1873 1883 'PhabricatorApplicationUIExamples' => 'PhabricatorApplication', 1874 1884 'PhabricatorApplicationsListController' => 'PhabricatorController', ··· 2024 2034 'PhabricatorFileDataController' => 'PhabricatorFileController', 2025 2035 'PhabricatorFileDeleteController' => 'PhabricatorFileController', 2026 2036 'PhabricatorFileDropUploadController' => 'PhabricatorFileController', 2027 - 'PhabricatorFileImageMacro' => 'PhabricatorFileDAO', 2037 + 'PhabricatorFileImageMacro' => 2038 + array( 2039 + 0 => 'PhabricatorFileDAO', 2040 + 1 => 'PhabricatorSubscribableInterface', 2041 + ), 2028 2042 'PhabricatorFileInfoController' => 'PhabricatorFileController', 2029 2043 'PhabricatorFileLinkListView' => 'AphrontView', 2030 2044 'PhabricatorFileLinkView' => 'AphrontView', ··· 2080 2094 'PhabricatorLoginController' => 'PhabricatorAuthController', 2081 2095 'PhabricatorLoginValidateController' => 'PhabricatorAuthController', 2082 2096 'PhabricatorLogoutController' => 'PhabricatorAuthController', 2097 + 'PhabricatorMacroCommentController' => 'PhabricatorMacroController', 2083 2098 'PhabricatorMacroController' => 'PhabricatorController', 2084 - 'PhabricatorMacroDeleteController' => 'PhabricatorMacroController', 2099 + 'PhabricatorMacroDisableController' => 'PhabricatorMacroController', 2085 2100 'PhabricatorMacroEditController' => 'PhabricatorMacroController', 2101 + 'PhabricatorMacroEditor' => 'PhabricatorApplicationTransactionEditor', 2086 2102 'PhabricatorMacroListController' => 'PhabricatorMacroController', 2103 + 'PhabricatorMacroReplyHandler' => 'PhabricatorMailReplyHandler', 2104 + 'PhabricatorMacroTransaction' => 'PhabricatorApplicationTransaction', 2105 + 'PhabricatorMacroTransactionComment' => 'PhabricatorApplicationTransactionComment', 2106 + 'PhabricatorMacroTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 2107 + 'PhabricatorMacroViewController' => 'PhabricatorMacroController', 2087 2108 'PhabricatorMailImplementationAmazonSESAdapter' => 'PhabricatorMailImplementationPHPMailerLiteAdapter', 2088 2109 'PhabricatorMailImplementationPHPMailerAdapter' => 'PhabricatorMailImplementationAdapter', 2089 2110 'PhabricatorMailImplementationPHPMailerLiteAdapter' => 'PhabricatorMailImplementationAdapter',
+5
src/aphront/AphrontRequest.php
··· 161 161 return array_key_exists($name, $this->requestData); 162 162 } 163 163 164 + final public function getFileExists($name) { 165 + return isset($_FILES[$name]) && 166 + (idx($_FILES[$name], 'error') !== UPLOAD_ERR_NO_FILE); 167 + } 168 + 164 169 final public function isHTTPPost() { 165 170 return ($_SERVER['REQUEST_METHOD'] == 'POST'); 166 171 }
+5 -2
src/applications/macro/application/PhabricatorApplicationMacro.php
··· 26 26 return array( 27 27 '/macro/' => array( 28 28 '' => 'PhabricatorMacroListController', 29 - 'edit/(?:(?P<id>[1-9]\d*)/)?' => 'PhabricatorMacroEditController', 30 - 'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroDeleteController', 29 + 'create/' => 'PhabricatorMacroEditController', 30 + 'view/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroViewController', 31 + 'comment/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroCommentController', 32 + 'edit/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroEditController', 33 + 'disable/(?P<id>[1-9]\d*)/' => 'PhabricatorMacroDisableController', 31 34 ), 32 35 ); 33 36 }
+9
src/applications/macro/constants/PhabricatorMacroTransactionType.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroTransactionType { 4 + 5 + const TYPE_NAME = 'macro:name'; 6 + const TYPE_DISABLED = 'macro:disabled'; 7 + const TYPE_FILE = 'macro:file'; 8 + 9 + }
+49
src/applications/macro/controller/PhabricatorMacroCommentController.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroCommentController 4 + extends PhabricatorMacroController { 5 + 6 + private $id; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->id = idx($data, 'id'); 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $user = $request->getUser(); 15 + 16 + if (!$request->isFormPost()) { 17 + return new Aphront400Response(); 18 + } 19 + 20 + $macro = id(new PhabricatorFileImageMacro())->load($this->id); 21 + if (!$macro) { 22 + return new Aphront404Response(); 23 + } 24 + 25 + $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/'); 26 + 27 + $xactions = array(); 28 + 29 + $xactions[] = id(new PhabricatorMacroTransaction()) 30 + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 31 + ->attachComment( 32 + id(new PhabricatorMacroTransactionComment()) 33 + ->setContent($request->getStr('comment'))); 34 + 35 + $editor = id(new PhabricatorMacroEditor()) 36 + ->setActor($user) 37 + ->setContentSource( 38 + PhabricatorContentSource::newForSource( 39 + PhabricatorContentSource::SOURCE_WEB, 40 + array( 41 + 'ip' => $request->getRemoteAddr(), 42 + ))) 43 + ->applyTransactions($macro, $xactions); 44 + 45 + return id(new AphrontRedirectResponse()) 46 + ->setURI($view_uri); 47 + } 48 + 49 + }
+26 -6
src/applications/macro/controller/PhabricatorMacroController.php
··· 3 3 abstract class PhabricatorMacroController 4 4 extends PhabricatorController { 5 5 6 - protected function buildSideNavView(PhabricatorFileImageMacro $macro = null) { 6 + protected function buildSideNavView($for_app = false, $has_search = false) { 7 7 $nav = new AphrontSideNavFilterView(); 8 8 $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 9 9 10 - $nav->addLabel('Create'); 11 - $nav->addFilter('edit', 'Create Macro'); 12 - 13 - $nav->addSpacer(); 10 + if ($for_app) { 11 + $nav->addLabel('Create'); 12 + $nav->addFilter('', 'Create Macro', $this->getApplicationURI('/create/')); 13 + } 14 14 15 15 $nav->addLabel('Macros'); 16 - $nav->addFilter('', 'All Macros'); 16 + $nav->addFilter('/', 'All Macros'); 17 + if ($has_search) { 18 + $nav->addFilter('search', 'Search', $this->getRequest()->getRequestURI()); 19 + } 20 + 17 21 18 22 return $nav; 23 + } 24 + 25 + public function buildApplicationMenu() { 26 + return $this->buildSideNavView($for_app = true)->getMenu(); 27 + } 28 + 29 + protected function buildApplicationCrumbs() { 30 + $crumbs = parent::buildApplicationCrumbs(); 31 + 32 + $crumbs->addAction( 33 + id(new PhabricatorMenuItemView()) 34 + ->setName(pht('Create Macro')) 35 + ->setHref($this->getApplicationURI('/create/')) 36 + ->setIcon('create')); 37 + 38 + return $crumbs; 19 39 } 20 40 21 41 }
-42
src/applications/macro/controller/PhabricatorMacroDeleteController.php
··· 1 - <?php 2 - 3 - final class PhabricatorMacroDeleteController 4 - extends PhabricatorMacroController { 5 - 6 - private $id; 7 - 8 - public function willProcessRequest(array $data) { 9 - $this->id = $data['id']; 10 - } 11 - 12 - public function processRequest() { 13 - 14 - $macro = id(new PhabricatorFileImageMacro())->load($this->id); 15 - if (!$macro) { 16 - return new Aphront404Response(); 17 - } 18 - 19 - $request = $this->getRequest(); 20 - 21 - if ($request->isDialogFormPost()) { 22 - $macro->delete(); 23 - return id(new AphrontRedirectResponse())->setURI( 24 - $this->getApplicationURI()); 25 - } 26 - 27 - $dialog = new AphrontDialogView(); 28 - $dialog 29 - ->setUser($request->getUser()) 30 - ->setTitle('Really delete macro?') 31 - ->appendChild( 32 - '<p>Really delete the much-beloved image macro "'. 33 - phutil_escape_html($macro->getName()).'"? It will be sorely missed.'. 34 - '</p>') 35 - ->setSubmitURI($this->getApplicationURI('/delete/'.$this->id.'/')) 36 - ->addSubmitButton('Delete') 37 - ->addCancelButton($this->getApplicationURI()); 38 - 39 - 40 - return id(new AphrontDialogResponse())->setDialog($dialog); 41 - } 42 - }
+55
src/applications/macro/controller/PhabricatorMacroDisableController.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroDisableController 4 + extends PhabricatorMacroController { 5 + 6 + private $id; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->id = $data['id']; 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $user = $request->getUser(); 15 + 16 + $macro = id(new PhabricatorFileImageMacro())->load($this->id); 17 + if (!$macro) { 18 + return new Aphront404Response(); 19 + } 20 + 21 + $view_uri = $this->getApplicationURI('/view/'.$this->id.'/'); 22 + 23 + if ($request->isDialogFormPost() || $macro->getIsDisabled()) { 24 + $xaction = id(new PhabricatorMacroTransaction()) 25 + ->setTransactionType(PhabricatorMacroTransactionType::TYPE_DISABLED) 26 + ->setNewValue($macro->getIsDisabled() ? 0 : 1); 27 + 28 + $editor = id(new PhabricatorMacroEditor()) 29 + ->setActor($user) 30 + ->setContentSource( 31 + PhabricatorContentSource::newForSource( 32 + PhabricatorContentSource::SOURCE_WEB, 33 + array( 34 + 'ip' => $request->getRemoteAddr(), 35 + ))) 36 + ->applyTransactions($macro, array($xaction)); 37 + 38 + return id(new AphrontRedirectResponse())->setURI($view_uri); 39 + } 40 + 41 + $dialog = new AphrontDialogView(); 42 + $dialog 43 + ->setUser($request->getUser()) 44 + ->setTitle('Really disable macro?') 45 + ->appendChild( 46 + '<p>Really disable the much-beloved image macro "'. 47 + phutil_escape_html($macro->getName()).'"? It will be sorely missed.'. 48 + '</p>') 49 + ->setSubmitURI($this->getApplicationURI('/disable/'.$this->id.'/')) 50 + ->addSubmitButton('Disable') 51 + ->addCancelButton($view_uri); 52 + 53 + return id(new AphrontDialogResponse())->setDialog($dialog); 54 + } 55 + }
+163 -47
src/applications/macro/controller/PhabricatorMacroEditController.php
··· 22 22 23 23 $errors = array(); 24 24 $e_name = true; 25 + $e_file = true; 25 26 $file = null; 26 27 27 28 $request = $this->getRequest(); 28 29 $user = $request->getUser(); 29 30 if ($request->isFormPost()) { 31 + $original = clone $macro; 30 32 31 - $macro->setName($request->getStr('name')); 33 + $new_name = null; 34 + if ($request->getBool('name_form') || !$macro->getID()) { 35 + $new_name = $request->getStr('name'); 32 36 33 - if (!strlen($macro->getName())) { 34 - $errors[] = 'Macro name is required.'; 35 - $e_name = 'Required'; 36 - } else if (!preg_match('/^[a-z0-9_-]{3,}$/', $macro->getName())) { 37 - $errors[] = 'Macro must be at least three characters long and contain '. 38 - 'only lowercase letters, digits, hyphen and underscore.'; 39 - $e_name = 'Invalid'; 40 - } else { 41 - $e_name = null; 37 + $macro->setName($new_name); 38 + 39 + if (!strlen($macro->getName())) { 40 + $errors[] = 'Macro name is required.'; 41 + $e_name = 'Required'; 42 + } else if (!preg_match('/^[a-z0-9_-]{3,}$/', $macro->getName())) { 43 + $errors[] = 'Macro must be at least three characters long and '. 44 + 'contain only lowercase letters, digits, hyphen and '. 45 + 'underscore.'; 46 + $e_name = 'Invalid'; 47 + } else { 48 + $e_name = null; 49 + } 42 50 } 43 51 44 - if (!$errors) { 45 - 52 + $file = null; 53 + if ($request->getFileExists('file')) { 46 54 $file = PhabricatorFile::newFromPHPUpload( 47 - idx($_FILES, 'file'), 55 + $_FILES['file'], 48 56 array( 49 57 'name' => $request->getStr('name'), 50 58 'authorPHID' => $user->getPHID(), 51 59 )); 52 - $macro->setFilePHID($file->getPHID()); 60 + } else if ($request->getStr('phid')) { 61 + $file = id(new PhabricatorFile())->loadOneWhere( 62 + 'phid = %s', 63 + $request->getStr('phid')); 64 + } 65 + 66 + if ($file) { 67 + if (!$file->isViewableInBrowser()) { 68 + $errors[] = pht('You must upload an image.'); 69 + $e_file = pht('Invalid'); 70 + } else { 71 + $macro->setFilePHID($file->getPHID()); 72 + $e_file = null; 73 + } 74 + } 75 + 76 + if (!$macro->getID() && !$file) { 77 + $errors[] = 'You must upload an image to create a macro.'; 78 + $e_file = pht('Required'); 79 + } 53 80 81 + if (!$errors) { 54 82 try { 55 - $macro->save(); 56 - return id(new AphrontRedirectResponse())->setURI( 57 - $this->getApplicationURI()); 83 + $xactions = array(); 84 + 85 + if ($new_name !== null) { 86 + $xactions[] = id(new PhabricatorMacroTransaction()) 87 + ->setTransactionType(PhabricatorMacroTransactionType::TYPE_NAME) 88 + ->setNewValue($new_name); 89 + } 90 + 91 + if ($file) { 92 + $xactions[] = id(new PhabricatorMacroTransaction()) 93 + ->setTransactionType(PhabricatorMacroTransactionType::TYPE_FILE) 94 + ->setNewValue($file->getPHID()); 95 + } 96 + 97 + $editor = id(new PhabricatorMacroEditor()) 98 + ->setActor($user) 99 + ->setContentSource( 100 + PhabricatorContentSource::newForSource( 101 + PhabricatorContentSource::SOURCE_WEB, 102 + array( 103 + 'ip' => $request->getRemoteAddr(), 104 + ))) 105 + ->applyTransactions($original, $xactions); 106 + 107 + $view_uri = $this->getApplicationURI('/view/'.$original->getID().'/'); 108 + return id(new AphrontRedirectResponse())->setURI($view_uri); 58 109 } catch (AphrontQueryDuplicateKeyException $ex) { 110 + throw $ex; 59 111 $errors[] = 'Macro name is not unique!'; 60 112 $e_name = 'Duplicate'; 61 113 } 62 114 } 63 - } else if ($this->id) { 64 - $file = id(new PhabricatorFile()) 65 - ->loadOneWhere('phid = %s', $macro->getFilePHID()); 66 - } 67 - 68 - $caption = null; 69 - if ($file) { 70 - $caption = phutil_render_tag( 71 - 'img', 72 - array( 73 - 'src' => $file->getViewURI(), 74 - )); 75 115 } 76 116 77 117 if ($errors) { ··· 82 122 $error_view = null; 83 123 } 84 124 125 + 126 + $current_file = null; 127 + if ($macro->getFilePHID()) { 128 + $current_file = id(new PhabricatorFile())->loadOneWhere( 129 + 'phid = %s', 130 + $macro->getFilePHID()); 131 + } 132 + 85 133 $form = new AphrontFormView(); 134 + $form->setFlexible(true); 135 + $form->addHiddenInput('name_form', 1); 86 136 $form->setUser($request->getUser()); 87 137 88 138 $form ··· 93 143 ->setName('name') 94 144 ->setValue($macro->getName()) 95 145 ->setCaption('This word or phrase will be replaced with the image.') 96 - ->setError($e_name)) 97 - ->appendChild( 146 + ->setError($e_name)); 147 + 148 + if (!$macro->getID()) { 149 + if ($current_file) { 150 + $current_file_view = id(new PhabricatorFileLinkView()) 151 + ->setFilePHID($current_file->getPHID()) 152 + ->setFileName($current_file->getName()) 153 + ->setFileViewable(true) 154 + ->setFileViewURI($current_file->getBestURI()) 155 + ->render(); 156 + $form->addHiddenInput('phid', $current_file->getPHID()); 157 + $form->appendChild( 158 + id(new AphrontFormMarkupControl()) 159 + ->setLabel('Selected File') 160 + ->setValue($current_file_view)); 161 + 162 + $other_label = pht('Change File'); 163 + } else { 164 + $other_label = pht('File'); 165 + } 166 + 167 + $form->appendChild( 98 168 id(new AphrontFormFileControl()) 99 - ->setLabel('File') 169 + ->setLabel($other_label) 100 170 ->setName('file') 101 - ->setCaption($caption) 102 - ->setError(true)) 171 + ->setError($e_file)); 172 + } 173 + 174 + 175 + $view_uri = $this->getApplicationURI('/view/'.$macro->getID().'/'); 176 + 177 + if ($macro->getID()) { 178 + $cancel_uri = $view_uri; 179 + } else { 180 + $cancel_uri = $this->getApplicationURI(); 181 + } 182 + 183 + $form 103 184 ->appendChild( 104 185 id(new AphrontFormSubmitControl()) 105 - ->setValue('Save Image Macro') 106 - ->addCancelButton($this->getApplicationURI())); 186 + ->setValue(pht('Save Image Macro')) 187 + ->addCancelButton($cancel_uri)); 188 + 189 + $crumbs = $this->buildApplicationCrumbs(); 107 190 108 - $panel = new AphrontPanelView(); 109 191 if ($macro->getID()) { 110 - $title = 'Edit Image Macro'; 192 + $title = pht('Edit Image Macro'); 193 + $crumb = pht('Edit'); 194 + 195 + $crumbs->addCrumb( 196 + id(new PhabricatorCrumbView()) 197 + ->setHref($view_uri) 198 + ->setName(pht('Macro "%s"', $macro->getName()))); 111 199 } else { 112 - $title = 'Create Image Macro'; 200 + $title = pht('Create Image Macro'); 201 + $crumb = pht('Create'); 113 202 } 114 - $panel->setHeader($title); 115 - $panel->appendChild($form); 116 - $panel->setWidth(AphrontPanelView::WIDTH_FULL); 117 203 118 - $nav = $this->buildSideNavView($macro); 119 - $nav->selectFilter('#', 'edit'); 120 - $nav->appendChild($error_view); 121 - $nav->appendChild($panel); 204 + $crumbs->addCrumb( 205 + id(new PhabricatorCrumbView()) 206 + ->setHref($request->getRequestURI()) 207 + ->setName($crumb)); 208 + 209 + $header = id(new PhabricatorHeaderView()) 210 + ->setHeader($title); 211 + 212 + 213 + $upload = null; 214 + if ($macro->getID()) { 215 + $upload_header = id(new PhabricatorHeaderView()) 216 + ->setHeader(pht('Upload New File')); 217 + 218 + $upload_form = id(new AphrontFormView()) 219 + ->setFlexible(true) 220 + ->setEncType('multipart/form-data') 221 + ->setUser($request->getUser()) 222 + ->appendChild( 223 + id(new AphrontFormFileControl()) 224 + ->setLabel('File') 225 + ->setName('file')) 226 + ->appendChild( 227 + id(new AphrontFormSubmitControl()) 228 + ->setValue('Upload File')); 229 + 230 + $upload = array($upload_header, $upload_form); 231 + } 122 232 123 233 return $this->buildApplicationPage( 124 - $nav, 234 + array( 235 + $crumbs, 236 + $header, 237 + $error_view, 238 + $form, 239 + $upload, 240 + ), 125 241 array( 126 242 'title' => $title, 127 243 ));
+17 -5
src/applications/macro/controller/PhabricatorMacroListController.php
··· 70 70 $filter_view = new AphrontListFilterView(); 71 71 $filter_view->appendChild($filter_form); 72 72 73 - $nav = $this->buildSideNavView(); 74 - $nav->selectFilter('/'); 73 + $has_search = strlen($filter); 74 + $nav = $this->buildSideNavView( 75 + $for_app = false, 76 + $has_search); 77 + $nav->selectFilter($has_search ? 'search' : '/'); 75 78 76 79 $nav->appendChild($filter_view); 77 80 ··· 97 100 array(), 98 101 'Created on '.$datetime)); 99 102 } 100 - $item->setURI($this->getApplicationURI('/edit/'.$macro->getID().'/')); 103 + $item->setURI($this->getApplicationURI('/view/'.$macro->getID().'/')); 101 104 $item->setHeader($macro->getName()); 102 105 103 106 $pinboard->addItem($item); ··· 109 112 $nav->appendChild($list); 110 113 } 111 114 112 - 113 - if ($filter === null) { 115 + if (!strlen($filter)) { 114 116 $nav->appendChild($pager); 117 + $name = pht('All Macros'); 118 + } else { 119 + $name = pht('Search'); 115 120 } 121 + 122 + $crumbs = $this->buildApplicationCrumbs(); 123 + $crumbs->addCrumb( 124 + id(new PhabricatorCrumbView()) 125 + ->setName($name) 126 + ->setHref($request->getRequestURI())); 127 + $nav->setCrumbs($crumbs); 116 128 117 129 return $this->buildApplicationPage( 118 130 $nav,
+193
src/applications/macro/controller/PhabricatorMacroViewController.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroViewController 4 + extends PhabricatorMacroController { 5 + 6 + private $id; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->id = $data['id']; 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $user = $request->getUser(); 15 + 16 + $macro = id(new PhabricatorFileImageMacro())->load($this->id); 17 + if (!$macro) { 18 + return new Aphront404Response(); 19 + } 20 + 21 + $file = id(new PhabricatorFile())->loadOneWhere( 22 + 'phid = %s', 23 + $macro->getFilePHID()); 24 + 25 + $title_short = pht('Macro "%s"', $macro->getName()); 26 + $title_long = pht('Image Macro "%s"', $macro->getName()); 27 + 28 + $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID( 29 + $macro->getPHID()); 30 + 31 + $this->loadHandles($subscribers); 32 + 33 + $crumbs = $this->buildApplicationCrumbs(); 34 + $crumbs->addCrumb( 35 + id(new PhabricatorCrumbView()) 36 + ->setHref($this->getApplicationURI('/view/'.$macro->getID().'/')) 37 + ->setName($title_short)); 38 + 39 + $actions = $this->buildActionView($macro); 40 + $properties = $this->buildPropertyView($macro, $file, $subscribers); 41 + 42 + $xactions = id(new PhabricatorMacroTransactionQuery()) 43 + ->setViewer($request->getUser()) 44 + ->withObjectPHIDs(array($macro->getPHID())) 45 + ->execute(); 46 + 47 + $engine = id(new PhabricatorMarkupEngine()) 48 + ->setViewer($user); 49 + foreach ($xactions as $xaction) { 50 + if ($xaction->getComment()) { 51 + $engine->addObject( 52 + $xaction->getComment(), 53 + PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT); 54 + } 55 + } 56 + $engine->process(); 57 + 58 + $timeline = id(new PhabricatorApplicationTransactionView()) 59 + ->setViewer($user) 60 + ->setTransactions($xactions) 61 + ->setMarkupEngine($engine); 62 + 63 + $header = id(new PhabricatorHeaderView()) 64 + ->setHeader($title_long); 65 + 66 + if ($macro->getIsDisabled()) { 67 + $header->addTag( 68 + id(new PhabricatorTagView()) 69 + ->setType(PhabricatorTagView::TYPE_STATE) 70 + ->setName(pht('Macro Disabled')) 71 + ->setBackgroundColor(PhabricatorTagView::COLOR_RED)); 72 + } 73 + 74 + $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); 75 + 76 + $add_comment_header = id(new PhabricatorHeaderView()) 77 + ->setHeader( 78 + $is_serious 79 + ? pht('Add Comment') 80 + : pht('Grovel in Awe')); 81 + 82 + $add_comment_form = id(new AphrontFormView()) 83 + ->setWorkflow(true) 84 + ->setFlexible(true) 85 + ->setAction($this->getApplicationURI('/comment/'.$macro->getID().'/')) 86 + ->setUser($user) 87 + ->appendChild( 88 + id(new PhabricatorRemarkupControl()) 89 + ->setUser($user) 90 + ->setLabel('Comment') 91 + ->setName('comment')) 92 + ->appendChild( 93 + id(new AphrontFormSubmitControl()) 94 + ->setValue( 95 + $is_serious 96 + ? pht('Add Comment') 97 + : pht('Lavish Praise'))); 98 + 99 + return $this->buildApplicationPage( 100 + array( 101 + $crumbs, 102 + $header, 103 + $actions, 104 + $properties, 105 + $timeline, 106 + $add_comment_header, 107 + $add_comment_form, 108 + ), 109 + array( 110 + 'title' => $title_short, 111 + )); 112 + } 113 + 114 + private function buildActionView(PhabricatorFileImageMacro $macro) { 115 + $view = new PhabricatorActionListView(); 116 + $view->setUser($this->getRequest()->getUser()); 117 + $view->setObject($macro); 118 + $view->addAction( 119 + id(new PhabricatorActionView()) 120 + ->setName('Edit Macro') 121 + ->setHref($this->getApplicationURI('/edit/'.$macro->getID().'/')) 122 + ->setIcon('edit')); 123 + 124 + if ($macro->getIsDisabled()) { 125 + $view->addAction( 126 + id(new PhabricatorActionView()) 127 + ->setName('Restore Macro') 128 + ->setHref($this->getApplicationURI('/disable/'.$macro->getID().'/')) 129 + ->setWorkflow(true) 130 + ->setIcon('undo')); 131 + } else { 132 + $view->addAction( 133 + id(new PhabricatorActionView()) 134 + ->setName('Disable Macro') 135 + ->setHref($this->getApplicationURI('/disable/'.$macro->getID().'/')) 136 + ->setWorkflow(true) 137 + ->setIcon('delete')); 138 + } 139 + 140 + return $view; 141 + } 142 + 143 + private function buildPropertyView( 144 + PhabricatorFileImageMacro $macro, 145 + PhabricatorFile $file = null, 146 + array $subscribers) { 147 + 148 + $view = new PhabricatorPropertyListView(); 149 + 150 + $view->addProperty( 151 + pht('Name'), 152 + phutil_escape_html($macro->getName())); 153 + 154 + $view->addProperty( 155 + pht('Status'), 156 + $macro->getIsDisabled() 157 + ? pht('Disabled') 158 + : pht('Enabled')); 159 + 160 + $view->addProperty( 161 + pht('Created'), 162 + phabricator_date( 163 + $macro->getDateCreated(), 164 + $this->getRequest()->getUser())); 165 + 166 + if ($subscribers) { 167 + $sub_view = array(); 168 + foreach ($subscribers as $subscriber) { 169 + $sub_view[] = $this->getHandle($subscriber)->renderLink(); 170 + } 171 + $sub_view = implode(', ', $sub_view); 172 + } else { 173 + $sub_view = '<em>'.pht('None').'</em>'; 174 + } 175 + 176 + $view->addProperty( 177 + pht('Subscribers'), 178 + $sub_view); 179 + 180 + if ($file) { 181 + $view->addTextContent( 182 + phutil_render_tag( 183 + 'img', 184 + array( 185 + 'src' => $file->getViewURI(), 186 + 'class' => 'phabricator-image-macro-hero', 187 + ))); 188 + } 189 + 190 + return $view; 191 + } 192 + 193 + }
+123
src/applications/macro/editor/PhabricatorMacroEditor.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getTransactionTypes() { 7 + $types = parent::getTransactionTypes(); 8 + 9 + $types[] = PhabricatorMacroTransactionType::TYPE_NAME; 10 + $types[] = PhabricatorMacroTransactionType::TYPE_DISABLED; 11 + $types[] = PhabricatorMacroTransactionType::TYPE_FILE; 12 + 13 + return $types; 14 + } 15 + 16 + protected function getCustomTransactionOldValue( 17 + PhabricatorLiskDAO $object, 18 + PhabricatorApplicationTransaction $xaction) { 19 + 20 + switch ($xaction->getTransactionType()) { 21 + case PhabricatorMacroTransactionType::TYPE_NAME: 22 + return $object->getName(); 23 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 24 + return $object->getIsDisabled(); 25 + case PhabricatorMacroTransactionType::TYPE_FILE: 26 + return $object->getFilePHID(); 27 + } 28 + } 29 + 30 + protected function getCustomTransactionNewValue( 31 + PhabricatorLiskDAO $object, 32 + PhabricatorApplicationTransaction $xaction) { 33 + 34 + switch ($xaction->getTransactionType()) { 35 + case PhabricatorMacroTransactionType::TYPE_NAME: 36 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 37 + case PhabricatorMacroTransactionType::TYPE_FILE: 38 + return $xaction->getNewValue(); 39 + } 40 + } 41 + 42 + protected function applyCustomInternalTransaction( 43 + PhabricatorLiskDAO $object, 44 + PhabricatorApplicationTransaction $xaction) { 45 + 46 + switch ($xaction->getTransactionType()) { 47 + case PhabricatorMacroTransactionType::TYPE_NAME: 48 + $object->setName($xaction->getNewValue()); 49 + break; 50 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 51 + $object->setIsDisabled($xaction->getNewValue()); 52 + break; 53 + case PhabricatorMacroTransactionType::TYPE_FILE: 54 + $object->setFilePHID($xaction->getNewValue()); 55 + break; 56 + } 57 + } 58 + 59 + protected function applyCustomExternalTransaction( 60 + PhabricatorLiskDAO $object, 61 + PhabricatorApplicationTransaction $xaction) { 62 + return; 63 + } 64 + 65 + protected function mergeTransactions( 66 + PhabricatorApplicationTransaction $u, 67 + PhabricatorApplicationTransaction $v) { 68 + 69 + $type = $u->getTransactionType(); 70 + switch ($type) { 71 + case PhabricatorMacroTransactionType::TYPE_NAME: 72 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 73 + case PhabricatorMacroTransactionType::TYPE_FILE: 74 + return $v; 75 + } 76 + 77 + return parent::mergeTransactions($u, $v); 78 + } 79 + 80 + protected function supportsMail() { 81 + return true; 82 + } 83 + 84 + protected function buildReplyHandler(PhabricatorLiskDAO $object) { 85 + return id(new PhabricatorMacroReplyHandler()) 86 + ->setMailReceiver($object); 87 + } 88 + 89 + protected function buildMailTemplate(PhabricatorLiskDAO $object) { 90 + $name = $object->getName(); 91 + $name = 'Image Macro "'.$name.'"'; 92 + 93 + return id(new PhabricatorMetaMTAMail()) 94 + ->setSubject($name) 95 + ->addHeader('Thread-Topic', $name); 96 + } 97 + 98 + protected function getMailTo(PhabricatorLiskDAO $object) { 99 + return array( 100 + $this->requireActor()->getPHID(), 101 + ); 102 + } 103 + 104 + protected function buildMailBody( 105 + PhabricatorLiskDAO $object, 106 + array $xactions) { 107 + 108 + $body = parent::buildMailBody($object, $xactions); 109 + $body->addTextSection( 110 + pht('MACRO DETAIL'), 111 + PhabricatorEnv::getProductionURI('/macro/view/'.$object->getID().'/')); 112 + 113 + return $body; 114 + } 115 + 116 + protected function getMailSubjectPrefix() { 117 + return PhabricatorEnv::getEnvConfig('metamta.macro.subject-prefix'); 118 + } 119 + 120 + protected function supportsFeed() { 121 + return true; 122 + } 123 + }
+40
src/applications/macro/mail/PhabricatorMacroReplyHandler.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroReplyHandler extends PhabricatorMailReplyHandler { 4 + 5 + public function validateMailReceiver($mail_receiver) { 6 + if (!($mail_receiver instanceof PhabricatorFileImageMacro)) { 7 + throw new Exception("Mail receiver is not a PhabricatorFileImageMacro!"); 8 + } 9 + } 10 + 11 + public function getPrivateReplyHandlerEmailAddress( 12 + PhabricatorObjectHandle $handle) { 13 + return $this->getDefaultPrivateReplyHandlerEmailAddress($handle, 'MCRO'); 14 + } 15 + 16 + public function getPublicReplyHandlerEmailAddress() { 17 + return $this->getDefaultPublicReplyHandlerEmailAddress('MCRO'); 18 + } 19 + 20 + public function getReplyHandlerDomain() { 21 + return PhabricatorEnv::getEnvConfig( 22 + 'metamta.macro.reply-handler-domain'); 23 + } 24 + 25 + public function getReplyHandlerInstructions() { 26 + if ($this->supportsReplies()) { 27 + // TODO: Implement. 28 + return null; 29 + return "Reply to comment."; 30 + } else { 31 + return null; 32 + } 33 + } 34 + 35 + protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) { 36 + // TODO: Implement this. 37 + return null; 38 + } 39 + 40 + }
+10
src/applications/macro/query/PhabricatorMacroTransactionQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + protected function getTemplateApplicationTransaction() { 7 + return new PhabricatorMacroTransaction(); 8 + } 9 + 10 + }
+15 -2
src/applications/macro/storage/PhabricatorFileImageMacro.php
··· 1 1 <?php 2 2 3 - final class PhabricatorFileImageMacro extends PhabricatorFileDAO { 3 + final class PhabricatorFileImageMacro extends PhabricatorFileDAO 4 + implements PhabricatorSubscribableInterface { 4 5 5 6 protected $filePHID; 7 + protected $phid; 6 8 protected $name; 9 + protected $isDisabled = 0; 7 10 8 11 public function getConfiguration() { 9 12 return array( 10 - self::CONFIG_TIMESTAMPS => false, 13 + self::CONFIG_AUX_PHID => true, 11 14 ) + parent::getConfiguration(); 12 15 } 13 16 17 + public function generatePHID() { 18 + return PhabricatorPHID::generateNewPHID( 19 + PhabricatorPHIDConstants::PHID_TYPE_MCRO); 20 + } 21 + 14 22 static public function newFromImageURI($uri, $file_name, $image_macro_name) { 15 23 $file = PhabricatorFile::newFromFileDownload($uri, $file_name); 16 24 ··· 25 33 26 34 return $image_macro; 27 35 } 36 + 37 + public function isAutomaticallySubscribed($phid) { 38 + return false; 39 + } 40 + 28 41 } 29 42
+231
src/applications/macro/storage/PhabricatorMacroTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + public function getApplicationName() { 7 + return 'file'; 8 + } 9 + 10 + public function getTableName() { 11 + return 'macro_transaction'; 12 + } 13 + 14 + public function getApplicationTransactionType() { 15 + return PhabricatorPHIDConstants::PHID_TYPE_MCRO; 16 + } 17 + 18 + public function getApplicationTransactionCommentObject() { 19 + return new PhabricatorMacroTransactionComment(); 20 + } 21 + 22 + public function getApplicationObjectTypeName() { 23 + return pht('macro'); 24 + } 25 + 26 + public function getRequiredHandlePHIDs() { 27 + $phids = parent::getRequiredHandlePHIDs(); 28 + 29 + $old = $this->getOldValue(); 30 + $new = $this->getNewValue(); 31 + 32 + switch ($this->getTransactionType()) { 33 + case PhabricatorMacroTransactionType::TYPE_FILE: 34 + if ($old !== null) { 35 + $phids[] = $old; 36 + } 37 + $phids[] = $new; 38 + break; 39 + } 40 + 41 + return $phids; 42 + } 43 + 44 + public function shouldHide() { 45 + $old = $this->getOldValue(); 46 + $new = $this->getNewValue(); 47 + 48 + switch ($this->getTransactionType()) { 49 + case PhabricatorMacroTransactionType::TYPE_NAME: 50 + return ($old === null); 51 + } 52 + 53 + return parent::shouldHide(); 54 + } 55 + 56 + public function getTitle() { 57 + $author_phid = $this->getAuthorPHID(); 58 + 59 + $old = $this->getOldValue(); 60 + $new = $this->getNewValue(); 61 + 62 + switch ($this->getTransactionType()) { 63 + case PhabricatorMacroTransactionType::TYPE_NAME: 64 + return pht( 65 + '%s renamed this macro from "%s" to "%s".', 66 + $this->renderHandleLink($author_phid), 67 + phutil_escape_html($old), 68 + phutil_escape_html($new)); 69 + break; 70 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 71 + if ($new) { 72 + return pht( 73 + '%s disabled this macro.', 74 + $this->renderHandleLink($author_phid)); 75 + } else { 76 + return pht( 77 + '%s restored this macro.', 78 + $this->renderHandleLink($author_phid)); 79 + } 80 + break; 81 + case PhabricatorMacroTransactionType::TYPE_FILE: 82 + if ($old === null) { 83 + return pht( 84 + '%s created this macro.', 85 + $this->renderHandleLink($author_phid)); 86 + } else { 87 + return pht( 88 + '%s changed the image for this macro from %s to %s.', 89 + $this->renderHandleLink($author_phid), 90 + $this->renderHandleLink($old), 91 + $this->renderHandleLink($new)); 92 + } 93 + break; 94 + } 95 + 96 + return parent::getTitle(); 97 + } 98 + 99 + public function getTitleForFeed() { 100 + $author_phid = $this->getAuthorPHID(); 101 + $object_phid = $this->getObjectPHID(); 102 + 103 + $old = $this->getOldValue(); 104 + $new = $this->getNewValue(); 105 + 106 + switch ($this->getTransactionType()) { 107 + case PhabricatorMacroTransactionType::TYPE_NAME: 108 + return pht( 109 + '%s renamed %s from "%s" to "%s".', 110 + $this->renderHandleLink($author_phid), 111 + $this->renderHandleLink($object_phid), 112 + phutil_escape_html($old), 113 + phutil_escape_html($new)); 114 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 115 + if ($new) { 116 + return pht( 117 + '%s disabled %s.', 118 + $this->renderHandleLink($author_phid), 119 + $this->renderHandleLink($object_phid)); 120 + } else { 121 + return pht( 122 + '%s restored %s.', 123 + $this->renderHandleLink($author_phid), 124 + $this->renderHandleLink($object_phid)); 125 + } 126 + case PhabricatorMacroTransactionType::TYPE_FILE: 127 + if ($old === null) { 128 + return pht( 129 + '%s created %s.', 130 + $this->renderHandleLink($author_phid), 131 + $this->renderHandleLink($object_phid)); 132 + } else { 133 + return pht( 134 + '%s updated the image for %s.', 135 + $this->renderHandleLink($author_phid), 136 + $this->renderHandleLink($object_phid)); 137 + } 138 + } 139 + 140 + return parent::getTitleForFeed(); 141 + } 142 + 143 + public function getActionName() { 144 + $old = $this->getOldValue(); 145 + $new = $this->getNewValue(); 146 + 147 + switch ($this->getTransactionType()) { 148 + case PhabricatorMacroTransactionType::TYPE_NAME: 149 + if ($old === null) { 150 + return pht('Created'); 151 + } else { 152 + return pht('Renamed'); 153 + } 154 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 155 + if ($new) { 156 + return pht('Disabled'); 157 + } else { 158 + return pht('Restored'); 159 + } 160 + case PhabricatorMacroTransactionType::TYPE_FILE: 161 + if ($old === null) { 162 + return pht('Created'); 163 + } else { 164 + return pht('Edited Image'); 165 + } 166 + } 167 + 168 + return parent::getActionName(); 169 + } 170 + 171 + public function getActionStrength() { 172 + switch ($this->getTransactionType()) { 173 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 174 + return 2.0; 175 + case PhabricatorMacroTransactionType::TYPE_FILE: 176 + return 1.5; 177 + } 178 + return parent::getActionStrength(); 179 + } 180 + 181 + public function getIcon() { 182 + $old = $this->getOldValue(); 183 + $new = $this->getNewValue(); 184 + 185 + switch ($this->getTransactionType()) { 186 + case PhabricatorMacroTransactionType::TYPE_NAME: 187 + return 'edit'; 188 + case PhabricatorMacroTransactionType::TYPE_FILE: 189 + if ($old === null) { 190 + return 'create'; 191 + } else { 192 + return 'edit'; 193 + } 194 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 195 + if ($new) { 196 + return 'delete'; 197 + } else { 198 + return 'undo'; 199 + } 200 + } 201 + 202 + return parent::getIcon(); 203 + } 204 + 205 + public function getColor() { 206 + $old = $this->getOldValue(); 207 + $new = $this->getNewValue(); 208 + 209 + switch ($this->getTransactionType()) { 210 + case PhabricatorMacroTransactionType::TYPE_NAME: 211 + return PhabricatorTransactions::COLOR_BLUE; 212 + case PhabricatorMacroTransactionType::TYPE_FILE: 213 + if ($old === null) { 214 + return PhabricatorTransactions::COLOR_GREEN; 215 + } else { 216 + return PhabricatorTransactions::COLOR_BLUE; 217 + } 218 + case PhabricatorMacroTransactionType::TYPE_DISABLED: 219 + if ($new) { 220 + return PhabricatorTransactions::COLOR_BLACK; 221 + } else { 222 + return PhabricatorTransactions::COLOR_SKY; 223 + } 224 + } 225 + 226 + return parent::getColor(); 227 + } 228 + 229 + 230 + } 231 +
+11
src/applications/macro/storage/PhabricatorMacroTransactionComment.php
··· 1 + <?php 2 + 3 + final class PhabricatorMacroTransactionComment 4 + extends PhabricatorApplicationTransactionComment { 5 + 6 + public function getApplicationTransactionObject() { 7 + return new PhabricatorMacroTransaction(); 8 + } 9 + 10 + } 11 +
+1
src/applications/phid/PhabricatorPHIDConstants.php
··· 29 29 const PHID_TYPE_QUES = 'QUES'; 30 30 const PHID_TYPE_ANSW = 'ANSW'; 31 31 const PHID_TYPE_MOCK = 'MOCK'; 32 + const PHID_TYPE_MCRO = 'MCRO'; 32 33 33 34 const PHID_TYPE_XACT = 'XACT'; 34 35 const PHID_TYPE_XCMT = 'XCMT';
+34
src/applications/phid/handle/PhabricatorObjectHandleData.php
··· 120 120 ->execute(); 121 121 $xactions += mpull($results, null, 'getPHID'); 122 122 break; 123 + case PhabricatorPHIDConstants::PHID_TYPE_MCRO: 124 + $results = id(new PhabricatorMacroTransactionQuery()) 125 + ->setViewer($this->viewer) 126 + ->withPHIDs($subtype_phids) 127 + ->execute(); 128 + $xactions += mpull($results, null, 'getPHID'); 129 + break; 123 130 } 124 131 } 125 132 foreach ($xactions as $xaction) { 126 133 $objects[$xaction->getPHID()] = $xaction; 127 134 } 135 + break; 136 + case PhabricatorPHIDConstants::PHID_TYPE_MCRO: 137 + $macros = id(new PhabricatorFileImageMacro())->loadAllWhere( 138 + 'phid IN (%Ls)', 139 + $phids); 140 + $objects += mpull($macros, null, 'getPHID'); 128 141 break; 129 142 } 130 143 } ··· 596 609 $handle->setName($mock->getName()); 597 610 $handle->setFullName('M'.$mock->getID().': '.$mock->getName()); 598 611 $handle->setURI('/M'.$mock->getID()); 612 + $handle->setComplete(true); 613 + } 614 + $handles[$phid] = $handle; 615 + } 616 + break; 617 + case PhabricatorPHIDConstants::PHID_TYPE_MCRO: 618 + $macros = id(new PhabricatorFileImageMacro())->loadAllWhere( 619 + 'phid IN (%Ls)', 620 + $phids); 621 + $macros = mpull($macros, null, 'getPHID'); 622 + foreach ($phids as $phid) { 623 + $handle = new PhabricatorObjectHandle(); 624 + $handle->setPHID($phid); 625 + $handle->setType($type); 626 + if (empty($macros[$phid])) { 627 + $handle->setName('Unknown Macro'); 628 + } else { 629 + $macro = $macros[$phid]; 630 + $handle->setName($macro->getName()); 631 + $handle->setFullName('Image Macro "'.$macro->getName().'"'); 632 + $handle->setURI('/macro/view/'.$macro->getID().'/'); 599 633 $handle->setComplete(true); 600 634 } 601 635 $handles[$phid] = $handle;
-2
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 771 771 $story_type = $this->getFeedStoryType(); 772 772 $story_data = $this->getFeedStoryData($object, $xactions); 773 773 774 - phlog($subscribed_phids); 775 - 776 774 id(new PhabricatorFeedStoryPublisher()) 777 775 ->setStoryType($story_type) 778 776 ->setStoryData($story_data)
+15 -4
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 2 2 3 3 abstract class PhabricatorApplicationTransaction 4 4 extends PhabricatorLiskDAO 5 - implements PhabricatorPolicyInterface{ 6 - 7 - const MARKUP_FIELD_COMMENT = 'markup:comment'; 5 + implements PhabricatorPolicyInterface { 8 6 9 7 const TARGET_TEXT = 'text'; 10 8 const TARGET_HTML = 'html'; ··· 17 15 18 16 protected $commentPHID; 19 17 protected $commentVersion = 0; 20 - 21 18 protected $transactionType; 22 19 protected $oldValue; 23 20 protected $newValue; ··· 58 55 59 56 public function getContentSource() { 60 57 return PhabricatorContentSource::newFromSerialized($this->contentSource); 58 + } 59 + 60 + public function hasComment() { 61 + return $this->getComment() && strlen($this->getComment()->getContent()); 61 62 } 62 63 63 64 public function getComment() { ··· 137 138 } 138 139 139 140 public function getIcon() { 141 + switch ($this->getTransactionType()) { 142 + case PhabricatorTransactions::TYPE_COMMENT: 143 + return 'comment'; 144 + case PhabricatorTransactions::TYPE_SUBSCRIBERS: 145 + return 'message'; 146 + case PhabricatorTransactions::TYPE_VIEW_POLICY: 147 + case PhabricatorTransactions::TYPE_EDIT_POLICY: 148 + return 'lock'; 149 + } 150 + 140 151 return null; 141 152 } 142 153
+2
src/applications/transactions/storage/PhabricatorApplicationTransactionComment.php
··· 4 4 extends PhabricatorLiskDAO 5 5 implements PhabricatorMarkupInterface, PhabricatorPolicyInterface { 6 6 7 + const MARKUP_FIELD_COMMENT = 'markup:comment'; 8 + 7 9 protected $transactionPHID; 8 10 protected $commentVersion; 9 11 protected $authorPHID;
+81
src/applications/transactions/view/PhabricatorApplicationTransactionView.php
··· 1 + <?php 2 + 3 + /** 4 + * @concrete-extensible 5 + */ 6 + class PhabricatorApplicationTransactionView extends AphrontView { 7 + 8 + private $viewer; 9 + private $transactions; 10 + private $engine; 11 + private $anchorOffset = 0; 12 + 13 + public function setAnchorOffset($anchor_offset) { 14 + $this->anchorOffset = $anchor_offset; 15 + return $this; 16 + } 17 + 18 + public function setMarkupEngine(PhabricatorMarkupEngine $engine) { 19 + $this->engine = $engine; 20 + return $this; 21 + } 22 + 23 + public function setTransactions(array $transactions) { 24 + assert_instances_of($transactions, 'PhabricatorApplicationTransaction'); 25 + $this->transactions = $transactions; 26 + return $this; 27 + } 28 + 29 + public function setViewer(PhabricatorUser $viewer) { 30 + $this->viewer = $viewer; 31 + return $this; 32 + } 33 + 34 + public function render() { 35 + $field = PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT; 36 + 37 + if (!$this->engine) { 38 + $engine = id(new PhabricatorMarkupEngine()) 39 + ->setViewer($this->viewer); 40 + foreach ($this->transactions as $xaction) { 41 + if (!$xaction->hasComment()) { 42 + continue; 43 + } 44 + $engine->addObject($xaction->getComment(), $field); 45 + } 46 + $engine->process(); 47 + 48 + $this->engine = $engine; 49 + } 50 + 51 + $view = new PhabricatorTimelineView(); 52 + 53 + $anchor = $this->anchorOffset; 54 + foreach ($this->transactions as $xaction) { 55 + if ($xaction->shouldHide()) { 56 + continue; 57 + } 58 + 59 + $anchor++; 60 + $event = id(new PhabricatorTimelineEventView()) 61 + ->setViewer($this->viewer) 62 + ->setUserHandle($xaction->getHandle($xaction->getAuthorPHID())) 63 + ->setIcon($xaction->getIcon()) 64 + ->setColor($xaction->getColor()) 65 + ->setTitle($xaction->getTitle()) 66 + ->setDateCreated($xaction->getDateCreated()) 67 + ->setContentSource($xaction->getContentSource()) 68 + ->setAnchor($anchor); 69 + 70 + if ($xaction->hasComment()) { 71 + $event->appendChild( 72 + $this->engine->getOutput($xaction->getComment(), $field)); 73 + } 74 + 75 + $view->addEvent($event); 76 + } 77 + 78 + return $view->render(); 79 + } 80 + } 81 +
+1
src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
··· 108 108 PhabricatorPHIDConstants::PHID_TYPE_QUES => 'PonderQuestion', 109 109 PhabricatorPHIDConstants::PHID_TYPE_ANSW => 'PonderAnswer', 110 110 PhabricatorPHIDConstants::PHID_TYPE_MOCK => 'PholioMock', 111 + PhabricatorPHIDConstants::PHID_TYPE_MCRO => 'PhabricatorFileImageMacro', 111 112 112 113 ); 113 114
+2 -1
src/infrastructure/markup/rule/PhabricatorRemarkupRuleImageMacro.php
··· 18 18 public function markupImageMacro($matches) { 19 19 if ($this->images === null) { 20 20 $this->images = array(); 21 - $rows = id(new PhabricatorFileImageMacro())->loadAll(); 21 + $rows = id(new PhabricatorFileImageMacro())->loadAllWhere( 22 + 'isDisabled = 0'); 22 23 foreach ($rows as $row) { 23 24 $this->images[$row->getName()] = $row->getFilePHID(); 24 25 }
+12
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1048 1048 'type' => 'sql', 1049 1049 'name' => $this->getPatchPath('20121209.pholioxactions.sql'), 1050 1050 ), 1051 + '20121209.xmacroadd.sql' => array( 1052 + 'type' => 'sql', 1053 + 'name' => $this->getPatchPath('20121209.xmacroadd.sql'), 1054 + ), 1055 + '20121209.xmacromigrate.php' => array( 1056 + 'type' => 'php', 1057 + 'name' => $this->getPatchPath('20121209.xmacromigrate.php'), 1058 + ), 1059 + '20121209.xmacromigratekey.sql' => array( 1060 + 'type' => 'sql', 1061 + 'name' => $this->getPatchPath('20121209.xmacromigratekey.sql'), 1062 + ), 1051 1063 ); 1052 1064 } 1053 1065
+15
src/view/layout/PhabricatorHeaderView.php
··· 4 4 5 5 private $objectName; 6 6 private $header; 7 + private $tags = array(); 7 8 8 9 public function setHeader($header) { 9 10 $this->header = $header; ··· 15 16 return $this; 16 17 } 17 18 19 + public function addTag(PhabricatorTagView $tag) { 20 + $this->tags[] = $tag; 21 + return $this; 22 + } 23 + 18 24 public function render() { 19 25 require_celerity_resource('phabricator-header-view-css'); 20 26 ··· 27 33 'href' => '/'.$this->objectName, 28 34 ), 29 35 phutil_escape_html($this->objectName)).' '.$header; 36 + } 37 + 38 + if ($this->tags) { 39 + $header .= phutil_render_tag( 40 + 'div', 41 + array( 42 + 'class' => 'phabricator-header-tags', 43 + ), 44 + self::renderSingleView($this->tags)); 30 45 } 31 46 32 47 return phutil_render_tag(
+5
webroot/rsrc/css/core/remarkup.css
··· 312 312 .remarkup-assist-right { 313 313 float: right; 314 314 } 315 + 316 + .phabricator-image-macro-hero { 317 + margin: 2em auto; 318 + max-width: 90%; 319 + }
+5
webroot/rsrc/css/layout/phabricator-header-view.css
··· 11 11 .device-desktop .phabricator-header-view { 12 12 width: 66%; 13 13 } 14 + 15 + .phabricator-header-tags { 16 + font-size: 13px; 17 + float: right; 18 + }