1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 01:08:50 +02:00

Modernize Conpherence access to user preferences

Summary:
Ref T4103. Conpherence is doing some weird stuff and has its own redudnant settings object.

  - Get rid of `ConpherenceSettings`.
  - Use `getUserSetting()` instead of `loadPreferences()`.
  - When applying transactions, add a new mechanism to efficiently prefill caches (this will still work anyway, but it's slower if we don't bulk-fetch).

Test Plan:
  - Changed global Conpherence setting.
  - Created a new Conpherence, saw setting set to global default.
  - Changed local room setting.
  - Submitted messages.
  - Saw cache prefill for all particpiants in database.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T4103

Differential Revision: https://secure.phabricator.com/D16025
This commit is contained in:
epriestley 2016-06-03 07:24:55 -07:00
parent 9a076b71a3
commit 1e17fd31a4
10 changed files with 200 additions and 79 deletions

View file

@ -312,7 +312,6 @@ phutil_register_library_map(array(
'ConpherenceRoomListController' => 'applications/conpherence/controller/ConpherenceRoomListController.php',
'ConpherenceRoomTestCase' => 'applications/conpherence/__tests__/ConpherenceRoomTestCase.php',
'ConpherenceSchemaSpec' => 'applications/conpherence/storage/ConpherenceSchemaSpec.php',
'ConpherenceSettings' => 'applications/conpherence/constants/ConpherenceSettings.php',
'ConpherenceTestCase' => 'applications/conpherence/__tests__/ConpherenceTestCase.php',
'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php',
'ConpherenceThreadIndexEngineExtension' => 'applications/conpherence/engineextension/ConpherenceThreadIndexEngineExtension.php',
@ -3636,6 +3635,7 @@ phutil_register_library_map(array(
'PhabricatorUserPreferencesPHIDType' => 'applications/settings/phid/PhabricatorUserPreferencesPHIDType.php',
'PhabricatorUserPreferencesQuery' => 'applications/settings/query/PhabricatorUserPreferencesQuery.php',
'PhabricatorUserPreferencesTransaction' => 'applications/settings/storage/PhabricatorUserPreferencesTransaction.php',
'PhabricatorUserPreferencesTransactionQuery' => 'applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php',
'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php',
'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php',
'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php',
@ -4565,7 +4565,6 @@ phutil_register_library_map(array(
'ConpherenceRoomListController' => 'ConpherenceController',
'ConpherenceRoomTestCase' => 'ConpherenceTestCase',
'ConpherenceSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'ConpherenceSettings' => 'ConpherenceConstants',
'ConpherenceTestCase' => 'PhabricatorTestCase',
'ConpherenceThread' => array(
'ConpherenceDAO',
@ -8439,6 +8438,7 @@ phutil_register_library_map(array(
'PhabricatorUserPreferencesPHIDType' => 'PhabricatorPHIDType',
'PhabricatorUserPreferencesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorUserPreferencesTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorUserPreferencesTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorUserProfile' => 'PhabricatorUserDAO',
'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField',

View file

@ -1,22 +0,0 @@
<?php
final class ConpherenceSettings extends ConpherenceConstants {
const EMAIL_ALWAYS = 0;
const NOTIFICATIONS_ONLY = 1;
public static function getHumanString($constant) {
$string = pht('Unknown setting.');
switch ($constant) {
case self::EMAIL_ALWAYS:
$string = pht('Email me every update.');
break;
case self::NOTIFICATIONS_ONLY:
$string = pht('Notifications only.');
break;
}
return $string;
}
}

View file

@ -127,9 +127,14 @@ final class ConpherenceUpdateController
}
$participant->setSettings(array('notifications' => $notifications));
$participant->save();
$label = PhabricatorConpherenceNotificationsSetting::getSettingLabel(
$notifications);
$result = pht(
'Updated notification settings to "%s".',
ConpherenceSettings::getHumanString($notifications));
$label);
return id(new AphrontAjaxResponse())
->setContent($result);
break;

View file

@ -2,17 +2,6 @@
final class ConpherenceWidgetController extends ConpherenceController {
private $userPreferences;
public function setUserPreferences(PhabricatorUserPreferences $pref) {
$this->userPreferences = $pref;
return $this;
}
public function getUserPreferences() {
return $this->userPreferences;
}
public function shouldAllowPublic() {
return true;
}
@ -35,8 +24,6 @@ final class ConpherenceWidgetController extends ConpherenceController {
}
$this->setConpherence($conpherence);
$this->setUserPreferences($user->loadPreferences());
switch ($request->getStr('widget')) {
case 'widgets-people':
$content = $this->renderPeopleWidgetPaneContent();
@ -143,28 +130,24 @@ final class ConpherenceWidgetController extends ConpherenceController {
),
$text);
}
$default = ConpherenceSettings::EMAIL_ALWAYS;
$preference = $this->getUserPreferences();
if ($preference) {
$default = $preference->getPreference(
PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS,
ConpherenceSettings::EMAIL_ALWAYS);
}
$notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY;
$notification_default = $viewer->getUserSetting($notification_key);
$settings = $participant->getSettings();
$notifications = idx(
$settings,
'notifications',
$default);
$notification_default);
$options = id(new AphrontFormRadioButtonControl())
->addButton(
ConpherenceSettings::EMAIL_ALWAYS,
ConpherenceSettings::getHumanString(
ConpherenceSettings::EMAIL_ALWAYS),
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL,
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL),
'')
->addButton(
ConpherenceSettings::NOTIFICATIONS_ONLY,
ConpherenceSettings::getHumanString(
ConpherenceSettings::NOTIFICATIONS_ONLY),
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY,
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY),
'')
->setName('notifications')
->setValue($notifications);

View file

@ -541,26 +541,29 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
$participant_phids = mpull($participants, 'getParticipantPHID');
$preferences = id(new PhabricatorUserPreferencesQuery())
$users = id(new PhabricatorPeopleQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withUserPHIDs($participant_phids)
->withPHIDs($participant_phids)
->needUserSettings(true)
->execute();
$preferences = mpull($preferences, null, 'getUserPHID');
$users = mpull($users, null, 'getPHID');
$notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY;
$notification_email =
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL;
foreach ($participants as $phid => $participant) {
$default = ConpherenceSettings::EMAIL_ALWAYS;
$preference = idx($preferences, $phid);
if ($preference) {
$default = $preference->getPreference(
PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS,
ConpherenceSettings::EMAIL_ALWAYS);
$user = idx($users, $phid);
if ($user) {
$default = $user->getUserSetting($notification_key);
} else {
$default = $notification_email;
}
$settings = $participant->getSettings();
$notifications = idx(
$settings,
'notifications',
$default);
if ($notifications == ConpherenceSettings::EMAIL_ALWAYS) {
$notifications = idx($settings, 'notifications', $default);
if ($notifications == $notification_email) {
$to_phids[] = $phid;
}
}

View file

@ -20,12 +20,16 @@ final class PhabricatorUserPreferencesCacheType
public function newValueForUsers($key, array $users) {
$viewer = $this->getViewer();
$user_phids = mpull($users, 'getPHID');
$preferences = id(new PhabricatorUserPreferencesQuery())
->setViewer($viewer)
->withUserPHIDs(mpull($users, 'getPHID'))
->withUserPHIDs($user_phids)
->execute();
return mpull($preferences, 'getPreferences', 'getUserPHID');
$empty = array_fill_keys($user_phids, array());
return mpull($preferences, 'getPreferences', 'getUserPHID') + $empty;
}
}

View file

@ -23,6 +23,7 @@ final class PhabricatorPeopleQuery
private $needProfileImage;
private $needAvailability;
private $needBadges;
private $cacheKeys = array();
public function withIDs(array $ids) {
$this->ids = $ids;
@ -119,6 +120,18 @@ final class PhabricatorPeopleQuery
return $this;
}
public function needUserSettings($need) {
$cache_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES;
if ($need) {
$this->cacheKeys[$cache_key] = true;
} else {
unset($this->cacheKeys[$cache_key]);
}
return $this;
}
public function newResultObject() {
return new PhabricatorUser();
}
@ -238,6 +251,8 @@ final class PhabricatorPeopleQuery
}
}
$this->fillUserCaches($users);
return $users;
}
@ -481,4 +496,91 @@ final class PhabricatorPeopleQuery
}
}
private function fillUserCaches(array $users) {
if (!$this->cacheKeys) {
return;
}
$user_map = mpull($users, null, 'getPHID');
$keys = array_keys($this->cacheKeys);
$hashes = array();
foreach ($keys as $key) {
$hashes[] = PhabricatorHash::digestForIndex($key);
}
// First, pull any available caches. If we wanted to be particularly clever
// we could do this with JOINs in the main query.
$cache_table = new PhabricatorUserCache();
$cache_conn = $cache_table->establishConnection('r');
$cache_data = queryfx_all(
$cache_conn,
'SELECT cacheKey, userPHID, cacheData FROM %T
WHERE cacheIndex IN (%Ls) AND userPHID IN (%Ls)',
$cache_table->getTableName(),
$hashes,
array_keys($user_map));
$need = array();
$cache_data = igroup($cache_data, 'userPHID');
foreach ($user_map as $user_phid => $user) {
$raw_rows = idx($cache_data, $user_phid, array());
if (!$raw_rows) {
continue;
}
$raw_data = ipull($raw_rows, 'cacheData', 'cacheKey');
foreach ($keys as $key) {
if (isset($raw_data[$key]) || array_key_exists($key, $raw_data)) {
continue;
}
$need[$key][$user_phid] = $user;
}
$user->attachRawCacheData($raw_data);
}
// If we missed any cache values, bulk-construct them now. This is
// usually much cheaper than generating them on-demand for each user
// record.
if (!$need) {
return;
}
$writes = array();
foreach ($need as $cache_key => $need_users) {
$type = PhabricatorUserCacheType::getCacheTypeForKey($cache_key);
if (!$type) {
continue;
}
$data = $type->newValueForUsers($cache_key, $need_users);
foreach ($data as $user_phid => $value) {
$raw_value = $type->getValueForStorage($value);
$data[$user_phid] = $raw_value;
$writes[] = array(
'userPHID' => $user_phid,
'key' => $cache_key,
'type' => $type,
'value' => $raw_value,
);
}
foreach ($need_users as $user_phid => $user) {
if (isset($data[$user_phid]) || array_key_exists($user_phid, $data)) {
$user->attachRawCacheData(
array(
$cache_key => $data[$user_phid],
));
}
}
}
PhabricatorUserCache::writeCaches($writes);
}
}

