1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 19:40:55 +01:00

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
This commit is contained in:
epriestley 2016-05-27 05:58:46 -07:00
parent b23c85b169
commit b256f2d7b2
16 changed files with 379 additions and 19 deletions

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_user.user_preferencestransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user_preferences
ADD dateCreated INT UNSIGNED NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user_preferences
ADD dateModified INT UNSIGNED NOT NULL;

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_user.user_preferences
SET dateCreated = UNIX_TIMESTAMP() WHERE dateCreated = 0;

View file

@ -0,0 +1,2 @@
UPDATE {$NAMESPACE}_user.user_preferences
SET dateModified = UNIX_TIMESTAMP() WHERE dateModified = 0;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user_preferences
ADD phid VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,17 @@
<?php
$table = new PhabricatorUserPreferences();
$conn_w = $table->establishConnection('w');
foreach (new LiskMigrationIterator($table) as $row) {
if ($row->getPHID() !== '') {
continue;
}
queryfx(
$conn_w,
'UPDATE %T SET phid = %s WHERE id = %d',
$table->getTableName(),
$table->generatePHID(),
$row->getID());
}

View file

@ -3592,6 +3592,9 @@ phutil_register_library_map(array(
'PhabricatorUserLogView' => 'applications/people/view/PhabricatorUserLogView.php',
'PhabricatorUserPHIDResolver' => 'applications/phid/resolver/PhabricatorUserPHIDResolver.php',
'PhabricatorUserPreferences' => 'applications/settings/storage/PhabricatorUserPreferences.php',
'PhabricatorUserPreferencesPHIDType' => 'applications/settings/phid/PhabricatorUserPreferencesPHIDType.php',
'PhabricatorUserPreferencesQuery' => 'applications/settings/query/PhabricatorUserPreferencesQuery.php',
'PhabricatorUserPreferencesTransaction' => 'applications/settings/storage/PhabricatorUserPreferencesTransaction.php',
'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php',
'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php',
'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php',
@ -8342,7 +8345,15 @@ phutil_register_library_map(array(
),
'PhabricatorUserLogView' => 'AphrontView',
'PhabricatorUserPHIDResolver' => 'PhabricatorPHIDResolver',
'PhabricatorUserPreferences' => 'PhabricatorUserDAO',
'PhabricatorUserPreferences' => array(
'PhabricatorUserDAO',
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorApplicationTransactionInterface',
),
'PhabricatorUserPreferencesPHIDType' => 'PhabricatorPHIDType',
'PhabricatorUserPreferencesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorUserPreferencesTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorUserProfile' => 'PhabricatorUserDAO',
'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField',

View file

@ -533,13 +533,20 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
protected function getMailTo(PhabricatorLiskDAO $object) {
$to_phids = array();
$participants = $object->getParticipants();
if (empty($participants)) {
if (!$participants) {
return $to_phids;
}
$preferences = id(new PhabricatorUserPreferences())
->loadAllWhere('userPHID in (%Ls)', array_keys($participants));
$participant_phids = mpull($participants, 'getParticipantPHID');
$preferences = id(new PhabricatorUserPreferencesQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUserPHIDs($participant_phids)
->execute();
$preferences = mpull($preferences, null, 'getUserPHID');
foreach ($participants as $phid => $participant) {
$default = ConpherenceSettings::EMAIL_ALWAYS;
$preference = idx($preferences, $phid);
@ -557,6 +564,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
$to_phids[] = $phid;
}
}
return $to_phids;
}

View file

@ -207,9 +207,10 @@ final class PhabricatorFeedStoryPublisher extends Phobject {
$tags = $this->getMailTags();
if ($tags) {
$all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
'userPHID in (%Ls)',
$phids);
$all_prefs = id(new PhabricatorUserPreferencesQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUserPHIDs($phids)
->execute();
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
}

View file

@ -940,9 +940,10 @@ final class PhabricatorMetaMTAMail
}
}
$all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
'userPHID in (%Ls)',
$actor_phids);
$all_prefs = id(new PhabricatorUserPreferencesQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUserPHIDs($actor_phids)
->execute();
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
$value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL;

View file

@ -494,9 +494,10 @@ final class PhabricatorUser
$preferences = null;
if ($this->getPHID()) {
$preferences = id(new PhabricatorUserPreferences())->loadOneWhere(
'userPHID = %s',
$this->getPHID());
$preferences = id(new PhabricatorUserPreferencesQuery())
->setViewer($this)
->withUsers(array($this))
->executeOne();
}
if (!$preferences) {
@ -1293,11 +1294,12 @@ final class PhabricatorUser
$external->delete();
}
$prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
$prefs = id(new PhabricatorUserPreferencesQuery())
->setViewer($engine->getViewer())
->withUsers(array($this))
->execute();
foreach ($prefs as $pref) {
$pref->delete();
$engine->destroyObject($pref);
}
$profiles = id(new PhabricatorUserProfile())->loadAllWhere(

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorUserPreferencesPHIDType extends PhabricatorPHIDType {
const TYPECONST = 'PSET';
public function getTypeName() {
return pht('Settings');
}
public function newObject() {
return new PhabricatorUserPreferences();
}
public function getPHIDTypeApplicationClass() {
return 'PhabricatorSettingsApplication';
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorUserPreferencesQuery())
->withPHIDs($phids);
}
public function loadHandles(
PhabricatorHandleQuery $query,
array $handles,
array $objects) {
$viewer = $query->getViewer();
foreach ($handles as $phid => $handle) {
$preferences = $objects[$phid];
$handle->setName(pht('Settings %d', $preferences->getID()));
}
}
}

View file

@ -0,0 +1,118 @@
<?php
final class PhabricatorUserPreferencesQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $userPHIDs;
private $users = array();
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withUserPHIDs(array $phids) {
$this->userPHIDs = $phids;
return $this;
}
public function withUsers(array $users) {
assert_instances_of($users, 'PhabricatorUser');
$this->users = mpull($users, null, 'getPHID');
$this->withUserPHIDs(array_keys($this->users));
return $this;
}
public function newResultObject() {
return new PhabricatorUserPreferences();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $prefs) {
$user_phids = mpull($prefs, 'getUserPHID');
$user_phids = array_filter($user_phids);
// If some of the preferences are attached to users, try to use any objects
// we were handed first. If we're missing some, load them.
if ($user_phids) {
$users = $this->users;
$user_phids = array_fuse($user_phids);
$load_phids = array_diff_key($user_phids, $users);
$load_phids = array_keys($load_phids);
if ($load_phids) {
$load_users = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer())
->withPHIDs($load_phids)
->execute();
$load_users = mpull($load_users, null, 'getPHID');
$users += $load_users;
}
} else {
$users = array();
}
foreach ($prefs as $key => $pref) {
$user_phid = $pref->getUserPHID();
if (!$user_phid) {
$pref->attachUser(null);
continue;
}
$user = idx($users, $user_phid);
if (!$user) {
$this->didRejectResult($pref);
unset($prefs[$key]);
continue;
}
$pref->attachUser($user);
}
return $prefs;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->userPHIDs !== null) {
$where[] = qsprintf(
$conn,
'userPHID IN (%Ls)',
$this->userPHIDs);
}
return $where;
}
public function getQueryApplicationClass() {
return 'PhabricatorSettingsApplication';
}
}

