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

Allow users to set notifications to "Email", "Notification", or "Ignore"

Summary:
Ref T5861. Ref T5769. If users don't care at all about something, allow them to ignore it.

We have some higher-volume notifications either built now (column changes) or coming (mentions) which users might reasonably want to ignore completely.

Test Plan:
Ignored some notifications, then took appropraite actions. Saw my user culled from the notification subscriber list.

{F189531}

Reviewers: chad, btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T5769, T5861

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

+123 -43
+69 -20
src/applications/feed/PhabricatorFeedStoryPublisher.php
··· 11 11 private $subscribedPHIDs = array(); 12 12 private $mailRecipientPHIDs = array(); 13 13 private $notifyAuthor; 14 + private $mailTags = array(); 14 15 16 + public function setMailTags(array $mail_tags) { 17 + $this->mailTags = $mail_tags; 18 + return $this; 19 + } 20 + 21 + public function getMailTags() { 22 + return $this->mailTags; 23 + } 15 24 16 25 public function setNotifyAuthor($notify_author) { 17 26 $this->notifyAuthor = $notify_author; ··· 111 120 implode(', ', $sql)); 112 121 } 113 122 114 - $this->insertNotifications($chrono_key); 115 - $this->sendNotification($chrono_key); 123 + $subscribed_phids = $this->subscribedPHIDs; 124 + $subscribed_phids = $this->filterSubscribedPHIDs($subscribed_phids); 125 + if ($subscribed_phids) { 126 + $this->insertNotifications($chrono_key, $subscribed_phids); 127 + $this->sendNotification($chrono_key, $subscribed_phids); 128 + } 116 129 117 130 PhabricatorWorker::scheduleTask( 118 131 'FeedPublisherWorker', ··· 123 136 return $story; 124 137 } 125 138 126 - private function insertNotifications($chrono_key) { 127 - $subscribed_phids = $this->subscribedPHIDs; 128 - 129 - if (!$this->notifyAuthor) { 130 - $subscribed_phids = array_diff( 131 - $subscribed_phids, 132 - array($this->storyAuthorPHID)); 133 - } 134 - 135 - if (!$subscribed_phids) { 136 - return; 137 - } 138 - 139 + private function insertNotifications($chrono_key, array $subscribed_phids) { 139 140 if (!$this->primaryObjectPHID) { 140 141 throw new Exception( 141 142 'You must call setPrimaryObjectPHID() if you setSubscribedPHIDs()!'); ··· 165 166 166 167 queryfx( 167 168 $conn, 168 - 'INSERT INTO %T 169 - (primaryObjectPHID, userPHID, chronologicalKey, hasViewed) 170 - VALUES %Q', 169 + 'INSERT INTO %T (primaryObjectPHID, userPHID, chronologicalKey, hasViewed) 170 + VALUES %Q', 171 171 $notif->getTableName(), 172 172 implode(', ', $sql)); 173 173 } 174 174 175 - private function sendNotification($chrono_key) { 175 + private function sendNotification($chrono_key, array $subscribed_phids) { 176 176 $data = array( 177 177 'key' => (string)$chrono_key, 178 178 'type' => 'notification', 179 - 'subscribers' => array_values($this->subscribedPHIDs), 179 + 'subscribers' => $subscribed_phids, 180 180 ); 181 181 182 182 PhabricatorNotificationClient::tryToPostMessage($data); 183 + } 184 + 185 + /** 186 + * Remove PHIDs who should not receive notifications from a subscriber list. 187 + * 188 + * @param list<phid> List of potential subscribers. 189 + * @return list<phid> List of actual subscribers. 190 + */ 191 + private function filterSubscribedPHIDs(array $phids) { 192 + $tags = $this->getMailTags(); 193 + if ($tags) { 194 + $all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere( 195 + 'userPHID in (%Ls)', 196 + $phids); 197 + $all_prefs = mpull($all_prefs, null, 'getUserPHID'); 198 + } 199 + 200 + $pref_default = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; 201 + $pref_ignore = PhabricatorUserPreferences::MAILTAG_PREFERENCE_IGNORE; 202 + 203 + $keep = array(); 204 + foreach ($phids as $phid) { 205 + if (($phid == $this->storyAuthorPHID) && !$this->getNotifyAuthor()) { 206 + continue; 207 + } 208 + 209 + if ($tags && isset($all_prefs[$phid])) { 210 + $mailtags = $all_prefs[$phid]->getPreference( 211 + PhabricatorUserPreferences::PREFERENCE_MAILTAGS, 212 + array()); 213 + 214 + $notify = false; 215 + foreach ($tags as $tag) { 216 + // If this is set to "email" or "notify", notify the user. 217 + if ((int)idx($mailtags, $tag, $pref_default) != $pref_ignore) { 218 + $notify = true; 219 + break; 220 + } 221 + } 222 + 223 + if (!$notify) { 224 + continue; 225 + } 226 + } 227 + 228 + $keep[] = $phid; 229 + } 230 + 231 + return array_values(array_unique($keep)); 183 232 } 184 233 185 234 /**
+3 -1
src/applications/metamta/storage/PhabricatorMetaMTAMail.php
··· 876 876 } 877 877 } 878 878 879 + $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; 880 + 879 881 // Exclude all recipients who have set preferences to not receive this type 880 882 // of email (for example, a user who says they don't want emails about task 881 883 // CC changes). ··· 890 892 // of the mailtags. 891 893 $send = false; 892 894 foreach ($tags as $tag) { 893 - if (idx($user_mailtags, $tag, true)) { 895 + if (((int)idx($user_mailtags, $tag, $value_email)) == $value_email) { 894 896 $send = true; 895 897 break; 896 898 }
+45 -22
src/applications/settings/panel/PhabricatorSettingsPanelEmailPreferences.php
··· 23 23 $pref_no_mail = PhabricatorUserPreferences::PREFERENCE_NO_MAIL; 24 24 $pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL; 25 25 26 + $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; 27 + 26 28 $errors = array(); 27 29 if ($request->isFormPost()) { 28 30 $preferences->setPreference( ··· 38 40 $all_tags = $this->getAllTags($user); 39 41 40 42 foreach ($all_tags as $key => $label) { 41 - $mailtags[$key] = (bool)idx($new_tags, $key, false); 43 + $mailtags[$key] = (int)idx($new_tags, $key, $value_email); 42 44 } 43 45 $preferences->setPreference('mailtags', $mailtags); 44 46 ··· 96 98 97 99 $form->appendRemarkupInstructions( 98 100 pht( 99 - 'You can customize which kinds of events you receive email for '. 100 - 'here. If you turn off email for a certain type of event, you '. 101 - 'will receive an unread notification in Phabricator instead.'. 101 + 'You can adjust **Application Settings** here to customize when '. 102 + 'you are emailed and notified.'. 102 103 "\n\n". 103 - 'Phabricator notifications (shown in the menu bar) which you receive '. 104 - 'an email for are marked read by default in Phabricator. If you turn '. 105 - 'off email for a certain type of event, the corresponding '. 106 - 'notification will not be marked read.'. 104 + "| Setting | Effect\n". 105 + "| ------- | -------\n". 106 + "| Email | You will receive an email and a notification, but the ". 107 + "notification will be marked \"read\".\n". 108 + "| Notify | You will receive an unread notification only.\n". 109 + "| Ignore | You will receive nothing.\n". 107 110 "\n\n". 108 - 'Note that if an update makes several changes (like adding CCs to a '. 109 - 'task, closing it, and adding a comment) you will still receive '. 110 - 'an email as long as at least one of the changes is set to notify '. 111 - 'you.'. 111 + 'If an update makes several changes (like adding CCs to a task, '. 112 + 'closing it, and adding a comment) you will receive the strongest '. 113 + 'notification any of the changes is configured to deliver.'. 112 114 "\n\n". 113 115 'These preferences **only** apply to objects you are connected to '. 114 116 '(for example, Revisions where you are a reviewer or tasks you are '. 115 117 'CC\'d on). To receive email alerts when other objects are created, '. 116 - 'configure [[ /herald/ | Herald Rules ]].'. 117 - "\n\n". 118 - 'Phabricator will send an email to your primary account when:')); 118 + 'configure [[ /herald/ | Herald Rules ]].')); 119 119 120 120 $editors = $this->getAllEditorsWithTags($user); 121 121 ··· 216 216 array $tags, 217 217 array $prefs) { 218 218 219 - $control = new AphrontFormCheckboxControl(); 220 - $control->setLabel($control_label); 219 + $value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL; 220 + $value_notify = PhabricatorUserPreferences::MAILTAG_PREFERENCE_NOTIFY; 221 + $value_ignore = PhabricatorUserPreferences::MAILTAG_PREFERENCE_IGNORE; 222 + 223 + $content = array(); 221 224 foreach ($tags as $key => $label) { 222 - $control->addCheckbox( 223 - 'mailtags['.$key.']', 224 - 1, 225 - $label, 226 - idx($prefs, $key, 1)); 225 + $select = AphrontFormSelectControl::renderSelectTag( 226 + (int)idx($prefs, $key, $value_email), 227 + array( 228 + $value_email => pht("\xE2\x9A\xAB Email"), 229 + $value_notify => pht("\xE2\x97\x90 Notify"), 230 + $value_ignore => pht("\xE2\x9A\xAA Ignore"), 231 + ), 232 + array( 233 + 'name' => 'mailtags['.$key.']', 234 + )); 235 + 236 + $content[] = phutil_tag( 237 + 'div', 238 + array( 239 + 'class' => 'psb', 240 + ), 241 + array( 242 + $select, 243 + ' ', 244 + $label, 245 + )); 227 246 } 247 + 248 + $control = new AphrontFormStaticControl(); 249 + $control->setLabel($control_label); 250 + $control->setValue($content); 228 251 229 252 return $control; 230 253 }
+5
src/applications/settings/storage/PhabricatorUserPreferences.php
··· 31 31 32 32 const PREFERENCE_CONPH_NOTIFICATIONS = 'conph-notifications'; 33 33 34 + // These are in an unusual order for historic reasons. 35 + const MAILTAG_PREFERENCE_NOTIFY = 0; 36 + const MAILTAG_PREFERENCE_EMAIL = 1; 37 + const MAILTAG_PREFERENCE_IGNORE = 2; 38 + 34 39 protected $userPHID; 35 40 protected $preferences = array(); 36 41
+1
src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
··· 2203 2203 ->setPrimaryObjectPHID($object->getPHID()) 2204 2204 ->setSubscribedPHIDs($subscribed_phids) 2205 2205 ->setMailRecipientPHIDs($mailed_phids) 2206 + ->setMailTags($this->getMailTags($object, $xactions)) 2206 2207 ->publish(); 2207 2208 } 2208 2209