View file

@ -42,27 +42,54 @@ final class PhabricatorUserCache extends PhabricatorUserDAO {
$key,
$user_phid,
$raw_value) {
self::writeCaches(
array(
array(
'type' => $type,
'key' => $key,
'userPHID' => $user_phid,
'value' => $raw_value,
),
));
}
public static function writeCaches(array $values) {
if (PhabricatorEnv::isReadOnly()) {
return;
}
if (!$values) {
return;
}
$table = new self();
$conn_w = $table->establishConnection('w');
$sql = array();
foreach ($values as $value) {
$key = $value['key'];
$sql[] = qsprintf(
$conn_w,
'(%s, %s, %s, %s, %s)',
$value['userPHID'],
PhabricatorHash::digestForIndex($key),
$key,
$value['value'],
$value['type']->getUserCacheType());
}
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
queryfx(
$conn_w,
'INSERT INTO %T (userPHID, cacheIndex, cacheKey, cacheData, cacheType)
VALUES (%s, %s, %s, %s, %s)
ON DUPLICATE KEY UPDATE cacheData = VALUES(cacheData)',
$table->getTableName(),
$user_phid,
PhabricatorHash::digestForIndex($key),
$key,
$raw_value,
$type->getUserCacheType());
foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) {
queryfx(
$conn_w,
'INSERT INTO %T (userPHID, cacheIndex, cacheKey, cacheData, cacheType)
VALUES %Q
ON DUPLICATE KEY UPDATE cacheData = VALUES(cacheData)',
$table->getTableName(),
$chunk);
}
unset($unguarded);
}

View file

@ -0,0 +1,10 @@
<?php
final class PhabricatorUserPreferencesTransactionQuery
extends PhabricatorApplicationTransactionQuery {
public function getTemplateApplicationTransaction() {
return new PhabricatorUserPreferencesTransaction();
}
}

View file

@ -32,6 +32,15 @@ final class PhabricatorConpherenceNotificationsSetting
}
protected function getSelectOptions() {
return self::getOptionsMap();
}
public static function getSettingLabel($key) {
$labels = self::getOptionsMap();
return idx($labels, $key, pht('Unknown ("%s")', $key));
}
private static function getOptionsMap() {
return array(
self::VALUE_CONPHERENCE_EMAIL => pht('Send Email'),
self::VALUE_CONPHERENCE_NOTIFY => pht('Send Notifications'),