@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.

Modularize application transactions in Paste, mostly

Summary:
Ref T9789. `Transaction` and `Editor` classes are the last major pieces of infrastructure that haven't been fully modularized.

Some of the specific issues are:

- `Editor` classes rely on a bunch of `instanceof` stuff in the base class to pick up transaction types like "subscribe", "projects", etc. Instead, applications should be adding these, and third-party applications should be able to add them.
- Code is spread across `Transaction` and `Editor` classes somewhat oddly. For example, generating old/new values would probably make more sense at the `Transaction` level, but it currently exists at the `Editor` level.
- Both types of classes have a lot of functions based on `switch()` statements, which require a ton of boilerplate and are just generally kind of hard to work with.

This creates classes for each type of transaction, and moves almost all of the logic to them. These classes are simpler and more focused than the old stuff was, and can organize related code better.

This starts inching toward defining `CoreTransactions` for features shared across applications. It only defines the "Create" transaction so far, but at some point I plan to move all the other shared transactions to Core and let them control which objects they're available for.

Test Plan:
- Created pastes with web UI and API.
- Edited all paste properites.
- Archived/activated.
- Verified files got reasonable names.
- Reviewed timeline and feed.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9789

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

+746 -467
+3 -45
resources/sql/patches/20130801.pastexactions.php
··· 1 1 <?php 2 2 3 - $table = new PhabricatorPaste(); 4 - $x_table = new PhabricatorPasteTransaction(); 5 - 6 - $conn_w = $table->establishConnection('w'); 7 - $conn_w->openTransaction(); 8 - 9 - echo pht('Adding transactions for existing paste objects...')."\n"; 10 - 11 - $rows = new LiskRawMigrationIterator($conn_w, 'pastebin_paste'); 12 - foreach ($rows as $row) { 13 - 14 - $id = $row['id']; 15 - echo pht('Adding transactions for paste id %d...', $id)."\n"; 16 - 17 - $xaction_phid = PhabricatorPHID::generateNewPHID( 18 - PhabricatorApplicationTransactionTransactionPHIDType::TYPECONST); 19 - 20 - queryfx( 21 - $conn_w, 22 - 'INSERT INTO %T (phid, authorPHID, objectPHID, viewPolicy, editPolicy, 23 - transactionType, oldValue, newValue, 24 - contentSource, metadata, dateCreated, dateModified, 25 - commentVersion) 26 - VALUES (%s, %s, %s, %s, %s, %s, %ns, %ns, %s, %s, %d, %d, %d)', 27 - $x_table->getTableName(), 28 - $xaction_phid, 29 - $row['authorPHID'], 30 - $row['phid'], 31 - 'public', 32 - $row['authorPHID'], 33 - PhabricatorPasteTransaction::TYPE_CONTENT, 34 - 'null', 35 - $row['filePHID'], 36 - PhabricatorContentSource::newForSource( 37 - PhabricatorOldWorldContentSource::SOURCECONST)->serialize(), 38 - '[]', 39 - $row['dateCreated'], 40 - $row['dateCreated'], 41 - 0); 42 - 43 - } 44 - 45 - $conn_w->saveTransaction(); 46 - 47 - echo pht('Done.')."\n"; 3 + // Long ago, this migration populated initial "create" transactions for old 4 + // pastes from before transactions came into existence. It was removed after 5 + // about three years.
+21 -1
src/__phutil_library_map__.php
··· 2160 2160 'PhabricatorController' => 'applications/base/controller/PhabricatorController.php', 2161 2161 'PhabricatorCookies' => 'applications/auth/constants/PhabricatorCookies.php', 2162 2162 'PhabricatorCoreConfigOptions' => 'applications/config/option/PhabricatorCoreConfigOptions.php', 2163 + 'PhabricatorCoreCreateTransaction' => 'applications/transactions/xaction/PhabricatorCoreCreateTransaction.php', 2164 + 'PhabricatorCoreTransactionType' => 'applications/transactions/xaction/PhabricatorCoreTransactionType.php', 2165 + 'PhabricatorCoreVoidTransaction' => 'applications/transactions/xaction/PhabricatorCoreVoidTransaction.php', 2163 2166 'PhabricatorCountdown' => 'applications/countdown/storage/PhabricatorCountdown.php', 2164 2167 'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php', 2165 2168 'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php', ··· 2757 2760 'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php', 2758 2761 'PhabricatorMetaMTAWorker' => 'applications/metamta/PhabricatorMetaMTAWorker.php', 2759 2762 'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php', 2763 + 'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php', 2764 + 'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php', 2760 2765 'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php', 2761 2766 'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php', 2762 2767 'PhabricatorMotivatorProfilePanel' => 'applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php', ··· 2916 2921 'PhabricatorPasteArchiveController' => 'applications/paste/controller/PhabricatorPasteArchiveController.php', 2917 2922 'PhabricatorPasteConfigOptions' => 'applications/paste/config/PhabricatorPasteConfigOptions.php', 2918 2923 'PhabricatorPasteContentSearchEngineAttachment' => 'applications/paste/engineextension/PhabricatorPasteContentSearchEngineAttachment.php', 2924 + 'PhabricatorPasteContentTransaction' => 'applications/paste/xaction/PhabricatorPasteContentTransaction.php', 2919 2925 'PhabricatorPasteController' => 'applications/paste/controller/PhabricatorPasteController.php', 2920 2926 'PhabricatorPasteDAO' => 'applications/paste/storage/PhabricatorPasteDAO.php', 2921 2927 'PhabricatorPasteEditController' => 'applications/paste/controller/PhabricatorPasteEditController.php', 2922 2928 'PhabricatorPasteEditEngine' => 'applications/paste/editor/PhabricatorPasteEditEngine.php', 2923 2929 'PhabricatorPasteEditor' => 'applications/paste/editor/PhabricatorPasteEditor.php', 2924 2930 'PhabricatorPasteFilenameContextFreeGrammar' => 'applications/paste/lipsum/PhabricatorPasteFilenameContextFreeGrammar.php', 2931 + 'PhabricatorPasteLanguageTransaction' => 'applications/paste/xaction/PhabricatorPasteLanguageTransaction.php', 2925 2932 'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php', 2926 2933 'PhabricatorPastePastePHIDType' => 'applications/paste/phid/PhabricatorPastePastePHIDType.php', 2927 2934 'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php', ··· 2930 2937 'PhabricatorPasteSchemaSpec' => 'applications/paste/storage/PhabricatorPasteSchemaSpec.php', 2931 2938 'PhabricatorPasteSearchEngine' => 'applications/paste/query/PhabricatorPasteSearchEngine.php', 2932 2939 'PhabricatorPasteSnippet' => 'applications/paste/snippet/PhabricatorPasteSnippet.php', 2940 + 'PhabricatorPasteStatusTransaction' => 'applications/paste/xaction/PhabricatorPasteStatusTransaction.php', 2933 2941 'PhabricatorPasteTestDataGenerator' => 'applications/paste/lipsum/PhabricatorPasteTestDataGenerator.php', 2942 + 'PhabricatorPasteTitleTransaction' => 'applications/paste/xaction/PhabricatorPasteTitleTransaction.php', 2934 2943 'PhabricatorPasteTransaction' => 'applications/paste/storage/PhabricatorPasteTransaction.php', 2935 2944 'PhabricatorPasteTransactionComment' => 'applications/paste/storage/PhabricatorPasteTransactionComment.php', 2936 2945 'PhabricatorPasteTransactionQuery' => 'applications/paste/query/PhabricatorPasteTransactionQuery.php', 2946 + 'PhabricatorPasteTransactionType' => 'applications/paste/xaction/PhabricatorPasteTransactionType.php', 2937 2947 'PhabricatorPasteViewController' => 'applications/paste/controller/PhabricatorPasteViewController.php', 2938 2948 'PhabricatorPathSetupCheck' => 'applications/config/check/PhabricatorPathSetupCheck.php', 2939 2949 'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php', ··· 6729 6739 'PhabricatorController' => 'AphrontController', 6730 6740 'PhabricatorCookies' => 'Phobject', 6731 6741 'PhabricatorCoreConfigOptions' => 'PhabricatorApplicationConfigOptions', 6742 + 'PhabricatorCoreCreateTransaction' => 'PhabricatorCoreTransactionType', 6743 + 'PhabricatorCoreTransactionType' => 'PhabricatorModularTransactionType', 6744 + 'PhabricatorCoreVoidTransaction' => 'PhabricatorModularTransactionType', 6732 6745 'PhabricatorCountdown' => array( 6733 6746 'PhabricatorCountdownDAO', 6734 6747 'PhabricatorPolicyInterface', ··· 7406 7419 'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController', 7407 7420 'PhabricatorMetaMTAWorker' => 'PhabricatorWorker', 7408 7421 'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock', 7422 + 'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction', 7423 + 'PhabricatorModularTransactionType' => 'Phobject', 7409 7424 'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting', 7410 7425 'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting', 7411 7426 'PhabricatorMotivatorProfilePanel' => 'PhabricatorProfilePanel', ··· 7601 7616 'PhabricatorPasteArchiveController' => 'PhabricatorPasteController', 7602 7617 'PhabricatorPasteConfigOptions' => 'PhabricatorApplicationConfigOptions', 7603 7618 'PhabricatorPasteContentSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment', 7619 + 'PhabricatorPasteContentTransaction' => 'PhabricatorPasteTransactionType', 7604 7620 'PhabricatorPasteController' => 'PhabricatorController', 7605 7621 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', 7606 7622 'PhabricatorPasteEditController' => 'PhabricatorPasteController', 7607 7623 'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine', 7608 7624 'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor', 7609 7625 'PhabricatorPasteFilenameContextFreeGrammar' => 'PhutilContextFreeGrammar', 7626 + 'PhabricatorPasteLanguageTransaction' => 'PhabricatorPasteTransactionType', 7610 7627 'PhabricatorPasteListController' => 'PhabricatorPasteController', 7611 7628 'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType', 7612 7629 'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', ··· 7615 7632 'PhabricatorPasteSchemaSpec' => 'PhabricatorConfigSchemaSpec', 7616 7633 'PhabricatorPasteSearchEngine' => 'PhabricatorApplicationSearchEngine', 7617 7634 'PhabricatorPasteSnippet' => 'Phobject', 7635 + 'PhabricatorPasteStatusTransaction' => 'PhabricatorPasteTransactionType', 7618 7636 'PhabricatorPasteTestDataGenerator' => 'PhabricatorTestDataGenerator', 7619 - 'PhabricatorPasteTransaction' => 'PhabricatorApplicationTransaction', 7637 + 'PhabricatorPasteTitleTransaction' => 'PhabricatorPasteTransactionType', 7638 + 'PhabricatorPasteTransaction' => 'PhabricatorModularTransaction', 7620 7639 'PhabricatorPasteTransactionComment' => 'PhabricatorApplicationTransactionComment', 7621 7640 'PhabricatorPasteTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 7641 + 'PhabricatorPasteTransactionType' => 'PhabricatorModularTransactionType', 7622 7642 'PhabricatorPasteViewController' => 'PhabricatorPasteController', 7623 7643 'PhabricatorPathSetupCheck' => 'PhabricatorSetupCheck', 7624 7644 'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
+3 -3
src/applications/paste/conduit/PasteCreateConduitAPIMethod.php
··· 47 47 $xactions = array(); 48 48 49 49 $xactions[] = id(new PhabricatorPasteTransaction()) 50 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_CONTENT) 50 + ->setTransactionType(PhabricatorPasteContentTransaction::TRANSACTIONTYPE) 51 51 ->setNewValue($content); 52 52 53 53 $xactions[] = id(new PhabricatorPasteTransaction()) 54 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_TITLE) 54 + ->setTransactionType(PhabricatorPasteTitleTransaction::TRANSACTIONTYPE) 55 55 ->setNewValue($title); 56 56 57 57 $xactions[] = id(new PhabricatorPasteTransaction()) 58 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE) 58 + ->setTransactionType(PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE) 59 59 ->setNewValue($language); 60 60 61 61 $editor = id(new PhabricatorPasteEditor())
+2 -2
src/applications/paste/controller/PhabricatorPasteArchiveController.php
··· 20 20 return new Aphront404Response(); 21 21 } 22 22 23 - $view_uri = '/P'.$paste->getID(); 23 + $view_uri = $paste->getURI(); 24 24 25 25 if ($request->isFormPost()) { 26 26 if ($paste->isArchived()) { ··· 32 32 $xactions = array(); 33 33 34 34 $xactions[] = id(new PhabricatorPasteTransaction()) 35 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_STATUS) 35 + ->setTransactionType(PhabricatorPasteStatusTransaction::TRANSACTIONTYPE) 36 36 ->setNewValue($new_status); 37 37 38 38 id(new PhabricatorPasteEditor())
+7 -4
src/applications/paste/editor/PhabricatorPasteEditEngine.php
··· 71 71 id(new PhabricatorTextEditField()) 72 72 ->setKey('title') 73 73 ->setLabel(pht('Title')) 74 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_TITLE) 74 + ->setTransactionType(PhabricatorPasteTitleTransaction::TRANSACTIONTYPE) 75 75 ->setDescription(pht('The title of the paste.')) 76 76 ->setConduitDescription(pht('Retitle the paste.')) 77 77 ->setConduitTypeDescription(pht('New paste title.')) ··· 79 79 id(new PhabricatorSelectEditField()) 80 80 ->setKey('language') 81 81 ->setLabel(pht('Language')) 82 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE) 82 + ->setTransactionType( 83 + PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE) 83 84 ->setAliases(array('lang')) 84 85 ->setIsCopyable(true) 85 86 ->setOptions($langs) ··· 94 95 id(new PhabricatorTextAreaEditField()) 95 96 ->setKey('text') 96 97 ->setLabel(pht('Text')) 97 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_CONTENT) 98 + ->setTransactionType( 99 + PhabricatorPasteContentTransaction::TRANSACTIONTYPE) 98 100 ->setMonospaced(true) 99 101 ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) 100 102 ->setDescription(pht('The main body text of the paste.')) ··· 104 106 id(new PhabricatorSelectEditField()) 105 107 ->setKey('status') 106 108 ->setLabel(pht('Status')) 107 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_STATUS) 109 + ->setTransactionType( 110 + PhabricatorPasteStatusTransaction::TRANSACTIONTYPE) 108 111 ->setIsConduitOnly(true) 109 112 ->setOptions(PhabricatorPaste::getStatusNameMap()) 110 113 ->setDescription(pht('Active or archived status.'))
+8 -175
src/applications/paste/editor/PhabricatorPasteEditor.php
··· 3 3 final class PhabricatorPasteEditor 4 4 extends PhabricatorApplicationTransactionEditor { 5 5 6 - private $fileName; 7 - 8 6 public function getEditorApplicationClass() { 9 7 return 'PhabricatorPasteApplication'; 10 8 } ··· 13 11 return pht('Pastes'); 14 12 } 15 13 16 - public static function initializeFileForPaste( 17 - PhabricatorUser $actor, 18 - $name, 19 - $data) { 14 + public function getCreateObjectTitle($author, $object) { 15 + return pht('%s created this paste.', $author); 16 + } 20 17 21 - return PhabricatorFile::newFromFileData( 22 - $data, 23 - array( 24 - 'name' => $name, 25 - 'mime-type' => 'text/plain; charset=utf-8', 26 - 'authorPHID' => $actor->getPHID(), 27 - 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 28 - 'editPolicy' => PhabricatorPolicies::POLICY_NOONE, 29 - )); 18 + public function getCreateObjectTitleForFeed($author, $object) { 19 + return pht('%s created %s.', $author, $object); 30 20 } 31 21 32 22 public function getTransactionTypes() { 33 23 $types = parent::getTransactionTypes(); 34 24 35 - $types[] = PhabricatorPasteTransaction::TYPE_CONTENT; 36 - $types[] = PhabricatorPasteTransaction::TYPE_TITLE; 37 - $types[] = PhabricatorPasteTransaction::TYPE_LANGUAGE; 38 - $types[] = PhabricatorPasteTransaction::TYPE_STATUS; 39 25 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 40 26 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 41 27 $types[] = PhabricatorTransactions::TYPE_COMMENT; ··· 43 29 return $types; 44 30 } 45 31 46 - protected function shouldApplyInitialEffects( 47 - PhabricatorLiskDAO $object, 48 - array $xactions) { 49 - return true; 50 - } 51 - 52 - protected function applyInitialEffects( 53 - PhabricatorLiskDAO $object, 54 - array $xactions) { 55 - 56 - // Find the most user-friendly filename we can by examining the title of 57 - // the paste and the pending transactions. We'll use this if we create a 58 - // new file to store raw content later. 59 - 60 - $name = $object->getTitle(); 61 - if (!strlen($name)) { 62 - $name = 'paste.raw'; 63 - } 64 - 65 - $type_title = PhabricatorPasteTransaction::TYPE_TITLE; 66 - foreach ($xactions as $xaction) { 67 - if ($xaction->getTransactionType() == $type_title) { 68 - $name = $xaction->getNewValue(); 69 - } 70 - } 71 - 72 - $this->fileName = $name; 73 - } 74 - 75 - protected function validateTransaction( 32 + protected function shouldSendMail( 76 33 PhabricatorLiskDAO $object, 77 - $type, 78 34 array $xactions) { 79 35 80 - $errors = parent::validateTransaction($object, $type, $xactions); 81 - switch ($type) { 82 - case PhabricatorPasteTransaction::TYPE_CONTENT: 83 - if (!$object->getFilePHID() && !$xactions) { 84 - $error = new PhabricatorApplicationTransactionValidationError( 85 - $type, 86 - pht('Required'), 87 - pht('You must provide content to create a paste.'), 88 - null); 89 - 90 - $error->setIsMissingFieldError(true); 91 - $errors[] = $error; 92 - } 93 - break; 36 + if ($this->getIsNewObject()) { 37 + return false; 94 38 } 95 39 96 - return $errors; 97 - } 98 - 99 - protected function getCustomTransactionOldValue( 100 - PhabricatorLiskDAO $object, 101 - PhabricatorApplicationTransaction $xaction) { 102 - 103 - switch ($xaction->getTransactionType()) { 104 - case PhabricatorPasteTransaction::TYPE_CONTENT: 105 - return $object->getFilePHID(); 106 - case PhabricatorPasteTransaction::TYPE_TITLE: 107 - return $object->getTitle(); 108 - case PhabricatorPasteTransaction::TYPE_LANGUAGE: 109 - return $object->getLanguage(); 110 - case PhabricatorPasteTransaction::TYPE_STATUS: 111 - return $object->getStatus(); 112 - } 113 - } 114 - 115 - protected function getCustomTransactionNewValue( 116 - PhabricatorLiskDAO $object, 117 - PhabricatorApplicationTransaction $xaction) { 118 - 119 - switch ($xaction->getTransactionType()) { 120 - case PhabricatorPasteTransaction::TYPE_TITLE: 121 - case PhabricatorPasteTransaction::TYPE_LANGUAGE: 122 - case PhabricatorPasteTransaction::TYPE_STATUS: 123 - return $xaction->getNewValue(); 124 - case PhabricatorPasteTransaction::TYPE_CONTENT: 125 - // If this transaction does not really change the paste content, return 126 - // the current file PHID so this transaction no-ops. 127 - $new_content = $xaction->getNewValue(); 128 - $old_content = $object->getRawContent(); 129 - $file_phid = $object->getFilePHID(); 130 - if (($new_content === $old_content) && $file_phid) { 131 - return $file_phid; 132 - } 133 - 134 - $file = self::initializeFileForPaste( 135 - $this->getActor(), 136 - $this->fileName, 137 - $xaction->getNewValue()); 138 - 139 - return $file->getPHID(); 140 - } 141 - } 142 - 143 - protected function applyCustomInternalTransaction( 144 - PhabricatorLiskDAO $object, 145 - PhabricatorApplicationTransaction $xaction) { 146 - 147 - switch ($xaction->getTransactionType()) { 148 - case PhabricatorPasteTransaction::TYPE_CONTENT: 149 - $object->setFilePHID($xaction->getNewValue()); 150 - return; 151 - case PhabricatorPasteTransaction::TYPE_TITLE: 152 - $object->setTitle($xaction->getNewValue()); 153 - return; 154 - case PhabricatorPasteTransaction::TYPE_LANGUAGE: 155 - $object->setLanguage($xaction->getNewValue()); 156 - return; 157 - case PhabricatorPasteTransaction::TYPE_STATUS: 158 - $object->setStatus($xaction->getNewValue()); 159 - return; 160 - } 161 - 162 - return parent::applyCustomInternalTransaction($object, $xaction); 163 - } 164 - 165 - protected function applyCustomExternalTransaction( 166 - PhabricatorLiskDAO $object, 167 - PhabricatorApplicationTransaction $xaction) { 168 - 169 - switch ($xaction->getTransactionType()) { 170 - case PhabricatorPasteTransaction::TYPE_CONTENT: 171 - case PhabricatorPasteTransaction::TYPE_TITLE: 172 - case PhabricatorPasteTransaction::TYPE_LANGUAGE: 173 - case PhabricatorPasteTransaction::TYPE_STATUS: 174 - return; 175 - } 176 - 177 - return parent::applyCustomExternalTransaction($object, $xaction); 178 - } 179 - 180 - protected function extractFilePHIDsFromCustomTransaction( 181 - PhabricatorLiskDAO $object, 182 - PhabricatorApplicationTransaction $xaction) { 183 - 184 - switch ($xaction->getTransactionType()) { 185 - case PhabricatorPasteTransaction::TYPE_CONTENT: 186 - return array($xaction->getNewValue()); 187 - } 188 - 189 - return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); 190 - } 191 - 192 - protected function shouldSendMail( 193 - PhabricatorLiskDAO $object, 194 - array $xactions) { 195 - foreach ($xactions as $xaction) { 196 - switch ($xaction->getTransactionType()) { 197 - case PhabricatorPasteTransaction::TYPE_CONTENT: 198 - return false; 199 - default: 200 - break; 201 - } 202 - } 203 40 return true; 204 41 } 205 42 ··· 256 93 PhabricatorLiskDAO $object, 257 94 array $xactions) { 258 95 return true; 259 - } 260 - 261 - protected function supportsSearch() { 262 - return false; 263 96 } 264 97 265 98 }
+3 -3
src/applications/paste/lipsum/PhabricatorPasteTestDataGenerator.php
··· 17 17 $xactions = array(); 18 18 19 19 $xactions[] = $this->newTransaction( 20 - PhabricatorPasteTransaction::TYPE_TITLE, 20 + PhabricatorPasteTitleTransaction::TRANSACTIONTYPE, 21 21 $name); 22 22 23 23 $xactions[] = $this->newTransaction( 24 - PhabricatorPasteTransaction::TYPE_LANGUAGE, 24 + PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE, 25 25 $language); 26 26 27 27 $xactions[] = $this->newTransaction( 28 - PhabricatorPasteTransaction::TYPE_CONTENT, 28 + PhabricatorPasteContentTransaction::TRANSACTIONTYPE, 29 29 $content); 30 30 31 31 $editor = id(new PhabricatorPasteEditor())
+2 -6
src/applications/paste/mail/PasteCreateMailReceiver.php
··· 24 24 $xactions = array(); 25 25 26 26 $xactions[] = id(new PhabricatorPasteTransaction()) 27 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_CONTENT) 27 + ->setTransactionType(PhabricatorPasteContentTransaction::TRANSACTIONTYPE) 28 28 ->setNewValue($mail->getCleanTextBody()); 29 29 30 30 $xactions[] = id(new PhabricatorPasteTransaction()) 31 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_TITLE) 31 + ->setTransactionType(PhabricatorPasteTitleTransaction::TRANSACTIONTYPE) 32 32 ->setNewValue($title); 33 - 34 - $xactions[] = id(new PhabricatorPasteTransaction()) 35 - ->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE) 36 - ->setNewValue(''); // auto-detect 37 33 38 34 $paste = PhabricatorPaste::initializeNewPaste($sender); 39 35
+6 -221
src/applications/paste/storage/PhabricatorPasteTransaction.php
··· 1 1 <?php 2 2 3 3 final class PhabricatorPasteTransaction 4 - extends PhabricatorApplicationTransaction { 5 - 6 - const TYPE_CONTENT = 'paste.create'; 7 - const TYPE_TITLE = 'paste.title'; 8 - const TYPE_LANGUAGE = 'paste.language'; 9 - const TYPE_STATUS = 'paste.status'; 4 + extends PhabricatorModularTransaction { 10 5 11 6 const MAILTAG_CONTENT = 'paste-content'; 12 7 const MAILTAG_OTHER = 'paste-other'; ··· 24 19 return new PhabricatorPasteTransactionComment(); 25 20 } 26 21 27 - public function getRequiredHandlePHIDs() { 28 - $phids = parent::getRequiredHandlePHIDs(); 29 - 30 - switch ($this->getTransactionType()) { 31 - case self::TYPE_CONTENT: 32 - $phids[] = $this->getObjectPHID(); 33 - break; 34 - } 35 - 36 - return $phids; 37 - } 38 - 39 - public function shouldHide() { 40 - $old = $this->getOldValue(); 41 - switch ($this->getTransactionType()) { 42 - case self::TYPE_TITLE: 43 - case self::TYPE_LANGUAGE: 44 - if ($old === null) { 45 - return true; 46 - } 47 - break; 48 - } 49 - return parent::shouldHide(); 50 - } 51 - 52 - public function getIcon() { 53 - switch ($this->getTransactionType()) { 54 - case self::TYPE_CONTENT: 55 - return 'fa-plus'; 56 - break; 57 - case self::TYPE_TITLE: 58 - case self::TYPE_LANGUAGE: 59 - return 'fa-pencil'; 60 - break; 61 - case self::TYPE_STATUS: 62 - $new = $this->getNewValue(); 63 - switch ($new) { 64 - case PhabricatorPaste::STATUS_ACTIVE: 65 - return 'fa-check'; 66 - case PhabricatorPaste::STATUS_ARCHIVED: 67 - return 'fa-ban'; 68 - } 69 - break; 70 - } 71 - return parent::getIcon(); 72 - } 73 - 74 - public function getTitle() { 75 - $author_phid = $this->getAuthorPHID(); 76 - $object_phid = $this->getObjectPHID(); 77 - 78 - $old = $this->getOldValue(); 79 - $new = $this->getNewValue(); 80 - 81 - $type = $this->getTransactionType(); 82 - switch ($type) { 83 - case PhabricatorTransactions::TYPE_CREATE: 84 - return pht( 85 - '%s created this paste.', 86 - $this->renderHandleLink($author_phid)); 87 - case self::TYPE_CONTENT: 88 - if ($old === null) { 89 - return pht( 90 - '%s created this paste.', 91 - $this->renderHandleLink($author_phid)); 92 - } else { 93 - return pht( 94 - '%s edited the content of this paste.', 95 - $this->renderHandleLink($author_phid)); 96 - } 97 - break; 98 - case self::TYPE_TITLE: 99 - return pht( 100 - '%s updated the paste\'s title to "%s".', 101 - $this->renderHandleLink($author_phid), 102 - $new); 103 - break; 104 - case self::TYPE_LANGUAGE: 105 - return pht( 106 - "%s updated the paste's language.", 107 - $this->renderHandleLink($author_phid)); 108 - break; 109 - case self::TYPE_STATUS: 110 - switch ($new) { 111 - case PhabricatorPaste::STATUS_ACTIVE: 112 - return pht( 113 - '%s activated this paste.', 114 - $this->renderHandleLink($author_phid)); 115 - case PhabricatorPaste::STATUS_ARCHIVED: 116 - return pht( 117 - '%s archived this paste.', 118 - $this->renderHandleLink($author_phid)); 119 - } 120 - break; 121 - 122 - } 123 - 124 - return parent::getTitle(); 125 - } 126 - 127 - public function getTitleForFeed() { 128 - $author_phid = $this->getAuthorPHID(); 129 - $object_phid = $this->getObjectPHID(); 130 - 131 - $old = $this->getOldValue(); 132 - $new = $this->getNewValue(); 133 - 134 - $type = $this->getTransactionType(); 135 - switch ($type) { 136 - case self::TYPE_CONTENT: 137 - if ($old === null) { 138 - return pht( 139 - '%s created %s.', 140 - $this->renderHandleLink($author_phid), 141 - $this->renderHandleLink($object_phid)); 142 - } else { 143 - return pht( 144 - '%s edited %s.', 145 - $this->renderHandleLink($author_phid), 146 - $this->renderHandleLink($object_phid)); 147 - } 148 - break; 149 - case self::TYPE_TITLE: 150 - return pht( 151 - '%s updated the title for %s.', 152 - $this->renderHandleLink($author_phid), 153 - $this->renderHandleLink($object_phid)); 154 - break; 155 - case self::TYPE_LANGUAGE: 156 - return pht( 157 - '%s updated the language for %s.', 158 - $this->renderHandleLink($author_phid), 159 - $this->renderHandleLink($object_phid)); 160 - break; 161 - case self::TYPE_STATUS: 162 - switch ($new) { 163 - case PhabricatorPaste::STATUS_ACTIVE: 164 - return pht( 165 - '%s activated %s.', 166 - $this->renderHandleLink($author_phid), 167 - $this->renderHandleLink($object_phid)); 168 - case PhabricatorPaste::STATUS_ARCHIVED: 169 - return pht( 170 - '%s archived %s.', 171 - $this->renderHandleLink($author_phid), 172 - $this->renderHandleLink($object_phid)); 173 - } 174 - break; 175 - } 176 - 177 - return parent::getTitleForFeed(); 178 - } 179 - 180 - public function getColor() { 181 - $old = $this->getOldValue(); 182 - $new = $this->getNewValue(); 183 - 184 - switch ($this->getTransactionType()) { 185 - case self::TYPE_CONTENT: 186 - return PhabricatorTransactions::COLOR_GREEN; 187 - case self::TYPE_STATUS: 188 - switch ($new) { 189 - case PhabricatorPaste::STATUS_ACTIVE: 190 - return 'green'; 191 - case PhabricatorPaste::STATUS_ARCHIVED: 192 - return 'indigo'; 193 - } 194 - break; 195 - } 196 - 197 - return parent::getColor(); 198 - } 199 - 200 - 201 - public function hasChangeDetails() { 202 - switch ($this->getTransactionType()) { 203 - case self::TYPE_CONTENT: 204 - return ($this->getOldValue() !== null); 205 - } 206 - 207 - return parent::hasChangeDetails(); 208 - } 209 - 210 - public function renderChangeDetails(PhabricatorUser $viewer) { 211 - switch ($this->getTransactionType()) { 212 - case self::TYPE_CONTENT: 213 - $old = $this->getOldValue(); 214 - $new = $this->getNewValue(); 215 - 216 - $files = id(new PhabricatorFileQuery()) 217 - ->setViewer($viewer) 218 - ->withPHIDs(array_filter(array($old, $new))) 219 - ->execute(); 220 - $files = mpull($files, null, 'getPHID'); 221 - 222 - $old_text = ''; 223 - if (idx($files, $old)) { 224 - $old_text = $files[$old]->loadFileData(); 225 - } 226 - 227 - $new_text = ''; 228 - if (idx($files, $new)) { 229 - $new_text = $files[$new]->loadFileData(); 230 - } 231 - 232 - return $this->renderTextCorpusChangeDetails( 233 - $viewer, 234 - $old_text, 235 - $new_text); 236 - } 237 - 238 - return parent::renderChangeDetails($viewer); 22 + public function getBaseTransactionClass() { 23 + return 'PhabricatorPasteTransactionType'; 239 24 } 240 25 241 26 public function getMailTags() { 242 27 $tags = array(); 243 28 switch ($this->getTransactionType()) { 244 - case self::TYPE_TITLE: 245 - case self::TYPE_CONTENT: 246 - case self::TYPE_LANGUAGE: 29 + case PhabricatorPasteTitleTransaction::TRANSACTIONTYPE: 30 + case PhabricatorPasteContentTransaction::TRANSACTIONTYPE: 31 + case PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE: 247 32 $tags[] = self::MAILTAG_CONTENT; 248 33 break; 249 34 case PhabricatorTransactions::TYPE_COMMENT:
+135
src/applications/paste/xaction/PhabricatorPasteContentTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorPasteContentTransaction 4 + extends PhabricatorPasteTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'paste.create'; 7 + 8 + private $fileName; 9 + 10 + public function generateOldValue($object) { 11 + return $object->getFilePHID(); 12 + } 13 + 14 + public function applyInternalEffects($object, $value) { 15 + $object->setFilePHID($value); 16 + } 17 + 18 + public function extractFilePHIDs($object, $value) { 19 + return array($value); 20 + } 21 + 22 + public function validateTransactions($object, array $xactions) { 23 + if ($object->getFilePHID() || $xactions) { 24 + return array(); 25 + } 26 + 27 + $error = $this->newError( 28 + pht('Required'), 29 + pht('You must provide content to create a paste.')); 30 + $error->setIsMissingFieldError(true); 31 + 32 + return array($error); 33 + } 34 + 35 + public function willApplyTransactions($object, array $xactions) { 36 + // Find the most user-friendly filename we can by examining the title of 37 + // the paste and the pending transactions. We'll use this if we create a 38 + // new file to store raw content later. 39 + 40 + $name = $object->getTitle(); 41 + if (!strlen($name)) { 42 + $name = 'paste.raw'; 43 + } 44 + 45 + $type_title = PhabricatorPasteTitleTransaction::TRANSACTIONTYPE; 46 + foreach ($xactions as $xaction) { 47 + if ($xaction->getTransactionType() == $type_title) { 48 + $name = $xaction->getNewValue(); 49 + } 50 + } 51 + 52 + $this->fileName = $name; 53 + } 54 + 55 + public function generateNewValue($object, $value) { 56 + // If this transaction does not really change the paste content, return 57 + // the current file PHID so this transaction no-ops. 58 + $old_content = $object->getRawContent(); 59 + if ($value === $old_content) { 60 + $file_phid = $object->getFilePHID(); 61 + if ($file_phid) { 62 + return $file_phid; 63 + } 64 + } 65 + 66 + $editor = $this->getEditor(); 67 + $actor = $editor->getActor(); 68 + 69 + $file = $this->newFileForPaste($actor, $this->fileName, $value); 70 + 71 + return $file->getPHID(); 72 + } 73 + 74 + private function newFileForPaste(PhabricatorUser $actor, $name, $data) { 75 + return PhabricatorFile::newFromFileData( 76 + $data, 77 + array( 78 + 'name' => $name, 79 + 'mime-type' => 'text/plain; charset=utf-8', 80 + 'authorPHID' => $actor->getPHID(), 81 + 'viewPolicy' => PhabricatorPolicies::POLICY_NOONE, 82 + 'editPolicy' => PhabricatorPolicies::POLICY_NOONE, 83 + )); 84 + } 85 + 86 + public function getIcon() { 87 + return 'fa-plus'; 88 + } 89 + 90 + public function getTitle() { 91 + return pht( 92 + '%s edited the content of this paste.', 93 + $this->renderAuthor()); 94 + } 95 + 96 + public function getTitleForFeed() { 97 + return pht( 98 + '%s edited %s.', 99 + $this->renderAuthor(), 100 + $this->renderObject()); 101 + } 102 + 103 + public function hasChangeDetailView() { 104 + return true; 105 + } 106 + 107 + public function newChangeDetailView() { 108 + $viewer = $this->getViewer(); 109 + 110 + $old = $this->getOldValue(); 111 + $new = $this->getNewValue(); 112 + 113 + $files = id(new PhabricatorFileQuery()) 114 + ->setViewer($viewer) 115 + ->withPHIDs(array($old, $new)) 116 + ->execute(); 117 + $files = mpull($files, null, 'getPHID'); 118 + 119 + $old_text = ''; 120 + if (idx($files, $old)) { 121 + $old_text = $files[$old]->loadFileData(); 122 + } 123 + 124 + $new_text = ''; 125 + if (idx($files, $new)) { 126 + $new_text = $files[$new]->loadFileData(); 127 + } 128 + 129 + return id(new PhabricatorApplicationTransactionTextDiffDetailView()) 130 + ->setViewer($viewer) 131 + ->setOldText($old_text) 132 + ->setNewText($new_text); 133 + } 134 + 135 + }
+29
src/applications/paste/xaction/PhabricatorPasteLanguageTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorPasteLanguageTransaction 4 + extends PhabricatorPasteTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'paste.language'; 7 + 8 + public function generateOldValue($object) { 9 + return $object->getLanguage(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $object->setLanguage($value); 14 + } 15 + 16 + public function getTitle() { 17 + return pht( 18 + "%s updated the paste's language.", 19 + $this->renderAuthor()); 20 + } 21 + 22 + public function getTitleForFeed() { 23 + return pht( 24 + '%s updated the language for %s.', 25 + $this->renderAuthor(), 26 + $this->renderObject()); 27 + } 28 + 29 + }
+62
src/applications/paste/xaction/PhabricatorPasteStatusTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorPasteStatusTransaction 4 + extends PhabricatorPasteTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'paste.status'; 7 + 8 + public function generateOldValue($object) { 9 + return $object->getStatus(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $object->setStatus($value); 14 + } 15 + 16 + private function isActivate() { 17 + return ($this->getNewValue() == PhabricatorPaste::STATUS_ARCHIVED); 18 + } 19 + 20 + public function getIcon() { 21 + if ($this->isActivate()) { 22 + return 'fa-check'; 23 + } else { 24 + return 'fa-ban'; 25 + } 26 + } 27 + 28 + public function getColor() { 29 + if ($this->isActivate()) { 30 + return 'green'; 31 + } else { 32 + return 'indigo'; 33 + } 34 + } 35 + 36 + public function getTitle() { 37 + if ($this->isActivate()) { 38 + return pht( 39 + '%s activated this paste.', 40 + $this->renderAuthor()); 41 + } else { 42 + return pht( 43 + '%s archived this paste.', 44 + $this->renderAuthor()); 45 + } 46 + } 47 + 48 + public function getTitleForFeed() { 49 + if ($this->isActivate()) { 50 + return pht( 51 + '%s activated %s.', 52 + $this->renderAuthor(), 53 + $this->renderObject()); 54 + } else { 55 + return pht( 56 + '%s archived %s.', 57 + $this->renderAuthor(), 58 + $this->renderObject()); 59 + } 60 + } 61 + 62 + }
+33
src/applications/paste/xaction/PhabricatorPasteTitleTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorPasteTitleTransaction 4 + extends PhabricatorPasteTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'paste.title'; 7 + 8 + public function generateOldValue($object) { 9 + return $object->getTitle(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $object->setTitle($value); 14 + } 15 + 16 + public function getTitle() { 17 + return pht( 18 + '%s updated the paste\'s title from "%s" to "%s".', 19 + $this->renderAuthor(), 20 + $this->getOldValue(), 21 + $this->getNewValue()); 22 + } 23 + 24 + public function getTitleForFeed() { 25 + return pht( 26 + '%s updated the title for %s from "%s" to "%s".', 27 + $this->renderAuthor(), 28 + $this->renderObject(), 29 + $this->getOldValue(), 30 + $this->getNewValue()); 31 + } 32 + 33 + }
+4
src/applications/paste/xaction/PhabricatorPasteTransactionType.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorPasteTransactionType 4 + extends PhabricatorModularTransactionType {}
+107 -7
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 68 68 private $mailCCPHIDs = array(); 69 69 private $feedNotifyPHIDs = array(); 70 70 private $feedRelatedPHIDs = array(); 71 + private $modularTypes; 71 72 72 73 const STORAGE_ENCODING_BINARY = 'binary'; 73 74 ··· 285 286 $types[] = PhabricatorTransactions::TYPE_SPACE; 286 287 } 287 288 289 + $template = $this->object->getApplicationTransactionTemplate(); 290 + if ($template instanceof PhabricatorModularTransaction) { 291 + $xtypes = $template->newModularTransactionTypes(); 292 + foreach ($xtypes as $xtype) { 293 + $types[] = $xtype->getTransactionTypeConstant(); 294 + } 295 + } 296 + 288 297 return $types; 289 298 } 290 299 ··· 304 313 private function getTransactionOldValue( 305 314 PhabricatorLiskDAO $object, 306 315 PhabricatorApplicationTransaction $xaction) { 307 - switch ($xaction->getTransactionType()) { 316 + 317 + $type = $xaction->getTransactionType(); 318 + 319 + $xtype = $this->getModularTransactionType($type); 320 + if ($xtype) { 321 + return $xtype->generateOldValue($object); 322 + } 323 + 324 + switch ($type) { 308 325 case PhabricatorTransactions::TYPE_CREATE: 309 326 return null; 310 327 case PhabricatorTransactions::TYPE_SUBSCRIBERS: ··· 374 391 private function getTransactionNewValue( 375 392 PhabricatorLiskDAO $object, 376 393 PhabricatorApplicationTransaction $xaction) { 377 - switch ($xaction->getTransactionType()) { 394 + 395 + $type = $xaction->getTransactionType(); 396 + 397 + $xtype = $this->getModularTransactionType($type); 398 + if ($xtype) { 399 + return $xtype->generateNewValue($object, $xaction->getNewValue()); 400 + } 401 + 402 + switch ($type) { 378 403 case PhabricatorTransactions::TYPE_CREATE: 379 404 return null; 380 405 case PhabricatorTransactions::TYPE_SUBSCRIBERS: ··· 496 521 PhabricatorLiskDAO $object, 497 522 PhabricatorApplicationTransaction $xaction) { 498 523 499 - switch ($xaction->getTransactionType()) { 524 + $type = $xaction->getTransactionType(); 525 + 526 + $xtype = $this->getModularTransactionType($type); 527 + if ($xtype) { 528 + return $xtype->applyInternalEffects($object, $xaction->getNewValue()); 529 + } 530 + 531 + switch ($type) { 500 532 case PhabricatorTransactions::TYPE_CUSTOMFIELD: 501 533 $field = $this->getCustomFieldForTransaction($object, $xaction); 502 534 return $field->applyApplicationTransactionInternalEffects($xaction); ··· 520 552 private function applyExternalEffects( 521 553 PhabricatorLiskDAO $object, 522 554 PhabricatorApplicationTransaction $xaction) { 523 - switch ($xaction->getTransactionType()) { 555 + 556 + $type = $xaction->getTransactionType(); 557 + 558 + $xtype = $this->getModularTransactionType($type); 559 + if ($xtype) { 560 + return $xtype->applyExternalEffects($object, $xaction->getNewValue()); 561 + } 562 + 563 + switch ($type) { 524 564 case PhabricatorTransactions::TYPE_SUBSCRIBERS: 525 565 $subeditor = id(new PhabricatorSubscriptionsEditor()) 526 566 ->setObject($object) ··· 801 841 if ($errors) { 802 842 throw new PhabricatorApplicationTransactionValidationException($errors); 803 843 } 844 + 845 + $this->willApplyTransactions($object, $xactions); 804 846 805 847 if ($object->getID()) { 806 848 foreach ($xactions as $xaction) { ··· 2006 2048 array $xactions) { 2007 2049 2008 2050 $errors = array(); 2051 + 2052 + $xtype = $this->getModularTransactionType($type); 2053 + if ($xtype) { 2054 + $errors[] = $xtype->validateTransactions($object, $xactions); 2055 + } 2056 + 2009 2057 switch ($type) { 2010 2058 case PhabricatorTransactions::TYPE_VIEW_POLICY: 2011 2059 $errors[] = $this->validatePolicyTransaction( ··· 3129 3177 } 3130 3178 3131 3179 foreach ($xactions as $xaction) { 3132 - $phids[] = $this->extractFilePHIDsFromCustomTransaction( 3133 - $object, 3134 - $xaction); 3180 + $type = $xaction->getTransactionType(); 3181 + 3182 + $xtype = $this->getModularTransactionType($type); 3183 + if ($xtype) { 3184 + $phids[] = $xtype->extractFilePHIDs($object, $xaction->getNewValue()); 3185 + } else { 3186 + $phids[] = $this->extractFilePHIDsFromCustomTransaction( 3187 + $object, 3188 + $xaction); 3189 + } 3135 3190 } 3136 3191 3137 3192 $phids = array_unique(array_filter(array_mergev($phids))); ··· 3692 3747 $proxy_phids); 3693 3748 } 3694 3749 3750 + private function getModularTransactionTypes() { 3751 + if ($this->modularTypes === null) { 3752 + $template = $this->object->getApplicationTransactionTemplate(); 3753 + if ($template instanceof PhabricatorModularTransaction) { 3754 + $xtypes = $template->newModularTransactionTypes(); 3755 + foreach ($xtypes as $key => $xtype) { 3756 + $xtype = clone $xtype; 3757 + $xtype->setEditor($this); 3758 + $xtypes[$key] = $xtype; 3759 + } 3760 + } else { 3761 + $xtypes = array(); 3762 + } 3763 + 3764 + $this->modularTypes = $xtypes; 3765 + } 3766 + 3767 + return $this->modularTypes; 3768 + } 3769 + 3770 + private function getModularTransactionType($type) { 3771 + $types = $this->getModularTransactionTypes(); 3772 + return idx($types, $type); 3773 + } 3774 + 3775 + private function willApplyTransactions($object, array $xactions) { 3776 + foreach ($xactions as $xaction) { 3777 + $type = $xaction->getTransactionType(); 3778 + 3779 + $xtype = $this->getModularTransactionType($type); 3780 + if (!$xtype) { 3781 + continue; 3782 + } 3783 + 3784 + $xtype->willApplyTransactions($object, $xactions); 3785 + } 3786 + } 3787 + 3788 + public function getCreateObjectTitle($author, $object) { 3789 + return pht('%s created this object.', $author); 3790 + } 3791 + 3792 + public function getCreateObjectTitleForFeed($author, $object) { 3793 + return pht('%s created an object: %s.', $author, $object); 3794 + } 3695 3795 3696 3796 }
+1
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 280 280 $new = $this->getNewValue(); 281 281 282 282 $phids[] = array($this->getAuthorPHID()); 283 + $phids[] = array($this->getObjectPHID()); 283 284 switch ($this->getTransactionType()) { 284 285 case PhabricatorTransactions::TYPE_CUSTOMFIELD: 285 286 $field = $this->getTransactionCustomField();
+138
src/applications/transactions/storage/PhabricatorModularTransaction.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorModularTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + private $implementation; 7 + 8 + abstract public function getBaseTransactionClass(); 9 + 10 + final protected function getTransactionImplementation() { 11 + if (!$this->implementation) { 12 + $this->implementation = $this->newTransactionImplementation(); 13 + } 14 + 15 + return $this->implementation; 16 + } 17 + 18 + public function newModularTransactionTypes() { 19 + $base_class = $this->getBaseTransactionClass(); 20 + 21 + $types = id(new PhutilClassMapQuery()) 22 + ->setAncestorClass($base_class) 23 + ->setUniqueMethod('getTransactionTypeConstant') 24 + ->execute(); 25 + 26 + // Add core transaction types. 27 + $types += id(new PhutilClassMapQuery()) 28 + ->setAncestorClass('PhabricatorCoreTransactionType') 29 + ->setUniqueMethod('getTransactionTypeConstant') 30 + ->execute(); 31 + 32 + return $types; 33 + } 34 + 35 + private function newTransactionImplementation() { 36 + $types = $this->newModularTransactionTypes(); 37 + 38 + $key = $this->getTransactionType(); 39 + 40 + if (empty($types[$key])) { 41 + $type = new PhabricatorCoreVoidTransaction(); 42 + } else { 43 + $type = clone $types[$key]; 44 + } 45 + 46 + $type->setStorage($this); 47 + 48 + return $type; 49 + } 50 + 51 + final public function generateOldValue($object) { 52 + return $this->getTransactionImplementation()->generateOldValue($object); 53 + } 54 + 55 + final public function generateNewValue($object) { 56 + return $this->getTransactionImplementation() 57 + ->generateNewValue($object, $this->getNewValue()); 58 + } 59 + 60 + final public function willApplyTransactions($object, array $xactions) { 61 + return $this->getTransactionImplementation() 62 + ->willApplyTransactions($object, $xactions); 63 + } 64 + 65 + final public function applyInternalEffects($object) { 66 + return $this->getTransactionImplementation() 67 + ->applyInternalEffects($object); 68 + } 69 + 70 + final public function applyExternalEffects($object) { 71 + return $this->getTransactionImplementation() 72 + ->applyExternalEffects($object); 73 + } 74 + 75 + final public function shouldHide() { 76 + if ($this->getTransactionImplementation()->shouldHide()) { 77 + return true; 78 + } 79 + 80 + return parent::shouldHide(); 81 + } 82 + 83 + final public function getIcon() { 84 + $icon = $this->getTransactionImplementation()->getIcon(); 85 + if ($icon !== null) { 86 + return $icon; 87 + } 88 + 89 + return parent::getIcon(); 90 + } 91 + 92 + final public function getTitle() { 93 + $title = $this->getTransactionImplementation()->getTitle(); 94 + if ($title !== null) { 95 + return $title; 96 + } 97 + 98 + return parent::getTitle(); 99 + } 100 + 101 + final public function getTitleForFeed() { 102 + $title = $this->getTransactionImplementation()->getTitleForFeed(); 103 + if ($title !== null) { 104 + return $title; 105 + } 106 + 107 + return $this->getTitle(); 108 + } 109 + 110 + final public function getColor() { 111 + $color = $this->getTransactionImplementation()->getColor(); 112 + if ($color !== null) { 113 + return $color; 114 + } 115 + 116 + return parent::getColor(); 117 + } 118 + 119 + final public function hasChangeDetails() { 120 + if ($this->getTransactionImplementation()->hasChangeDetailView()) { 121 + return true; 122 + } 123 + 124 + return parent::hasChangeDetails(); 125 + } 126 + 127 + final public function renderChangeDetails(PhabricatorUser $viewer) { 128 + $impl = $this->getTransactionImplementation(); 129 + $impl->setViewer($viewer); 130 + $view = $impl->newChangeDetailView(); 131 + if ($view !== null) { 132 + return $view; 133 + } 134 + 135 + return parent::renderChangeDetails($viewer); 136 + } 137 + 138 + }
+140
src/applications/transactions/storage/PhabricatorModularTransactionType.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorModularTransactionType 4 + extends Phobject { 5 + 6 + private $storage; 7 + private $viewer; 8 + private $editor; 9 + 10 + final public function getTransactionTypeConstant() { 11 + return $this->getPhobjectClassConstant('TRANSACTIONTYPE'); 12 + } 13 + 14 + public function generateOldValue($object) { 15 + throw new PhutilMethodNotImplementedException(); 16 + } 17 + 18 + public function generateNewValue($object, $value) { 19 + return $value; 20 + } 21 + 22 + public function validateTransactions($object, array $xactions) { 23 + return array(); 24 + } 25 + 26 + public function willApplyTransactions($object, array $xactions) { 27 + return; 28 + } 29 + 30 + public function applyInternalEffects($object, $value) { 31 + return; 32 + } 33 + 34 + public function applyExternalEffects($object, $value) { 35 + return; 36 + } 37 + 38 + public function extractFilePHIDs($object, $value) { 39 + return array(); 40 + } 41 + 42 + public function shouldHide() { 43 + return false; 44 + } 45 + 46 + public function getIcon() { 47 + return null; 48 + } 49 + 50 + public function getTitle() { 51 + return null; 52 + } 53 + 54 + public function getTitleForFeed() { 55 + return null; 56 + } 57 + 58 + public function getColor() { 59 + return null; 60 + } 61 + 62 + public function hasChangeDetailView() { 63 + return false; 64 + } 65 + 66 + public function newChangeDetailView() { 67 + throw new PhutilMethodNotImplementedException(); 68 + } 69 + 70 + final public function setStorage( 71 + PhabricatorApplicationTransaction $xaction) { 72 + $this->storage = $xaction; 73 + return $this; 74 + } 75 + 76 + private function getStorage() { 77 + return $this->storage; 78 + } 79 + 80 + final public function setViewer(PhabricatorUser $viewer) { 81 + $this->viewer = $viewer; 82 + return $this; 83 + } 84 + 85 + final protected function getViewer() { 86 + return $this->viewer; 87 + } 88 + 89 + final public function setEditor( 90 + PhabricatorApplicationTransactionEditor $editor) { 91 + $this->editor = $editor; 92 + return $this; 93 + } 94 + 95 + final protected function getEditor() { 96 + if (!$this->editor) { 97 + throw new PhutilInvalidStateException('setEditor'); 98 + } 99 + return $this->editor; 100 + } 101 + 102 + final protected function getAuthorPHID() { 103 + return $this->getStorage()->getAuthorPHID(); 104 + } 105 + 106 + final protected function getObjectPHID() { 107 + return $this->getStorage()->getObjectPHID(); 108 + } 109 + 110 + final protected function getObject() { 111 + return $this->getStorage()->getObject(); 112 + } 113 + 114 + final protected function getOldValue() { 115 + return $this->getStorage()->getOldValue(); 116 + } 117 + 118 + final protected function getNewValue() { 119 + return $this->getStorage()->getNewValue(); 120 + } 121 + 122 + final protected function renderAuthor() { 123 + $author_phid = $this->getAuthorPHID(); 124 + return $this->getStorage()->renderHandleLink($author_phid); 125 + } 126 + 127 + final protected function renderObject() { 128 + $object_phid = $this->getObjectPHID(); 129 + return $this->getStorage()->renderHandleLink($object_phid); 130 + } 131 + 132 + final protected function newError($title, $message, $xaction = null) { 133 + return new PhabricatorApplicationTransactionValidationError( 134 + $this->getTransactionTypeConstant(), 135 + $title, 136 + $message, 137 + $xaction); 138 + } 139 + 140 + }
+30
src/applications/transactions/xaction/PhabricatorCoreCreateTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorCoreCreateTransaction 4 + extends PhabricatorCoreTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'core:create'; 7 + 8 + public function generateOldValue($object) { 9 + return null; 10 + } 11 + 12 + public function getTitle() { 13 + $editor = $this->getObject()->getApplicationTransactionEditor(); 14 + 15 + $author = $this->renderAuthor(); 16 + $object = $this->renderObject(); 17 + 18 + return $editor->getCreateObjectTitle($author, $object); 19 + } 20 + 21 + public function getTitleForFeed() { 22 + $editor = $this->getObject()->getApplicationTransactionEditor(); 23 + 24 + $author = $this->renderAuthor(); 25 + $object = $this->renderObject(); 26 + 27 + return $editor->getCreateObjectTitleForFeed($author, $object); 28 + } 29 + 30 + }
+4
src/applications/transactions/xaction/PhabricatorCoreTransactionType.php
··· 1 + <?php 2 + 3 + abstract class PhabricatorCoreTransactionType 4 + extends PhabricatorModularTransactionType {}
+8
src/applications/transactions/xaction/PhabricatorCoreVoidTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorCoreVoidTransaction 4 + extends PhabricatorModularTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'core.void'; 7 + 8 + }