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

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

+200 -79
+2 -2
src/__phutil_library_map__.php
··· 312 312 'ConpherenceRoomListController' => 'applications/conpherence/controller/ConpherenceRoomListController.php', 313 313 'ConpherenceRoomTestCase' => 'applications/conpherence/__tests__/ConpherenceRoomTestCase.php', 314 314 'ConpherenceSchemaSpec' => 'applications/conpherence/storage/ConpherenceSchemaSpec.php', 315 - 'ConpherenceSettings' => 'applications/conpherence/constants/ConpherenceSettings.php', 316 315 'ConpherenceTestCase' => 'applications/conpherence/__tests__/ConpherenceTestCase.php', 317 316 'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php', 318 317 'ConpherenceThreadIndexEngineExtension' => 'applications/conpherence/engineextension/ConpherenceThreadIndexEngineExtension.php', ··· 3636 3635 'PhabricatorUserPreferencesPHIDType' => 'applications/settings/phid/PhabricatorUserPreferencesPHIDType.php', 3637 3636 'PhabricatorUserPreferencesQuery' => 'applications/settings/query/PhabricatorUserPreferencesQuery.php', 3638 3637 'PhabricatorUserPreferencesTransaction' => 'applications/settings/storage/PhabricatorUserPreferencesTransaction.php', 3638 + 'PhabricatorUserPreferencesTransactionQuery' => 'applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php', 3639 3639 'PhabricatorUserProfile' => 'applications/people/storage/PhabricatorUserProfile.php', 3640 3640 'PhabricatorUserProfileEditor' => 'applications/people/editor/PhabricatorUserProfileEditor.php', 3641 3641 'PhabricatorUserRealNameField' => 'applications/people/customfield/PhabricatorUserRealNameField.php', ··· 4565 4565 'ConpherenceRoomListController' => 'ConpherenceController', 4566 4566 'ConpherenceRoomTestCase' => 'ConpherenceTestCase', 4567 4567 'ConpherenceSchemaSpec' => 'PhabricatorConfigSchemaSpec', 4568 - 'ConpherenceSettings' => 'ConpherenceConstants', 4569 4568 'ConpherenceTestCase' => 'PhabricatorTestCase', 4570 4569 'ConpherenceThread' => array( 4571 4570 'ConpherenceDAO', ··· 8439 8438 'PhabricatorUserPreferencesPHIDType' => 'PhabricatorPHIDType', 8440 8439 'PhabricatorUserPreferencesQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 8441 8440 'PhabricatorUserPreferencesTransaction' => 'PhabricatorApplicationTransaction', 8441 + 'PhabricatorUserPreferencesTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 8442 8442 'PhabricatorUserProfile' => 'PhabricatorUserDAO', 8443 8443 'PhabricatorUserProfileEditor' => 'PhabricatorApplicationTransactionEditor', 8444 8444 'PhabricatorUserRealNameField' => 'PhabricatorUserCustomField',
-22
src/applications/conpherence/constants/ConpherenceSettings.php
··· 1 - <?php 2 - 3 - final class ConpherenceSettings extends ConpherenceConstants { 4 - 5 - const EMAIL_ALWAYS = 0; 6 - const NOTIFICATIONS_ONLY = 1; 7 - 8 - public static function getHumanString($constant) { 9 - $string = pht('Unknown setting.'); 10 - 11 - switch ($constant) { 12 - case self::EMAIL_ALWAYS: 13 - $string = pht('Email me every update.'); 14 - break; 15 - case self::NOTIFICATIONS_ONLY: 16 - $string = pht('Notifications only.'); 17 - break; 18 - } 19 - 20 - return $string; 21 - } 22 - }
+6 -1
src/applications/conpherence/controller/ConpherenceUpdateController.php
··· 127 127 } 128 128 $participant->setSettings(array('notifications' => $notifications)); 129 129 $participant->save(); 130 + 131 + $label = PhabricatorConpherenceNotificationsSetting::getSettingLabel( 132 + $notifications); 133 + 130 134 $result = pht( 131 135 'Updated notification settings to "%s".', 132 - ConpherenceSettings::getHumanString($notifications)); 136 + $label); 137 + 133 138 return id(new AphrontAjaxResponse()) 134 139 ->setContent($result); 135 140 break;
+10 -27
src/applications/conpherence/controller/ConpherenceWidgetController.php
··· 2 2 3 3 final class ConpherenceWidgetController extends ConpherenceController { 4 4 5 - private $userPreferences; 6 - 7 - public function setUserPreferences(PhabricatorUserPreferences $pref) { 8 - $this->userPreferences = $pref; 9 - return $this; 10 - } 11 - 12 - public function getUserPreferences() { 13 - return $this->userPreferences; 14 - } 15 - 16 5 public function shouldAllowPublic() { 17 6 return true; 18 7 } ··· 34 23 return new Aphront404Response(); 35 24 } 36 25 $this->setConpherence($conpherence); 37 - 38 - $this->setUserPreferences($user->loadPreferences()); 39 26 40 27 switch ($request->getStr('widget')) { 41 28 case 'widgets-people': ··· 143 130 ), 144 131 $text); 145 132 } 146 - $default = ConpherenceSettings::EMAIL_ALWAYS; 147 - $preference = $this->getUserPreferences(); 148 - if ($preference) { 149 - $default = $preference->getPreference( 150 - PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS, 151 - ConpherenceSettings::EMAIL_ALWAYS); 152 - } 133 + $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; 134 + $notification_default = $viewer->getUserSetting($notification_key); 135 + 153 136 $settings = $participant->getSettings(); 154 137 $notifications = idx( 155 138 $settings, 156 139 'notifications', 157 - $default); 140 + $notification_default); 158 141 $options = id(new AphrontFormRadioButtonControl()) 159 142 ->addButton( 160 - ConpherenceSettings::EMAIL_ALWAYS, 161 - ConpherenceSettings::getHumanString( 162 - ConpherenceSettings::EMAIL_ALWAYS), 143 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL, 144 + PhabricatorConpherenceNotificationsSetting::getSettingLabel( 145 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL), 163 146 '') 164 147 ->addButton( 165 - ConpherenceSettings::NOTIFICATIONS_ONLY, 166 - ConpherenceSettings::getHumanString( 167 - ConpherenceSettings::NOTIFICATIONS_ONLY), 148 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY, 149 + PhabricatorConpherenceNotificationsSetting::getSettingLabel( 150 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY), 168 151 '') 169 152 ->setName('notifications') 170 153 ->setValue($notifications);
+17 -14
src/applications/conpherence/editor/ConpherenceEditor.php
··· 541 541 542 542 $participant_phids = mpull($participants, 'getParticipantPHID'); 543 543 544 - $preferences = id(new PhabricatorUserPreferencesQuery()) 544 + $users = id(new PhabricatorPeopleQuery()) 545 545 ->setViewer(PhabricatorUser::getOmnipotentUser()) 546 - ->withUserPHIDs($participant_phids) 546 + ->withPHIDs($participant_phids) 547 + ->needUserSettings(true) 547 548 ->execute(); 548 - $preferences = mpull($preferences, null, 'getUserPHID'); 549 + $users = mpull($users, null, 'getPHID'); 550 + 551 + $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; 552 + $notification_email = 553 + PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL; 549 554 550 555 foreach ($participants as $phid => $participant) { 551 - $default = ConpherenceSettings::EMAIL_ALWAYS; 552 - $preference = idx($preferences, $phid); 553 - if ($preference) { 554 - $default = $preference->getPreference( 555 - PhabricatorUserPreferences::PREFERENCE_CONPH_NOTIFICATIONS, 556 - ConpherenceSettings::EMAIL_ALWAYS); 556 + $user = idx($users, $phid); 557 + if ($user) { 558 + $default = $user->getUserSetting($notification_key); 559 + } else { 560 + $default = $notification_email; 557 561 } 562 + 558 563 $settings = $participant->getSettings(); 559 - $notifications = idx( 560 - $settings, 561 - 'notifications', 562 - $default); 563 - if ($notifications == ConpherenceSettings::EMAIL_ALWAYS) { 564 + $notifications = idx($settings, 'notifications', $default); 565 + 566 + if ($notifications == $notification_email) { 564 567 $to_phids[] = $phid; 565 568 } 566 569 }
+6 -2
src/applications/people/cache/PhabricatorUserPreferencesCacheType.php
··· 20 20 public function newValueForUsers($key, array $users) { 21 21 $viewer = $this->getViewer(); 22 22 23 + $user_phids = mpull($users, 'getPHID'); 24 + 23 25 $preferences = id(new PhabricatorUserPreferencesQuery()) 24 26 ->setViewer($viewer) 25 - ->withUserPHIDs(mpull($users, 'getPHID')) 27 + ->withUserPHIDs($user_phids) 26 28 ->execute(); 27 29 28 - return mpull($preferences, 'getPreferences', 'getUserPHID'); 30 + $empty = array_fill_keys($user_phids, array()); 31 + 32 + return mpull($preferences, 'getPreferences', 'getUserPHID') + $empty; 29 33 } 30 34 31 35 }
+102
src/applications/people/query/PhabricatorPeopleQuery.php
··· 23 23 private $needProfileImage; 24 24 private $needAvailability; 25 25 private $needBadges; 26 + private $cacheKeys = array(); 26 27 27 28 public function withIDs(array $ids) { 28 29 $this->ids = $ids; ··· 116 117 117 118 public function needBadges($need) { 118 119 $this->needBadges = $need; 120 + return $this; 121 + } 122 + 123 + public function needUserSettings($need) { 124 + $cache_key = PhabricatorUserPreferencesCacheType::KEY_PREFERENCES; 125 + 126 + if ($need) { 127 + $this->cacheKeys[$cache_key] = true; 128 + } else { 129 + unset($this->cacheKeys[$cache_key]); 130 + } 131 + 119 132 return $this; 120 133 } 121 134 ··· 237 250 $this->rebuildAvailabilityCache($rebuild); 238 251 } 239 252 } 253 + 254 + $this->fillUserCaches($users); 240 255 241 256 return $users; 242 257 } ··· 481 496 } 482 497 } 483 498 499 + private function fillUserCaches(array $users) { 500 + if (!$this->cacheKeys) { 501 + return; 502 + } 503 + 504 + $user_map = mpull($users, null, 'getPHID'); 505 + $keys = array_keys($this->cacheKeys); 506 + 507 + $hashes = array(); 508 + foreach ($keys as $key) { 509 + $hashes[] = PhabricatorHash::digestForIndex($key); 510 + } 511 + 512 + // First, pull any available caches. If we wanted to be particularly clever 513 + // we could do this with JOINs in the main query. 514 + 515 + $cache_table = new PhabricatorUserCache(); 516 + $cache_conn = $cache_table->establishConnection('r'); 517 + 518 + $cache_data = queryfx_all( 519 + $cache_conn, 520 + 'SELECT cacheKey, userPHID, cacheData FROM %T 521 + WHERE cacheIndex IN (%Ls) AND userPHID IN (%Ls)', 522 + $cache_table->getTableName(), 523 + $hashes, 524 + array_keys($user_map)); 525 + 526 + $need = array(); 527 + 528 + $cache_data = igroup($cache_data, 'userPHID'); 529 + foreach ($user_map as $user_phid => $user) { 530 + $raw_rows = idx($cache_data, $user_phid, array()); 531 + if (!$raw_rows) { 532 + continue; 533 + } 534 + $raw_data = ipull($raw_rows, 'cacheData', 'cacheKey'); 535 + 536 + foreach ($keys as $key) { 537 + if (isset($raw_data[$key]) || array_key_exists($key, $raw_data)) { 538 + continue; 539 + } 540 + $need[$key][$user_phid] = $user; 541 + } 542 + 543 + $user->attachRawCacheData($raw_data); 544 + } 545 + 546 + // If we missed any cache values, bulk-construct them now. This is 547 + // usually much cheaper than generating them on-demand for each user 548 + // record. 549 + 550 + if (!$need) { 551 + return; 552 + } 553 + 554 + $writes = array(); 555 + foreach ($need as $cache_key => $need_users) { 556 + $type = PhabricatorUserCacheType::getCacheTypeForKey($cache_key); 557 + if (!$type) { 558 + continue; 559 + } 560 + 561 + $data = $type->newValueForUsers($cache_key, $need_users); 562 + 563 + foreach ($data as $user_phid => $value) { 564 + $raw_value = $type->getValueForStorage($value); 565 + $data[$user_phid] = $raw_value; 566 + $writes[] = array( 567 + 'userPHID' => $user_phid, 568 + 'key' => $cache_key, 569 + 'type' => $type, 570 + 'value' => $raw_value, 571 + ); 572 + } 573 + 574 + foreach ($need_users as $user_phid => $user) { 575 + if (isset($data[$user_phid]) || array_key_exists($user_phid, $data)) { 576 + $user->attachRawCacheData( 577 + array( 578 + $cache_key => $data[$user_phid], 579 + )); 580 + } 581 + } 582 + } 583 + 584 + PhabricatorUserCache::writeCaches($writes); 585 + } 484 586 }
+38 -11
src/applications/people/storage/PhabricatorUserCache.php
··· 42 42 $key, 43 43 $user_phid, 44 44 $raw_value) { 45 + self::writeCaches( 46 + array( 47 + array( 48 + 'type' => $type, 49 + 'key' => $key, 50 + 'userPHID' => $user_phid, 51 + 'value' => $raw_value, 52 + ), 53 + )); 54 + } 45 55 56 + public static function writeCaches(array $values) { 46 57 if (PhabricatorEnv::isReadOnly()) { 47 58 return; 48 59 } 49 60 61 + if (!$values) { 62 + return; 63 + } 64 + 50 65 $table = new self(); 51 66 $conn_w = $table->establishConnection('w'); 52 67 68 + $sql = array(); 69 + foreach ($values as $value) { 70 + $key = $value['key']; 71 + 72 + $sql[] = qsprintf( 73 + $conn_w, 74 + '(%s, %s, %s, %s, %s)', 75 + $value['userPHID'], 76 + PhabricatorHash::digestForIndex($key), 77 + $key, 78 + $value['value'], 79 + $value['type']->getUserCacheType()); 80 + } 81 + 53 82 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); 54 83 55 - queryfx( 56 - $conn_w, 57 - 'INSERT INTO %T (userPHID, cacheIndex, cacheKey, cacheData, cacheType) 58 - VALUES (%s, %s, %s, %s, %s) 59 - ON DUPLICATE KEY UPDATE cacheData = VALUES(cacheData)', 60 - $table->getTableName(), 61 - $user_phid, 62 - PhabricatorHash::digestForIndex($key), 63 - $key, 64 - $raw_value, 65 - $type->getUserCacheType()); 84 + foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { 85 + queryfx( 86 + $conn_w, 87 + 'INSERT INTO %T (userPHID, cacheIndex, cacheKey, cacheData, cacheType) 88 + VALUES %Q 89 + ON DUPLICATE KEY UPDATE cacheData = VALUES(cacheData)', 90 + $table->getTableName(), 91 + $chunk); 92 + } 66 93 67 94 unset($unguarded); 68 95 }
+10
src/applications/settings/query/PhabricatorUserPreferencesTransactionQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorUserPreferencesTransactionQuery 4 + extends PhabricatorApplicationTransactionQuery { 5 + 6 + public function getTemplateApplicationTransaction() { 7 + return new PhabricatorUserPreferencesTransaction(); 8 + } 9 + 10 + }
+9
src/applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php
··· 32 32 } 33 33 34 34 protected function getSelectOptions() { 35 + return self::getOptionsMap(); 36 + } 37 + 38 + public static function getSettingLabel($key) { 39 + $labels = self::getOptionsMap(); 40 + return idx($labels, $key, pht('Unknown ("%s")', $key)); 41 + } 42 + 43 + private static function getOptionsMap() { 35 44 return array( 36 45 self::VALUE_CONPHERENCE_EMAIL => pht('Send Email'), 37 46 self::VALUE_CONPHERENCE_NOTIFY => pht('Send Notifications'),