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

Prepare UserPreferences for transactions

Summary:
Ref T4103. This give preferences a PHID, policy/transaction interfaces, a transaction table, and a Query class.

This doesn't actually change how they're edited, yet.

Test Plan:
- Ran migrations.
- Inspected database for date created, date modified, PHIDs.
- Changed some of my preferences.
- Deleted a user's preferences, verified they reset properly.
- Set some preferences as a new user, got a new row.
- Destroyed a user, verified their preferences were destroyed.
- Sent Conpherence messages.
- Send mail.
- Tried to edit another user's settings.
- Tried to edit a bot's settings as a non-admin.
- Edited a bot's settings as an admin (technically, none of the editable settings are actually stored in the settings table, currently).

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4103

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

+379 -19
+19
resources/sql/autopatches/20160531.pref.01.xaction.sql
··· 1 + CREATE TABLE {$NAMESPACE}_user.user_preferencestransaction ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + authorPHID VARBINARY(64) NOT NULL, 5 + objectPHID VARBINARY(64) NOT NULL, 6 + viewPolicy VARBINARY(64) NOT NULL, 7 + editPolicy VARBINARY(64) NOT NULL, 8 + commentPHID VARBINARY(64) DEFAULT NULL, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 11 + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 12 + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 13 + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 14 + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 15 + dateCreated INT UNSIGNED NOT NULL, 16 + dateModified INT UNSIGNED NOT NULL, 17 + UNIQUE KEY `key_phid` (`phid`), 18 + KEY `key_object` (`objectPHID`) 19 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+2
resources/sql/autopatches/20160531.pref.02.datecreatecol.sql
··· 1 + ALTER TABLE {$NAMESPACE}_user.user_preferences 2 + ADD dateCreated INT UNSIGNED NOT NULL;
+2
resources/sql/autopatches/20160531.pref.03.datemodcol.sql
··· 1 + ALTER TABLE {$NAMESPACE}_user.user_preferences 2 + ADD dateModified INT UNSIGNED NOT NULL;
+2
resources/sql/autopatches/20160531.pref.04.datecreateval.sql
··· 1 + UPDATE {$NAMESPACE}_user.user_preferences 2 + SET dateCreated = UNIX_TIMESTAMP() WHERE dateCreated = 0;
+2
resources/sql/autopatches/20160531.pref.05.datemodval.sql
··· 1 + UPDATE {$NAMESPACE}_user.user_preferences 2 + SET dateModified = UNIX_TIMESTAMP() WHERE dateModified = 0;
+2
resources/sql/autopatches/20160531.pref.06.phidcol.sql
··· 1 + ALTER TABLE {$NAMESPACE}_user.user_preferences 2 + ADD phid VARBINARY(64) NOT NULL;
+17
resources/sql/autopatches/20160531.pref.07.phidval.php
··· 1 + <?php 2 + 3 + $table = new PhabricatorUserPreferences(); 4 + $conn_w = $table->establishConnection('w'); 5 + 6 + foreach (new LiskMigrationIterator($table) as $row) { 7 + if ($row->getPHID() !== '') { 8 + continue; 9 + } 10 + 11 + queryfx( 12 + $conn_w, 13 + 'UPDATE %T SET phid = %s WHERE id = %d', 14 + $table->getTableName(), 15 + $table->generatePHID(), 16 + $row->getID()); 17 + }
+12 -1
src/__phutil_library_map__.php
··· 3592 3592 'PhabricatorUserLogView' => 'applications/people/view/PhabricatorUserLogView.php', 3593 3593 'PhabricatorUserPHIDResolver' => 'applications/phid/resolver/PhabricatorUserPHIDResolver.php', 3594 3594 'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php', 3595 + 'PhabricatorUserPreferencesPHIDType' => 'applications/settings/phid/PhabricatorUserPreferencesPHIDType.php', 3596 + 'PhabricatorUserPreferencesQuery' => 'applications/settings/query/PhabricatorUserPreferencesQuery.php', 3597 + 'PhabricatorUserPreferencesTransaction' => 'applications/settings/storage/PhabricatorUserPreferencesTransaction.php', 3595 3598 'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php', 3596 3599 'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php', 3597 3600 'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php', ··· 8342 8345 ), 8343 8346 'PhabricatorUserLogView' => 'AphrontView', 8344 8347 'PhabricatorUserPHIDResolver' => 'PhabricatorPHIDResolver', 8345 - 'PhabricatorUserPreferences' => 'PhabricatorUserDAO', 8348 + 'PhabricatorUserPreferences' => array( 8349 + 'PhabricatorUserDAO', 8350 + 'PhabricatorPolicyInterface', 8351 + 'PhabricatorDestructibleInterface', 8352 + 'PhabricatorApplicationTransactionInterface', 8353 + ), 8354 + 'PhabricatorUserPreferencesPHIDType' => 'PhabricatorPHIDType', 8355 + 'PhabricatorUserPreferencesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 8356 + 'PhabricatorUserPreferencesTransaction' => 'PhabricatorApplicationTransaction', 8346 8357 'PhabricatorUserProfile' => 'PhabricatorUserDAO', 8347 8358 'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor', 8348 8359 'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField',
+11 -3
src/applications/conpherence/editor/ConpherenceEditor.php
··· 533 533 534 534 protected function getMailTo(PhabricatorLiskDAO $object) { 535 535 $to_phids = array(); 536 + 536 537 $participants = $object->getParticipants(); 537 - if (empty($participants)) { 538 + if (!$participants) { 538 539 return $to_phids; 539 540 } 540 - $preferences = id(new PhabricatorUserPreferences()) 541 - ->loadAllWhere('userPHID in (%Ls)', array_keys($participants)); 541 + 542 + $participant_phids = mpull($participants, 'getParticipantPHID'); 543 + 544 + $preferences = id(new PhabricatorUserPreferencesQuery()) 545 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 546 + ->withUserPHIDs($participant_phids) 547 + ->execute(); 542 548 $preferences = mpull($preferences, null, 'getUserPHID'); 549 + 543 550 foreach ($participants as $phid => $participant) { 544 551 $default = ConpherenceSettings::EMAIL_ALWAYS; 545 552 $preference = idx($preferences, $phid); ··· 557 564 $to_phids[] = $phid; 558 565 } 559 566 } 567 + 560 568 return $to_phids; 561 569 } 562 570
+4 -3
src/applications/feed/PhabricatorFeedStoryPublisher.php
··· 207 207 208 208 $tags = $this->getMailTags(); 209 209 if ($tags) { 210 - $all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere( 211 - 'userPHID in (%Ls)', 212 - $phids); 210 + $all_prefs = id(new PhabricatorUserPreferencesQuery()) 211 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 212 + ->withUserPHIDs($phids) 213 + ->execute(); 213 214 $all_prefs = mpull($all_prefs, null, 'getUserPHID'); 214 215 } 215 216
+4 -3
src/applications/metamta/storage/PhabricatorMetaMTAMail.php
··· 940 940 } 941 941 } 942 942 943 - $all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere( 944 - 'userPHID in (%Ls)', 945 - $actor_phids); 943 + $all_prefs = id(new PhabricatorUserPreferencesQuery()) 944 + ->setViewer(PhabricatorUser::getOmnipotentUser()) 945 + ->withUserPHIDs($actor_phids) 946 + ->execute(); 946 947 $all_prefs = mpull($all_prefs, null, 'getUserPHID'); 947 948 948 949 $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL;
+9 -7
src/applications/people/storage/PhabricatorUser.php
··· 494 494 495 495 $preferences = null; 496 496 if ($this->getPHID()) { 497 - $preferences = id(new PhabricatorUserPreferences())->loadOneWhere( 498 - 'userPHID = %s', 499 - $this->getPHID()); 497 + $preferences = id(new PhabricatorUserPreferencesQuery()) 498 + ->setViewer($this) 499 + ->withUsers(array($this)) 500 + ->executeOne(); 500 501 } 501 502 502 503 if (!$preferences) { ··· 1293 1294 $external->delete(); 1294 1295 } 1295 1296 1296 - $prefs = id(new PhabricatorUserPreferences())->loadAllWhere( 1297 - 'userPHID = %s', 1298 - $this->getPHID()); 1297 + $prefs = id(new PhabricatorUserPreferencesQuery()) 1298 + ->setViewer($engine->getViewer()) 1299 + ->withUsers(array($this)) 1300 + ->execute(); 1299 1301 foreach ($prefs as $pref) { 1300 - $pref->delete(); 1302 + $engine->destroyObject($pref); 1301 1303 } 1302 1304 1303 1305 $profiles = id(new PhabricatorUserProfile())->loadAllWhere(
+40
src/applications/settings/phid/PhabricatorUserPreferencesPHIDType.php
··· 1 + <?php 2 + 3 + final class PhabricatorUserPreferencesPHIDType extends PhabricatorPHIDType { 4 + 5 + const TYPECONST = 'PSET'; 6 + 7 + public function getTypeName() { 8 + return pht('Settings'); 9 + } 10 + 11 + public function newObject() { 12 + return new PhabricatorUserPreferences(); 13 + } 14 + 15 + public function getPHIDTypeApplicationClass() { 16 + return 'PhabricatorSettingsApplication'; 17 + } 18 + 19 + protected function buildQueryForObjects( 20 + PhabricatorObjectQuery $query, 21 + array $phids) { 22 + 23 + return id(new PhabricatorUserPreferencesQuery()) 24 + ->withPHIDs($phids); 25 + } 26 + 27 + public function loadHandles( 28 + PhabricatorHandleQuery $query, 29 + array $handles, 30 + array $objects) { 31 + 32 + $viewer = $query->getViewer(); 33 + foreach ($handles as $phid => $handle) { 34 + $preferences = $objects[$phid]; 35 + 36 + $handle->setName(pht('Settings %d', $preferences->getID())); 37 + } 38 + } 39 + 40 + }
+118
src/applications/settings/query/PhabricatorUserPreferencesQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorUserPreferencesQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $userPHIDs; 9 + private $users = array(); 10 + 11 + public function withIDs(array $ids) { 12 + $this->ids = $ids; 13 + return $this; 14 + } 15 + 16 + public function withPHIDs(array $phids) { 17 + $this->phids = $phids; 18 + return $this; 19 + } 20 + 21 + public function withUserPHIDs(array $phids) { 22 + $this->userPHIDs = $phids; 23 + return $this; 24 + } 25 + 26 + public function withUsers(array $users) { 27 + assert_instances_of($users, 'PhabricatorUser'); 28 + $this->users = mpull($users, null, 'getPHID'); 29 + $this->withUserPHIDs(array_keys($this->users)); 30 + return $this; 31 + } 32 + 33 + public function newResultObject() { 34 + return new PhabricatorUserPreferences(); 35 + } 36 + 37 + protected function loadPage() { 38 + return $this->loadStandardPage($this->newResultObject()); 39 + } 40 + 41 + protected function willFilterPage(array $prefs) { 42 + $user_phids = mpull($prefs, 'getUserPHID'); 43 + $user_phids = array_filter($user_phids); 44 + 45 + // If some of the preferences are attached to users, try to use any objects 46 + // we were handed first. If we're missing some, load them. 47 + 48 + if ($user_phids) { 49 + $users = $this->users; 50 + 51 + $user_phids = array_fuse($user_phids); 52 + $load_phids = array_diff_key($user_phids, $users); 53 + $load_phids = array_keys($load_phids); 54 + 55 + if ($load_phids) { 56 + $load_users = id(new PhabricatorPeopleQuery()) 57 + ->setViewer($this->getViewer()) 58 + ->withPHIDs($load_phids) 59 + ->execute(); 60 + $load_users = mpull($load_users, null, 'getPHID'); 61 + $users += $load_users; 62 + } 63 + } else { 64 + $users = array(); 65 + } 66 + 67 + foreach ($prefs as $key => $pref) { 68 + $user_phid = $pref->getUserPHID(); 69 + if (!$user_phid) { 70 + $pref->attachUser(null); 71 + continue; 72 + } 73 + 74 + $user = idx($users, $user_phid); 75 + if (!$user) { 76 + $this->didRejectResult($pref); 77 + unset($prefs[$key]); 78 + continue; 79 + } 80 + 81 + $pref->attachUser($user); 82 + } 83 + 84 + return $prefs; 85 + } 86 + 87 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 88 + $where = parent::buildWhereClauseParts($conn); 89 + 90 + if ($this->ids !== null) { 91 + $where[] = qsprintf( 92 + $conn, 93 + 'id IN (%Ld)', 94 + $this->ids); 95 + } 96 + 97 + if ($this->phids !== null) { 98 + $where[] = qsprintf( 99 + $conn, 100 + 'phid IN (%Ls)', 101 + $this->phids); 102 + } 103 + 104 + if ($this->userPHIDs !== null) { 105 + $where[] = qsprintf( 106 + $conn, 107 + 'userPHID IN (%Ls)', 108 + $this->userPHIDs); 109 + } 110 + 111 + return $where; 112 + } 113 + 114 + public function getQueryApplicationClass() { 115 + return 'PhabricatorSettingsApplication'; 116 + } 117 + 118 + }
+117 -2
src/applications/settings/storage/PhabricatorUserPreferences.php
··· 1 1 <?php 2 2 3 - final class PhabricatorUserPreferences extends PhabricatorUserDAO { 3 + final class PhabricatorUserPreferences 4 + extends PhabricatorUserDAO 5 + implements 6 + PhabricatorPolicyInterface, 7 + PhabricatorDestructibleInterface, 8 + PhabricatorApplicationTransactionInterface { 4 9 5 10 const PREFERENCE_MONOSPACED = 'monospaced'; 6 11 const PREFERENCE_DARK_CONSOLE = 'dark_console'; ··· 51 56 protected $userPHID; 52 57 protected $preferences = array(); 53 58 59 + private $user = self::ATTACHABLE; 60 + 54 61 protected function getConfiguration() { 55 62 return array( 63 + self::CONFIG_AUX_PHID => true, 56 64 self::CONFIG_SERIALIZATION => array( 57 65 'preferences' => self::SERIALIZATION_JSON, 58 66 ), 59 - self::CONFIG_TIMESTAMPS => false, 60 67 self::CONFIG_KEY_SCHEMA => array( 61 68 'userPHID' => array( 62 69 'columns' => array('userPHID'), ··· 66 73 ) + parent::getConfiguration(); 67 74 } 68 75 76 + public function generatePHID() { 77 + return PhabricatorPHID::generateNewPHID( 78 + PhabricatorUserPreferencesPHIDType::TYPECONST); 79 + } 80 + 69 81 public function getPreference($key, $default = null) { 70 82 return idx($this->preferences, $key, $default); 71 83 } ··· 113 125 public static function filterMonospacedCSSRule($monospaced) { 114 126 // Prevent the user from doing dangerous things. 115 127 return preg_replace('([^a-z0-9 ,"./]+)i', '', $monospaced); 128 + } 129 + 130 + public function attachUser(PhabricatorUser $user = null) { 131 + $this->user = $user; 132 + return $this; 133 + } 134 + 135 + public function getUser() { 136 + return $this->assertAttached($this->user); 137 + } 138 + 139 + public function hasManagedUser() { 140 + $user_phid = $this->getUserPHID(); 141 + if (!$user_phid) { 142 + return false; 143 + } 144 + 145 + $user = $this->getUser(); 146 + if ($user->getIsSystemAgent() || $user->getIsMailingList()) { 147 + return true; 148 + } 149 + 150 + return false; 151 + } 152 + 153 + 154 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 155 + 156 + 157 + public function getCapabilities() { 158 + return array( 159 + PhabricatorPolicyCapability::CAN_VIEW, 160 + PhabricatorPolicyCapability::CAN_EDIT, 161 + ); 162 + } 163 + 164 + public function getPolicy($capability) { 165 + switch ($capability) { 166 + case PhabricatorPolicyCapability::CAN_VIEW: 167 + $user_phid = $this->getUserPHID(); 168 + if ($user_phid) { 169 + return $user_phid; 170 + } 171 + 172 + return PhabricatorPolicies::getMostOpenPolicy(); 173 + case PhabricatorPolicyCapability::CAN_EDIT: 174 + if ($this->hasManagedUser()) { 175 + return PhabricatorPolicies::POLICY_ADMIN; 176 + } 177 + 178 + $user_phid = $this->getUserPHID(); 179 + if ($user_phid) { 180 + return $user_phid; 181 + } 182 + 183 + return PhabricatorPolicies::POLICY_ADMIN; 184 + } 185 + } 186 + 187 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 188 + if ($this->hasManagedUser()) { 189 + if ($viewer->getIsAdmin()) { 190 + return true; 191 + } 192 + } 193 + 194 + return false; 195 + } 196 + 197 + public function describeAutomaticCapability($capability) { 198 + return null; 199 + } 200 + 201 + 202 + /* -( PhabricatorDestructibleInterface )----------------------------------- */ 203 + 204 + 205 + public function destroyObjectPermanently( 206 + PhabricatorDestructionEngine $engine) { 207 + $this->delete(); 208 + } 209 + 210 + 211 + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 212 + 213 + 214 + public function getApplicationTransactionEditor() { 215 + // TODO: Implement. 216 + throw new PhutilMethodNotImplementedException(); 217 + } 218 + 219 + public function getApplicationTransactionObject() { 220 + return $this; 221 + } 222 + 223 + public function getApplicationTransactionTemplate() { 224 + return new PhabricatorUserPreferencesTransaction(); 225 + } 226 + 227 + public function willRenderTimeline( 228 + PhabricatorApplicationTransactionView $timeline, 229 + AphrontRequest $request) { 230 + return $timeline; 116 231 } 117 232 118 233 }
+18
src/applications/settings/storage/PhabricatorUserPreferencesTransaction.php
··· 1 + <?php 2 + 3 + final class PhabricatorUserPreferencesTransaction 4 + extends PhabricatorApplicationTransaction { 5 + 6 + public function getApplicationName() { 7 + return 'user'; 8 + } 9 + 10 + public function getApplicationTransactionCommentObject() { 11 + return null; 12 + } 13 + 14 + public function getApplicationTransactionType() { 15 + return PhabricatorUserPreferencesPHIDType::TYPECONST; 16 + } 17 + 18 + }