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

Fix several issues with email-related global preferences

Summary:
Ref T11098. Mixture of issues here:

- Similar problem to D16112, where users with no settings at all could fail to fall back to the global defaults.
- I made `UserPreferencesQuery` responsible for building defaults instead to simplify this, since we have 4 or 5 callsites which need to do it and they aren't easily reducible.
- Handle cases where `metamta.one-mail-per-recipient` is off (and thus users can not have any custom settings) more explicitly.
- When `metamta.one-mail-per-recipient` is off, remove the "Email Format" panel for users only -- administrators can still access it in global preferences.

Test Plan:
- Deleted a user's preferences, changed globals, purged cache, made sure defaults reflected global defaults.
- Changed global mail tags, sent mail to the user, verified it was dropped in accordinace with global settings.
- Changed user's settings to get the mail instead, verified mail was sent.
- Toggled user's Re / Vary settings, verified mail subject lines reflected user settings.
- Disabled `metamta.one-mail-per-recipient`, verified user "Email Format" panel vanished.
- Edited "Email Format" in single-mail-mode in global prefs as an administrator.
- Sent more mail, verified mail respected new global settings.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11098

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

+81 -58
+1
src/applications/feed/PhabricatorFeedStoryPublisher.php
··· 215 215 $all_prefs = id(new PhabricatorUserPreferencesQuery()) 216 216 ->setViewer(PhabricatorUser::getOmnipotentUser()) 217 217 ->withUserPHIDs($phids) 218 + ->needSyntheticPreferences(true) 218 219 ->execute(); 219 220 $all_prefs = mpull($all_prefs, null, 'getUserPHID'); 220 221 }
+9 -17
src/applications/metamta/storage/PhabricatorMetaMTAMail.php
··· 877 877 $all_prefs = id(new PhabricatorUserPreferencesQuery()) 878 878 ->setViewer(PhabricatorUser::getOmnipotentUser()) 879 879 ->withUserPHIDs($actor_phids) 880 + ->needSyntheticPreferences(true) 880 881 ->execute(); 881 882 $all_prefs = mpull($all_prefs, null, 'getUserPHID'); 882 883 ··· 1105 1106 1106 1107 1107 1108 private function loadPreferences($target_phid) { 1108 - if (!self::shouldMultiplexAllMail()) { 1109 - $target_phid = null; 1110 - } 1109 + $viewer = PhabricatorUser::getOmnipotentUser(); 1111 1110 1112 - if ($target_phid) { 1111 + if (self::shouldMultiplexAllMail()) { 1113 1112 $preferences = id(new PhabricatorUserPreferencesQuery()) 1114 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 1113 + ->setViewer($viewer) 1115 1114 ->withUserPHIDs(array($target_phid)) 1115 + ->needSyntheticPreferences(true) 1116 1116 ->executeOne(); 1117 - } else { 1118 - $preferences = null; 1117 + if ($preferences) { 1118 + return $preferences; 1119 + } 1119 1120 } 1120 1121 1121 - // TODO: Here, we would load global preferences once they exist. 1122 - 1123 - if (!$preferences) { 1124 - // If we haven't found suitable preferences yet, return an empty object 1125 - // which implicitly has all the default values. 1126 - $preferences = id(new PhabricatorUserPreferences()) 1127 - ->attachUser(new PhabricatorUser()); 1128 - } 1129 - 1130 - return $preferences; 1122 + return PhabricatorUserPreferences::loadGlobalPreferences($viewer); 1131 1123 } 1132 1124 1133 1125 private function shouldAddRePrefix(PhabricatorUserPreferences $preferences) {
+3 -16
src/applications/people/cache/PhabricatorUserPreferencesCacheType.php
··· 29 29 30 30 $preferences = id(new PhabricatorUserPreferencesQuery()) 31 31 ->setViewer($viewer) 32 - ->withUserPHIDs($user_phids) 32 + ->withUsers($users) 33 + ->needSyntheticPreferences(true) 33 34 ->execute(); 34 35 $preferences = mpull($preferences, null, 'getUserPHID'); 35 36 36 - // If some users don't have settings of their own yet, we need to load 37 - // the global default settings to generate caches for them. 38 - if (count($preferences) < count($user_phids)) { 39 - $global = id(new PhabricatorUserPreferencesQuery()) 40 - ->setViewer($viewer) 41 - ->withBuiltinKeys( 42 - array( 43 - PhabricatorUserPreferences::BUILTIN_GLOBAL_DEFAULT, 44 - )) 45 - ->executeOne(); 46 - } else { 47 - $global = null; 48 - } 49 - 50 37 $all_settings = PhabricatorSetting::getAllSettings(); 51 38 52 39 $settings = array(); 53 40 foreach ($users as $user_phid => $user) { 54 - $preference = idx($preferences, $user_phid, $global); 41 + $preference = idx($preferences, $user_phid); 55 42 56 43 if (!$preference) { 57 44 continue;
+4
src/applications/settings/controller/PhabricatorSettingsMainController.php
··· 152 152 if (!$this->isSelf() && !$panel->isManagementPanel()) { 153 153 continue; 154 154 } 155 + 156 + if ($this->isSelf() && !$panel->isUserPanel()) { 157 + continue; 158 + } 155 159 } 156 160 157 161 if (!empty($result[$key])) {
+8
src/applications/settings/panel/PhabricatorEmailFormatSettingsPanel.php
··· 13 13 return PhabricatorSettingsEmailPanelGroup::PANELGROUPKEY; 14 14 } 15 15 16 + public function isUserPanel() { 17 + return PhabricatorMetaMTAMail::shouldMultiplexAllMail(); 18 + } 19 + 16 20 public function isManagementPanel() { 21 + if (!$this->isUserPanel()) { 22 + return false; 23 + } 24 + 17 25 if ($this->getUser()->getIsMailingList()) { 18 26 return true; 19 27 }
+12
src/applications/settings/panel/PhabricatorSettingsPanel.php
··· 155 155 156 156 157 157 /** 158 + * Return true if this panel is available to users while editing their own 159 + * settings. 160 + * 161 + * @return bool True to enable management on behalf of a user. 162 + * @task config 163 + */ 164 + public function isUserPanel() { 165 + return true; 166 + } 167 + 168 + 169 + /** 158 170 * Return true if this panel is available to administrators while managing 159 171 * bot and mailing list accounts. 160 172 *
+28 -1
src/applications/settings/query/PhabricatorUserPreferencesQuery.php
··· 9 9 private $builtinKeys; 10 10 private $hasUserPHID; 11 11 private $users = array(); 12 + private $synthetic; 12 13 13 14 public function withIDs(array $ids) { 14 15 $this->ids = $ids; ··· 42 43 return $this; 43 44 } 44 45 46 + /** 47 + * Always return preferences for every queried user. 48 + * 49 + * If no settings exist for a user, a new empty settings object with 50 + * appropriate defaults is returned. 51 + * 52 + * @param bool True to generat synthetic preferences for missing users. 53 + */ 54 + public function needSyntheticPreferences($synthetic) { 55 + $this->synthetic = $synthetic; 56 + return $this; 57 + } 58 + 45 59 public function newResultObject() { 46 60 return new PhabricatorUserPreferences(); 47 61 } 48 62 49 63 protected function loadPage() { 50 - return $this->loadStandardPage($this->newResultObject()); 64 + $preferences = $this->loadStandardPage($this->newResultObject()); 65 + 66 + if ($this->synthetic) { 67 + $user_map = mpull($preferences, null, 'getUserPHID'); 68 + foreach ($this->userPHIDs as $user_phid) { 69 + if (isset($user_map[$user_phid])) { 70 + continue; 71 + } 72 + $preferences[] = $this->newResultObject() 73 + ->setUserPHID($user_phid); 74 + } 75 + } 76 + 77 + return $preferences; 51 78 } 52 79 53 80 protected function willFilterPage(array $prefs) {
-4
src/applications/settings/setting/PhabricatorEmailFormatSetting.php
··· 20 20 return 100; 21 21 } 22 22 23 - protected function isEnabledForViewer(PhabricatorUser $viewer) { 24 - return PhabricatorMetaMTAMail::shouldMultiplexAllMail(); 25 - } 26 - 27 23 protected function getControlInstructions() { 28 24 return pht( 29 25 'You can opt to receive plain text email from Phabricator instead '.
-4
src/applications/settings/setting/PhabricatorEmailRePrefixSetting.php
··· 20 20 return 200; 21 21 } 22 22 23 - protected function isEnabledForViewer(PhabricatorUser $viewer) { 24 - return PhabricatorMetaMTAMail::shouldMultiplexAllMail(); 25 - } 26 - 27 23 protected function getControlInstructions() { 28 24 return pht( 29 25 'The **Add "Re:" Prefix** setting adds "Re:" in front of all messages, '.
-4
src/applications/settings/setting/PhabricatorEmailVarySubjectsSetting.php
··· 20 20 return 300; 21 21 } 22 22 23 - protected function isEnabledForViewer(PhabricatorUser $viewer) { 24 - return PhabricatorMetaMTAMail::shouldMultiplexAllMail(); 25 - } 26 - 27 23 protected function getControlInstructions() { 28 24 return pht( 29 25 'With **Vary Subjects** enabled, most mail subject lines will include '.
+16 -12
src/applications/settings/storage/PhabricatorUserPreferences.php
··· 122 122 * @param PhabricatorUser User to load or create preferences for. 123 123 */ 124 124 public static function loadUserPreferences(PhabricatorUser $user) { 125 - $preferences = id(new PhabricatorUserPreferencesQuery()) 125 + return id(new PhabricatorUserPreferencesQuery()) 126 126 ->setViewer($user) 127 127 ->withUsers(array($user)) 128 + ->needSyntheticPreferences(true) 128 129 ->executeOne(); 129 - if ($preferences) { 130 - return $preferences; 131 - } 132 - 133 - $preferences = id(new self()) 134 - ->setUserPHID($user->getPHID()) 135 - ->attachUser($user); 130 + } 136 131 132 + /** 133 + * Load or create a global preferences object. 134 + * 135 + * If no global preferences exist, an empty preferences object is returned. 136 + * 137 + * @param PhabricatorUser Viewing user. 138 + */ 139 + public static function loadGlobalPreferences(PhabricatorUser $viewer) { 137 140 $global = id(new PhabricatorUserPreferencesQuery()) 138 - ->setViewer($user) 141 + ->setViewer($viewer) 139 142 ->withBuiltinKeys( 140 143 array( 141 144 self::BUILTIN_GLOBAL_DEFAULT, 142 145 )) 143 146 ->executeOne(); 144 147 145 - if ($global) { 146 - $preferences->attachDefaultSettings($global); 148 + if (!$global) { 149 + $global = id(new self()) 150 + ->attachUser(new PhabricatorUser()); 147 151 } 148 152 149 - return $preferences; 153 + return $global; 150 154 } 151 155 152 156 public function newTransaction($key, $value) {