View file

@ -1,6 +1,11 @@
<?php
final class PhabricatorUserPreferences extends PhabricatorUserDAO {
final class PhabricatorUserPreferences
extends PhabricatorUserDAO
implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface,
PhabricatorApplicationTransactionInterface {
const PREFERENCE_MONOSPACED = 'monospaced';
const PREFERENCE_DARK_CONSOLE = 'dark_console';
@ -51,12 +56,14 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
protected $userPHID;
protected $preferences = array();
private $user = self::ATTACHABLE;
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'preferences' => self::SERIALIZATION_JSON,
),
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_KEY_SCHEMA => array(
'userPHID' => array(
'columns' => array('userPHID'),
@ -66,6 +73,11 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorUserPreferencesPHIDType::TYPECONST);
}
public function getPreference($key, $default = null) {
return idx($this->preferences, $key, $default);
}
@ -115,4 +127,107 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
return preg_replace('([^a-z0-9 ,"./]+)i', '', $monospaced);
}
public function attachUser(PhabricatorUser $user = null) {
$this->user = $user;
return $this;
}
public function getUser() {
return $this->assertAttached($this->user);
}
public function hasManagedUser() {
$user_phid = $this->getUserPHID();
if (!$user_phid) {
return false;
}
$user = $this->getUser();
if ($user->getIsSystemAgent() || $user->getIsMailingList()) {
return true;
}
return false;
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
$user_phid = $this->getUserPHID();
if ($user_phid) {
return $user_phid;
}
return PhabricatorPolicies::getMostOpenPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->hasManagedUser()) {
return PhabricatorPolicies::POLICY_ADMIN;
}
$user_phid = $this->getUserPHID();
if ($user_phid) {
return $user_phid;
}
return PhabricatorPolicies::POLICY_ADMIN;
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
if ($this->hasManagedUser()) {
if ($viewer->getIsAdmin()) {
return true;
}
}
return false;
}
public function describeAutomaticCapability($capability) {
return null;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->delete();
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
// TODO: Implement.
throw new PhutilMethodNotImplementedException();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorUserPreferencesTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}

View file

@ -0,0 +1,18 @@
<?php
final class PhabricatorUserPreferencesTransaction
extends PhabricatorApplicationTransaction {
public function getApplicationName() {
return 'user';
}
public function getApplicationTransactionCommentObject() {
return null;
}
public function getApplicationTransactionType() {
return PhabricatorUserPreferencesPHIDType::TYPECONST;
}
}