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

Use transactions to show edit history for Configuration

Summary: Use ApplicationTransactions in Config to create an edit history. Resolves T2256.

Test Plan: {F28477}

Reviewers: btrahan, codeblock

Reviewed By: codeblock

CC: aran

Maniphest Tasks: T2256

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

+281 -17
+20
resources/sql/patches/20130101.confxaction.sql
··· 1 + CREATE TABLE {$NAMESPACE}_config.config_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;
+6
src/__phutil_library_map__.php
··· 687 687 'PhabricatorConfigDefaultSource' => 'infrastructure/env/PhabricatorConfigDefaultSource.php', 688 688 'PhabricatorConfigDictionarySource' => 'infrastructure/env/PhabricatorConfigDictionarySource.php', 689 689 'PhabricatorConfigEditController' => 'applications/config/controller/PhabricatorConfigEditController.php', 690 + 'PhabricatorConfigEditor' => 'applications/config/editor/PhabricatorConfigEditor.php', 690 691 'PhabricatorConfigEntry' => 'applications/config/storage/PhabricatorConfigEntry.php', 691 692 'PhabricatorConfigEntryDAO' => 'applications/config/storage/PhabricatorConfigEntryDAO.php', 692 693 'PhabricatorConfigFileSource' => 'infrastructure/env/PhabricatorConfigFileSource.php', ··· 701 702 'PhabricatorConfigProxySource' => 'infrastructure/env/PhabricatorConfigProxySource.php', 702 703 'PhabricatorConfigSource' => 'infrastructure/env/PhabricatorConfigSource.php', 703 704 'PhabricatorConfigStackSource' => 'infrastructure/env/PhabricatorConfigStackSource.php', 705 + 'PhabricatorConfigTransaction' => 'applications/config/storage/PhabricatorConfigTransaction.php', 706 + 'PhabricatorConfigTransactionQuery' => 'applications/config/query/PhabricatorConfigTransactionQuery.php', 704 707 'PhabricatorContentSource' => 'applications/metamta/contentsource/PhabricatorContentSource.php', 705 708 'PhabricatorContentSourceView' => 'applications/metamta/contentsource/PhabricatorContentSourceView.php', 706 709 'PhabricatorController' => 'applications/base/controller/PhabricatorController.php', ··· 2023 2026 'PhabricatorConfigDefaultSource' => 'PhabricatorConfigProxySource', 2024 2027 'PhabricatorConfigDictionarySource' => 'PhabricatorConfigSource', 2025 2028 'PhabricatorConfigEditController' => 'PhabricatorConfigController', 2029 + 'PhabricatorConfigEditor' => 'PhabricatorApplicationTransactionEditor', 2026 2030 'PhabricatorConfigEntry' => 'PhabricatorConfigEntryDAO', 2027 2031 'PhabricatorConfigEntryDAO' => 'PhabricatorLiskDAO', 2028 2032 'PhabricatorConfigFileSource' => 'PhabricatorConfigProxySource', ··· 2040 2044 ), 2041 2045 'PhabricatorConfigProxySource' => 'PhabricatorConfigSource', 2042 2046 'PhabricatorConfigStackSource' => 'PhabricatorConfigSource', 2047 + 'PhabricatorConfigTransaction' => 'PhabricatorApplicationTransaction', 2048 + 'PhabricatorConfigTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 2043 2049 'PhabricatorContentSourceView' => 'AphrontView', 2044 2050 'PhabricatorController' => 'AphrontController', 2045 2051 'PhabricatorCountdownController' => 'PhabricatorController',
+54 -14
src/applications/config/controller/PhabricatorConfigEditController.php
··· 61 61 $errors = array(); 62 62 if ($request->isFormPost()) { 63 63 64 - list($e_value, $value_errors, $display_value) = $this->readRequest( 64 + $result = $this->readRequest( 65 65 $option, 66 - $config_entry, 67 66 $request); 68 67 68 + list($e_value, $value_errors, $display_value, $xaction) = $result; 69 69 $errors = array_merge($errors, $value_errors); 70 70 71 71 if (!$errors) { 72 - $config_entry->save(); 72 + 73 + $editor = id(new PhabricatorConfigEditor()) 74 + ->setActor($user) 75 + ->setContinueOnNoEffect(true) 76 + ->setContentSource( 77 + PhabricatorContentSource::newForSource( 78 + PhabricatorContentSource::SOURCE_WEB, 79 + array( 80 + 'ip' => $request->getRemoteAddr(), 81 + ))) 82 + ->applyTransactions($config_entry, array($xaction)); 83 + 73 84 return id(new AphrontRedirectResponse())->setURI($done_uri); 74 85 } 75 86 } else { ··· 148 159 ->setName($this->key) 149 160 ->setHref('/config/edit/'.$this->key)); 150 161 162 + 163 + $xactions = id(new PhabricatorConfigTransactionQuery()) 164 + ->withObjectPHIDs(array($config_entry->getPHID())) 165 + ->setViewer($user) 166 + ->execute(); 167 + 168 + $xaction_view = id(new PhabricatorApplicationTransactionView()) 169 + ->setUser($user) 170 + ->setTransactions($xactions); 171 + 151 172 return $this->buildApplicationPage( 152 173 array( 153 174 $crumbs, 154 175 id(new PhabricatorHeaderView())->setHeader($title), 155 176 $error_view, 156 177 $form, 178 + $xaction_view, 157 179 ), 158 180 array( 159 181 'title' => $title, ··· 163 185 164 186 private function readRequest( 165 187 PhabricatorConfigOption $option, 166 - PhabricatorConfigEntry $entry, 167 188 AphrontRequest $request) { 168 189 190 + $xaction = new PhabricatorConfigTransaction(); 191 + $xaction->setTransactionType(PhabricatorConfigTransaction::TYPE_EDIT); 192 + 169 193 $e_value = null; 170 194 $errors = array(); 171 195 172 196 $value = $request->getStr('value'); 173 197 if (!strlen($value)) { 174 198 $value = null; 175 - $entry->setValue($value); 176 - $entry->setIsDeleted(true); 177 - return array($e_value, $errors, $value); 178 - } else { 179 - $entry->setIsDeleted(false); 199 + 200 + $xaction->setNewValue( 201 + array( 202 + 'deleted' => true, 203 + 'value' => null, 204 + )); 205 + 206 + return array($e_value, $errors, $value, $xaction); 180 207 } 181 208 182 209 $type = $option->getType(); 210 + $set_value = null; 211 + 183 212 switch ($type) { 184 213 case 'int': 185 214 if (preg_match('/^-?[0-9]+$/', trim($value))) { 186 - $entry->setValue((int)$value); 215 + $set_value = (int)$value; 187 216 } else { 188 217 $e_value = pht('Invalid'); 189 218 $errors[] = pht('Value must be an integer.'); 190 219 } 191 220 break; 192 221 case 'string': 222 + $set_value = (string)$value; 193 223 break; 194 224 case 'bool': 195 225 switch ($value) { 196 226 case 'true': 197 - $entry->setValue(true); 227 + $set_value = true; 198 228 break; 199 229 case 'false': 200 - $entry->setValue(false); 230 + $set_value = false; 201 231 break; 202 232 default: 203 233 $e_value = pht('Invalid'); ··· 212 242 $errors[] = pht( 213 243 'The given value must be valid JSON. This means, among '. 214 244 'other things, that you must wrap strings in double-quotes.'); 215 - $entry->setValue($json); 245 + $set_value = $json; 216 246 } 217 247 break; 218 248 } 219 249 220 - return array($e_value, $errors, $value); 250 + if (!$errors) { 251 + $xaction->setNewValue( 252 + array( 253 + 'deleted' => false, 254 + 'value' => $set_value, 255 + )); 256 + } else { 257 + $xaction = null; 258 + } 259 + 260 + return array($e_value, $errors, $value, $xaction); 221 261 } 222 262 223 263 private function getDisplayValue(
+90
src/applications/config/editor/PhabricatorConfigEditor.php
··· 1 + <?php 2 + 3 + final class PhabricatorConfigEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getTransactionTypes() { 7 + $types = parent::getTransactionTypes(); 8 + 9 + $types[] = PhabricatorConfigTransaction::TYPE_EDIT; 10 + 11 + return $types; 12 + } 13 + 14 + protected function getCustomTransactionOldValue( 15 + PhabricatorLiskDAO $object, 16 + PhabricatorApplicationTransaction $xaction) { 17 + 18 + switch ($xaction->getTransactionType()) { 19 + case PhabricatorConfigTransaction::TYPE_EDIT: 20 + return array( 21 + 'deleted' => (bool)$object->getIsDeleted(), 22 + 'value' => $object->getValue(), 23 + ); 24 + } 25 + } 26 + 27 + protected function getCustomTransactionNewValue( 28 + PhabricatorLiskDAO $object, 29 + PhabricatorApplicationTransaction $xaction) { 30 + 31 + switch ($xaction->getTransactionType()) { 32 + case PhabricatorConfigTransaction::TYPE_EDIT: 33 + return $xaction->getNewValue(); 34 + } 35 + } 36 + 37 + protected function applyCustomInternalTransaction( 38 + PhabricatorLiskDAO $object, 39 + PhabricatorApplicationTransaction $xaction) { 40 + 41 + switch ($xaction->getTransactionType()) { 42 + case PhabricatorConfigTransaction::TYPE_EDIT: 43 + $v = $xaction->getNewValue(); 44 + 45 + $object->setIsDeleted($v['deleted']); 46 + $object->setValue($v['value']); 47 + break; 48 + } 49 + } 50 + 51 + protected function applyCustomExternalTransaction( 52 + PhabricatorLiskDAO $object, 53 + PhabricatorApplicationTransaction $xaction) { 54 + return; 55 + } 56 + 57 + protected function mergeTransactions( 58 + PhabricatorApplicationTransaction $u, 59 + PhabricatorApplicationTransaction $v) { 60 + 61 + $type = $u->getTransactionType(); 62 + switch ($type) { 63 + case PhabricatorConfigTransaction::TYPE_EDIT: 64 + return $v; 65 + } 66 + 67 + return parent::mergeTransactions($u, $v); 68 + } 69 + 70 + protected function transactionHasEffect( 71 + PhabricatorLiskDAO $object, 72 + PhabricatorApplicationTransaction $xaction) { 73 + 74 + $old = $xaction->getOldValue(); 75 + $new = $xaction->getNewValue(); 76 + 77 + $type = $xaction->getTransactionType(); 78 + switch ($type) { 79 + case PhabricatorConfigTransaction::TYPE_EDIT: 80 + // If an edit deletes an already-deleted entry, no-op it. 81 + if (idx($old, 'deleted') && idx($new, 'deleted')) { 82 + return false; 83 + } 84 + break; 85 + } 86 + 87 + return parent::transactionHasEffect($object, $xaction); 88 + } 89 + 90 + }
+10
src/applications/config/query/PhabricatorConfigTransactionQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorConfigTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + protected function getTemplateApplicationTransaction() { 7 + return new PhabricatorConfigTransaction(); 8 + } 9 + 10 + }
+94
src/applications/config/storage/PhabricatorConfigTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorConfigTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + const TYPE_EDIT = 'config:edit'; 7 + 8 + public function getApplicationName() { 9 + return 'config'; 10 + } 11 + 12 + public function getApplicationTransactionType() { 13 + return PhabricatorPHIDConstants::PHID_TYPE_CONF; 14 + } 15 + 16 + public function getApplicationTransactionCommentObject() { 17 + return null; 18 + } 19 + 20 + public function getApplicationObjectTypeName() { 21 + return pht('config'); 22 + } 23 + 24 + 25 + public function getTitle() { 26 + $author_phid = $this->getAuthorPHID(); 27 + 28 + $old = $this->getOldValue(); 29 + $new = $this->getNewValue(); 30 + 31 + switch ($this->getTransactionType()) { 32 + case self::TYPE_EDIT: 33 + 34 + // TODO: After T2213 show the actual values too; for now, we don't 35 + // have the tools to do it without making a bit of a mess of it. 36 + 37 + $old_del = idx($old, 'deleted'); 38 + $new_del = idx($new, 'deleted'); 39 + if ($old_del && !$new_del) { 40 + return pht( 41 + '%s created this configuration entry.', 42 + $this->renderHandleLink($author_phid)); 43 + } else if (!$old_del && $new_del) { 44 + return pht( 45 + '%s deleted this configuration entry.', 46 + $this->renderHandleLink($author_phid)); 47 + } else if ($old_del && $new_del) { 48 + // This is a bug. 49 + return pht( 50 + '%s deleted this configuration entry (again?).', 51 + $this->renderHandleLink($author_phid)); 52 + } else { 53 + return pht( 54 + '%s edited this configuration entry.', 55 + $this->renderHandleLink($author_phid)); 56 + } 57 + break; 58 + } 59 + 60 + return parent::getTitle(); 61 + } 62 + 63 + 64 + public function getIcon() { 65 + switch ($this->getTransactionType()) { 66 + case self::TYPE_EDIT: 67 + return 'edit'; 68 + } 69 + 70 + return parent::getIcon(); 71 + } 72 + 73 + public function getColor() { 74 + $old = $this->getOldValue(); 75 + $new = $this->getNewValue(); 76 + 77 + switch ($this->getTransactionType()) { 78 + case self::TYPE_EDIT: 79 + $old_del = idx($old, 'deleted'); 80 + $new_del = idx($new, 'deleted'); 81 + 82 + if ($old_del && !$new_del) { 83 + return PhabricatorTransactions::COLOR_GREEN; 84 + } else if (!$old_del && $new_del) { 85 + return PhabricatorTransactions::COLOR_RED; 86 + } else { 87 + return PhabricatorTransactions::COLOR_BLUE; 88 + } 89 + break; 90 + } 91 + } 92 + 93 + } 94 +
+1
src/applications/macro/editor/PhabricatorMacroEditor.php
··· 6 6 public function getTransactionTypes() { 7 7 $types = parent::getTransactionTypes(); 8 8 9 + $types[] = PhabricatorTransactions::TYPE_COMMENT; 9 10 $types[] = PhabricatorMacroTransactionType::TYPE_NAME; 10 11 $types[] = PhabricatorMacroTransactionType::TYPE_DISABLED; 11 12 $types[] = PhabricatorMacroTransactionType::TYPE_FILE;
+1
src/applications/pholio/editor/PholioMockEditor.php
··· 8 8 public function getTransactionTypes() { 9 9 $types = parent::getTransactionTypes(); 10 10 11 + $types[] = PhabricatorTransactions::TYPE_COMMENT; 11 12 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 12 13 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 13 14
+1 -3
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 58 58 } 59 59 60 60 public function getTransactionTypes() { 61 - $types = array( 62 - PhabricatorTransactions::TYPE_COMMENT, 63 - ); 61 + $types = array(); 64 62 65 63 if ($this->object instanceof PhabricatorSubscribableInterface) { 66 64 $types[] = PhabricatorTransactions::TYPE_SUBSCRIBERS;
+4
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 1072 1072 'type' => 'sql', 1073 1073 'name' => $this->getPatchPath('20121226.config.sql'), 1074 1074 ), 1075 + '20130101.confxaction.sql' => array( 1076 + 'type' => 'sql', 1077 + 'name' => $this->getPatchPath('20130101.confxaction.sql'), 1078 + ), 1075 1079 ); 1076 1080 } 1077 1081