mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-24 06:20:56 +01:00
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
This commit is contained in:
parent
f6f9d78f3a
commit
c443913c0b
5 changed files with 123 additions and 43 deletions
|
@ -11,7 +11,16 @@ final class PhabricatorFeedStoryPublisher {
|
||||||
private $subscribedPHIDs = array();
|
private $subscribedPHIDs = array();
|
||||||
private $mailRecipientPHIDs = array();
|
private $mailRecipientPHIDs = array();
|
||||||
private $notifyAuthor;
|
private $notifyAuthor;
|
||||||
|
private $mailTags = array();
|
||||||
|
|
||||||
|
public function setMailTags(array $mail_tags) {
|
||||||
|
$this->mailTags = $mail_tags;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMailTags() {
|
||||||
|
return $this->mailTags;
|
||||||
|
}
|
||||||
|
|
||||||
public function setNotifyAuthor($notify_author) {
|
public function setNotifyAuthor($notify_author) {
|
||||||
$this->notifyAuthor = $notify_author;
|
$this->notifyAuthor = $notify_author;
|
||||||
|
@ -111,8 +120,12 @@ final class PhabricatorFeedStoryPublisher {
|
||||||
implode(', ', $sql));
|
implode(', ', $sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->insertNotifications($chrono_key);
|
$subscribed_phids = $this->subscribedPHIDs;
|
||||||
$this->sendNotification($chrono_key);
|
$subscribed_phids = $this->filterSubscribedPHIDs($subscribed_phids);
|
||||||
|
if ($subscribed_phids) {
|
||||||
|
$this->insertNotifications($chrono_key, $subscribed_phids);
|
||||||
|
$this->sendNotification($chrono_key, $subscribed_phids);
|
||||||
|
}
|
||||||
|
|
||||||
PhabricatorWorker::scheduleTask(
|
PhabricatorWorker::scheduleTask(
|
||||||
'FeedPublisherWorker',
|
'FeedPublisherWorker',
|
||||||
|
@ -123,19 +136,7 @@ final class PhabricatorFeedStoryPublisher {
|
||||||
return $story;
|
return $story;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function insertNotifications($chrono_key) {
|
private function insertNotifications($chrono_key, array $subscribed_phids) {
|
||||||
$subscribed_phids = $this->subscribedPHIDs;
|
|
||||||
|
|
||||||
if (!$this->notifyAuthor) {
|
|
||||||
$subscribed_phids = array_diff(
|
|
||||||
$subscribed_phids,
|
|
||||||
array($this->storyAuthorPHID));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$subscribed_phids) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$this->primaryObjectPHID) {
|
if (!$this->primaryObjectPHID) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
'You must call setPrimaryObjectPHID() if you setSubscribedPHIDs()!');
|
'You must call setPrimaryObjectPHID() if you setSubscribedPHIDs()!');
|
||||||
|
@ -165,23 +166,71 @@ final class PhabricatorFeedStoryPublisher {
|
||||||
|
|
||||||
queryfx(
|
queryfx(
|
||||||
$conn,
|
$conn,
|
||||||
'INSERT INTO %T
|
'INSERT INTO %T (primaryObjectPHID, userPHID, chronologicalKey, hasViewed)
|
||||||
(primaryObjectPHID, userPHID, chronologicalKey, hasViewed)
|
VALUES %Q',
|
||||||
VALUES %Q',
|
|
||||||
$notif->getTableName(),
|
$notif->getTableName(),
|
||||||
implode(', ', $sql));
|
implode(', ', $sql));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sendNotification($chrono_key) {
|
private function sendNotification($chrono_key, array $subscribed_phids) {
|
||||||
$data = array(
|
$data = array(
|
||||||
'key' => (string)$chrono_key,
|
'key' => (string)$chrono_key,
|
||||||
'type' => 'notification',
|
'type' => 'notification',
|
||||||
'subscribers' => array_values($this->subscribedPHIDs),
|
'subscribers' => $subscribed_phids,
|
||||||
);
|
);
|
||||||
|
|
||||||
PhabricatorNotificationClient::tryToPostMessage($data);
|
PhabricatorNotificationClient::tryToPostMessage($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove PHIDs who should not receive notifications from a subscriber list.
|
||||||
|
*
|
||||||
|
* @param list<phid> List of potential subscribers.
|
||||||
|
* @return list<phid> List of actual subscribers.
|
||||||
|
*/
|
||||||
|
private function filterSubscribedPHIDs(array $phids) {
|
||||||
|
$tags = $this->getMailTags();
|
||||||
|
if ($tags) {
|
||||||
|
$all_prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
|
||||||
|
'userPHID in (%Ls)',
|
||||||
|
$phids);
|
||||||
|
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
|
||||||
|
}
|
||||||
|
|
||||||
|
$pref_default = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL;
|
||||||
|
$pref_ignore = PhabricatorUserPreferences::MAILTAG_PREFERENCE_IGNORE;
|
||||||
|
|
||||||
|
$keep = array();
|
||||||
|
foreach ($phids as $phid) {
|
||||||
|
if (($phid == $this->storyAuthorPHID) && !$this->getNotifyAuthor()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($tags && isset($all_prefs[$phid])) {
|
||||||
|
$mailtags = $all_prefs[$phid]->getPreference(
|
||||||
|
PhabricatorUserPreferences::PREFERENCE_MAILTAGS,
|
||||||
|
array());
|
||||||
|
|
||||||
|
$notify = false;
|
||||||
|
foreach ($tags as $tag) {
|
||||||
|
// If this is set to "email" or "notify", notify the user.
|
||||||
|
if ((int)idx($mailtags, $tag, $pref_default) != $pref_ignore) {
|
||||||
|
$notify = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$notify) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$keep[] = $phid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(array_unique($keep));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* We generate a unique chronological key for each story type because we want
|
* We generate a unique chronological key for each story type because we want
|
||||||
* to be able to page through the stream with a cursor (i.e., select stories
|
* to be able to page through the stream with a cursor (i.e., select stories
|
||||||
|
|
|
@ -876,6 +876,8 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL;
|
||||||
|
|
||||||
// Exclude all recipients who have set preferences to not receive this type
|
// Exclude all recipients who have set preferences to not receive this type
|
||||||
// of email (for example, a user who says they don't want emails about task
|
// of email (for example, a user who says they don't want emails about task
|
||||||
// CC changes).
|
// CC changes).
|
||||||
|
@ -890,7 +892,7 @@ final class PhabricatorMetaMTAMail extends PhabricatorMetaMTADAO {
|
||||||
// of the mailtags.
|
// of the mailtags.
|
||||||
$send = false;
|
$send = false;
|
||||||
foreach ($tags as $tag) {
|
foreach ($tags as $tag) {
|
||||||
if (idx($user_mailtags, $tag, true)) {
|
if (((int)idx($user_mailtags, $tag, $value_email)) == $value_email) {
|
||||||
$send = true;
|
$send = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,8 @@ final class PhabricatorSettingsPanelEmailPreferences
|
||||||
$pref_no_mail = PhabricatorUserPreferences::PREFERENCE_NO_MAIL;
|
$pref_no_mail = PhabricatorUserPreferences::PREFERENCE_NO_MAIL;
|
||||||
$pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL;
|
$pref_no_self_mail = PhabricatorUserPreferences::PREFERENCE_NO_SELF_MAIL;
|
||||||
|
|
||||||
|
$value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL;
|
||||||
|
|
||||||
$errors = array();
|
$errors = array();
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
$preferences->setPreference(
|
$preferences->setPreference(
|
||||||
|
@ -38,7 +40,7 @@ final class PhabricatorSettingsPanelEmailPreferences
|
||||||
$all_tags = $this->getAllTags($user);
|
$all_tags = $this->getAllTags($user);
|
||||||
|
|
||||||
foreach ($all_tags as $key => $label) {
|
foreach ($all_tags as $key => $label) {
|
||||||
$mailtags[$key] = (bool)idx($new_tags, $key, false);
|
$mailtags[$key] = (int)idx($new_tags, $key, $value_email);
|
||||||
}
|
}
|
||||||
$preferences->setPreference('mailtags', $mailtags);
|
$preferences->setPreference('mailtags', $mailtags);
|
||||||
|
|
||||||
|
@ -96,26 +98,24 @@ final class PhabricatorSettingsPanelEmailPreferences
|
||||||
|
|
||||||
$form->appendRemarkupInstructions(
|
$form->appendRemarkupInstructions(
|
||||||
pht(
|
pht(
|
||||||
'You can customize which kinds of events you receive email for '.
|
'You can adjust **Application Settings** here to customize when '.
|
||||||
'here. If you turn off email for a certain type of event, you '.
|
'you are emailed and notified.'.
|
||||||
'will receive an unread notification in Phabricator instead.'.
|
|
||||||
"\n\n".
|
"\n\n".
|
||||||
'Phabricator notifications (shown in the menu bar) which you receive '.
|
"| Setting | Effect\n".
|
||||||
'an email for are marked read by default in Phabricator. If you turn '.
|
"| ------- | -------\n".
|
||||||
'off email for a certain type of event, the corresponding '.
|
"| Email | You will receive an email and a notification, but the ".
|
||||||
'notification will not be marked read.'.
|
"notification will be marked \"read\".\n".
|
||||||
|
"| Notify | You will receive an unread notification only.\n".
|
||||||
|
"| Ignore | You will receive nothing.\n".
|
||||||
"\n\n".
|
"\n\n".
|
||||||
'Note that if an update makes several changes (like adding CCs to a '.
|
'If an update makes several changes (like adding CCs to a task, '.
|
||||||
'task, closing it, and adding a comment) you will still receive '.
|
'closing it, and adding a comment) you will receive the strongest '.
|
||||||
'an email as long as at least one of the changes is set to notify '.
|
'notification any of the changes is configured to deliver.'.
|
||||||
'you.'.
|
|
||||||
"\n\n".
|
"\n\n".
|
||||||
'These preferences **only** apply to objects you are connected to '.
|
'These preferences **only** apply to objects you are connected to '.
|
||||||
'(for example, Revisions where you are a reviewer or tasks you are '.
|
'(for example, Revisions where you are a reviewer or tasks you are '.
|
||||||
'CC\'d on). To receive email alerts when other objects are created, '.
|
'CC\'d on). To receive email alerts when other objects are created, '.
|
||||||
'configure [[ /herald/ | Herald Rules ]].'.
|
'configure [[ /herald/ | Herald Rules ]].'));
|
||||||
"\n\n".
|
|
||||||
'Phabricator will send an email to your primary account when:'));
|
|
||||||
|
|
||||||
$editors = $this->getAllEditorsWithTags($user);
|
$editors = $this->getAllEditorsWithTags($user);
|
||||||
|
|
||||||
|
@ -216,16 +216,39 @@ final class PhabricatorSettingsPanelEmailPreferences
|
||||||
array $tags,
|
array $tags,
|
||||||
array $prefs) {
|
array $prefs) {
|
||||||
|
|
||||||
$control = new AphrontFormCheckboxControl();
|
$value_email = PhabricatorUserPreferences::MAILTAG_PREFERENCE_EMAIL;
|
||||||
$control->setLabel($control_label);
|
$value_notify = PhabricatorUserPreferences::MAILTAG_PREFERENCE_NOTIFY;
|
||||||
|
$value_ignore = PhabricatorUserPreferences::MAILTAG_PREFERENCE_IGNORE;
|
||||||
|
|
||||||
|
$content = array();
|
||||||
foreach ($tags as $key => $label) {
|
foreach ($tags as $key => $label) {
|
||||||
$control->addCheckbox(
|
$select = AphrontFormSelectControl::renderSelectTag(
|
||||||
'mailtags['.$key.']',
|
(int)idx($prefs, $key, $value_email),
|
||||||
1,
|
array(
|
||||||
$label,
|
$value_email => pht("\xE2\x9A\xAB Email"),
|
||||||
idx($prefs, $key, 1));
|
$value_notify => pht("\xE2\x97\x90 Notify"),
|
||||||
|
$value_ignore => pht("\xE2\x9A\xAA Ignore"),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'mailtags['.$key.']',
|
||||||
|
));
|
||||||
|
|
||||||
|
$content[] = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'psb',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
$select,
|
||||||
|
' ',
|
||||||
|
$label,
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$control = new AphrontFormStaticControl();
|
||||||
|
$control->setLabel($control_label);
|
||||||
|
$control->setValue($content);
|
||||||
|
|
||||||
return $control;
|
return $control;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,11 @@ final class PhabricatorUserPreferences extends PhabricatorUserDAO {
|
||||||
|
|
||||||
const PREFERENCE_CONPH_NOTIFICATIONS = 'conph-notifications';
|
const PREFERENCE_CONPH_NOTIFICATIONS = 'conph-notifications';
|
||||||
|
|
||||||
|
// These are in an unusual order for historic reasons.
|
||||||
|
const MAILTAG_PREFERENCE_NOTIFY = 0;
|
||||||
|
const MAILTAG_PREFERENCE_EMAIL = 1;
|
||||||
|
const MAILTAG_PREFERENCE_IGNORE = 2;
|
||||||
|
|
||||||
protected $userPHID;
|
protected $userPHID;
|
||||||
protected $preferences = array();
|
protected $preferences = array();
|
||||||
|
|
||||||
|
|
|
@ -2203,6 +2203,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
->setPrimaryObjectPHID($object->getPHID())
|
->setPrimaryObjectPHID($object->getPHID())
|
||||||
->setSubscribedPHIDs($subscribed_phids)
|
->setSubscribedPHIDs($subscribed_phids)
|
||||||
->setMailRecipientPHIDs($mailed_phids)
|
->setMailRecipientPHIDs($mailed_phids)
|
||||||
|
->setMailTags($this->getMailTags($object, $xactions))
|
||||||
->publish();
|
->publish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue