Page MenuHomePhabricator

D19033.diff
No OneTemporary

D19033.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -9,7 +9,7 @@
'names' => array(
'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65',
- 'core.pkg.css' => '51debec3',
+ 'core.pkg.css' => 'ce8c2a58',
'core.pkg.js' => '4c79d74f',
'darkconsole.pkg.js' => '1f9a31bc',
'differential.pkg.css' => '45951e9e',
@@ -136,7 +136,7 @@
'rsrc/css/phui/object-item/phui-oi-flush-ui.css' => '9d9685d6',
'rsrc/css/phui/object-item/phui-oi-list-view.css' => '6ae18df0',
'rsrc/css/phui/object-item/phui-oi-simple-ui.css' => 'a8beebea',
- 'rsrc/css/phui/phui-action-list.css' => 'f7f61a34',
+ 'rsrc/css/phui/phui-action-list.css' => '0bcd9a45',
'rsrc/css/phui/phui-action-panel.css' => 'b4798122',
'rsrc/css/phui/phui-badge.css' => '22c0cf4f',
'rsrc/css/phui/phui-basic-nav-view.css' => '98c11ab3',
@@ -766,7 +766,7 @@
'path-typeahead' => 'f7fc67ec',
'people-picture-menu-item-css' => 'a06f7f34',
'people-profile-css' => '4df76faf',
- 'phabricator-action-list-view-css' => 'f7f61a34',
+ 'phabricator-action-list-view-css' => '0bcd9a45',
'phabricator-busy' => '59a7976a',
'phabricator-chatlog-css' => 'd295b020',
'phabricator-content-source-view-css' => '4b8b05d4',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -3291,6 +3291,8 @@
'PhabricatorMultiFactorSettingsPanel' => 'applications/settings/panel/PhabricatorMultiFactorSettingsPanel.php',
'PhabricatorMultimeterApplication' => 'applications/multimeter/application/PhabricatorMultimeterApplication.php',
'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php',
+ 'PhabricatorMutedByEdgeType' => 'applications/transactions/edges/PhabricatorMutedByEdgeType.php',
+ 'PhabricatorMutedEdgeType' => 'applications/transactions/edges/PhabricatorMutedEdgeType.php',
'PhabricatorMySQLConfigOptions' => 'applications/config/option/PhabricatorMySQLConfigOptions.php',
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php',
'PhabricatorMySQLSearchHost' => 'infrastructure/cluster/search/PhabricatorMySQLSearchHost.php',
@@ -4240,6 +4242,7 @@
'PhabricatorSubscriptionsHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsHeraldAction.php',
'PhabricatorSubscriptionsListController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsListController.php',
'PhabricatorSubscriptionsMailEngineExtension' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsMailEngineExtension.php',
+ 'PhabricatorSubscriptionsMuteController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsMuteController.php',
'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSelfHeraldAction.php',
'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'applications/subscriptions/herald/PhabricatorSubscriptionsRemoveSubscribersHeraldAction.php',
'PhabricatorSubscriptionsSearchEngineAttachment' => 'applications/subscriptions/engineextension/PhabricatorSubscriptionsSearchEngineAttachment.php',
@@ -8808,6 +8811,8 @@
'PhabricatorMultiFactorSettingsPanel' => 'PhabricatorSettingsPanel',
'PhabricatorMultimeterApplication' => 'PhabricatorApplication',
'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController',
+ 'PhabricatorMutedByEdgeType' => 'PhabricatorEdgeType',
+ 'PhabricatorMutedEdgeType' => 'PhabricatorEdgeType',
'PhabricatorMySQLConfigOptions' => 'PhabricatorApplicationConfigOptions',
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
'PhabricatorMySQLSearchHost' => 'PhabricatorSearchHost',
@@ -9960,6 +9965,7 @@
'PhabricatorSubscriptionsHeraldAction' => 'HeraldAction',
'PhabricatorSubscriptionsListController' => 'PhabricatorController',
'PhabricatorSubscriptionsMailEngineExtension' => 'PhabricatorMailEngineExtension',
+ 'PhabricatorSubscriptionsMuteController' => 'PhabricatorController',
'PhabricatorSubscriptionsRemoveSelfHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
'PhabricatorSubscriptionsRemoveSubscribersHeraldAction' => 'PhabricatorSubscriptionsHeraldAction',
'PhabricatorSubscriptionsSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
diff --git a/src/applications/metamta/query/PhabricatorMetaMTAActor.php b/src/applications/metamta/query/PhabricatorMetaMTAActor.php
--- a/src/applications/metamta/query/PhabricatorMetaMTAActor.php
+++ b/src/applications/metamta/query/PhabricatorMetaMTAActor.php
@@ -21,6 +21,7 @@
const REASON_ROUTE_AS_NOTIFICATION = 'route-as-notification';
const REASON_ROUTE_AS_MAIL = 'route-as-mail';
const REASON_UNVERIFIED = 'unverified';
+ const REASON_MUTED = 'muted';
private $phid;
private $emailAddress;
@@ -116,6 +117,7 @@
self::REASON_ROUTE_AS_NOTIFICATION => pht('Route as Notification'),
self::REASON_ROUTE_AS_MAIL => pht('Route as Mail'),
self::REASON_UNVERIFIED => pht('Address Not Verified'),
+ self::REASON_MUTED => pht('Muted'),
);
return idx($names, $reason, pht('Unknown ("%s")', $reason));
@@ -172,6 +174,8 @@
'in Herald.'),
self::REASON_UNVERIFIED => pht(
'This recipient does not have a verified primary email address.'),
+ self::REASON_MUTED => pht(
+ 'This recipient has muted notifications for this object.'),
);
return idx($descriptions, $reason, pht('Unknown Reason ("%s")', $reason));
diff --git a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
--- a/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
+++ b/src/applications/metamta/storage/PhabricatorMetaMTAMail.php
@@ -160,6 +160,15 @@
return $this->getParam('exclude', array());
}
+ public function setMutedPHIDs(array $muted) {
+ $this->setParam('muted', $muted);
+ return $this;
+ }
+
+ private function getMutedPHIDs() {
+ return $this->getParam('muted', array());
+ }
+
public function setForceHeraldMailRecipientPHIDs(array $force) {
$this->setParam('herald-force-recipients', $force);
return $this;
@@ -1113,6 +1122,18 @@
}
}
+ // Exclude muted recipients. We're doing this after saving deliverability
+ // so that Herald "Send me an email" actions can still punch through a
+ // mute.
+
+ foreach ($this->getMutedPHIDs() as $muted_phid) {
+ $muted_actor = idx($actors, $muted_phid);
+ if (!$muted_actor) {
+ continue;
+ }
+ $muted_actor->setUndeliverable(PhabricatorMetaMTAActor::REASON_MUTED);
+ }
+
// For the rest of the rules, order matters. We're going to run all the
// possible rules in order from weakest to strongest, and let the strongest
// matching rule win. The weaker rules leave annotations behind which help
diff --git a/src/applications/subscriptions/application/PhabricatorSubscriptionsApplication.php b/src/applications/subscriptions/application/PhabricatorSubscriptionsApplication.php
--- a/src/applications/subscriptions/application/PhabricatorSubscriptionsApplication.php
+++ b/src/applications/subscriptions/application/PhabricatorSubscriptionsApplication.php
@@ -24,7 +24,10 @@
return array(
'/subscriptions/' => array(
'(?P<action>add|delete)/'.
- '(?P<phid>[^/]+)/' => 'PhabricatorSubscriptionsEditController',
+ '(?P<phid>[^/]+)/' => 'PhabricatorSubscriptionsEditController',
+ 'mute/' => array(
+ '(?P<phid>[^/]+)/' => 'PhabricatorSubscriptionsMuteController',
+ ),
'list/(?P<phid>[^/]+)/' => 'PhabricatorSubscriptionsListController',
'transaction/(?P<type>add|rem)/(?<phid>[^/]+)/'
=> 'PhabricatorSubscriptionsTransactionController',
diff --git a/src/applications/subscriptions/controller/PhabricatorSubscriptionsMuteController.php b/src/applications/subscriptions/controller/PhabricatorSubscriptionsMuteController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/subscriptions/controller/PhabricatorSubscriptionsMuteController.php
@@ -0,0 +1,92 @@
+<?php
+
+final class PhabricatorSubscriptionsMuteController
+ extends PhabricatorController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $phid = $request->getURIData('phid');
+
+ $handle = id(new PhabricatorHandleQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($phid))
+ ->executeOne();
+
+ $object = id(new PhabricatorObjectQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($phid))
+ ->executeOne();
+
+ if (!($object instanceof PhabricatorSubscribableInterface)) {
+ return new Aphront400Response();
+ }
+
+ $muted_type = PhabricatorMutedByEdgeType::EDGECONST;
+
+ $edge_query = id(new PhabricatorEdgeQuery())
+ ->withSourcePHIDs(array($object->getPHID()))
+ ->withEdgeTypes(array($muted_type))
+ ->withDestinationPHIDs(array($viewer->getPHID()));
+
+ $edge_query->execute();
+
+ $is_mute = !$edge_query->getDestinationPHIDs();
+ $object_uri = $handle->getURI();
+
+ if ($request->isFormPost()) {
+ if ($is_mute) {
+ $xaction_value = array(
+ '+' => array_fuse(array($viewer->getPHID())),
+ );
+ } else {
+ $xaction_value = array(
+ '-' => array_fuse(array($viewer->getPHID())),
+ );
+ }
+
+ $muted_type = PhabricatorMutedByEdgeType::EDGECONST;
+
+ $xaction = id($object->getApplicationTransactionTemplate())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+ ->setMetadataValue('edge:type', $muted_type)
+ ->setNewValue($xaction_value);
+
+ $editor = id($object->getApplicationTransactionEditor())
+ ->setActor($viewer)
+ ->setContinueOnNoEffect(true)
+ ->setContinueOnMissingFields(true)
+ ->setContentSourceFromRequest($request);
+
+ $editor->applyTransactions(
+ $object->getApplicationTransactionObject(),
+ array($xaction));
+
+ return id(new AphrontReloadResponse())->setURI($object_uri);
+ }
+
+ $dialog = $this->newDialog()
+ ->addCancelButton($object_uri);
+
+ if ($is_mute) {
+ $dialog
+ ->setTitle(pht('Mute Notifications'))
+ ->appendParagraph(
+ pht(
+ 'Mute this object? You will no longer receive notifications or '.
+ 'email about it.'))
+ ->addSubmitButton(pht('Mute'));
+ } else {
+ $dialog
+ ->setTitle(pht('Unmute Notifications'))
+ ->appendParagraph(
+ pht(
+ 'Unmute this object? You will receive notifications and email '.
+ 'again.'))
+ ->addSubmitButton(pht('Unmute'));
+ }
+
+ return $dialog;
+ }
+
+
+}
diff --git a/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php
--- a/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php
+++ b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php
@@ -42,6 +42,28 @@
return;
}
+ $src_phid = $object->getPHID();
+ $subscribed_type = PhabricatorObjectHasSubscriberEdgeType::EDGECONST;
+ $muted_type = PhabricatorMutedByEdgeType::EDGECONST;
+
+ $edges = id(new PhabricatorEdgeQuery())
+ ->withSourcePHIDs(array($src_phid))
+ ->withEdgeTypes(
+ array(
+ $subscribed_type,
+ $muted_type,
+ ))
+ ->withDestinationPHIDs(array($user_phid))
+ ->execute();
+
+ if ($user_phid) {
+ $is_subscribed = isset($edges[$src_phid][$subscribed_type][$user_phid]);
+ $is_muted = isset($edges[$src_phid][$muted_type][$user_phid]);
+ } else {
+ $is_subscribed = false;
+ $is_muted = false;
+ }
+
if ($user_phid && $object->isAutomaticallySubscribed($user_phid)) {
$sub_action = id(new PhabricatorActionView())
->setWorkflow(true)
@@ -51,22 +73,9 @@
->setName(pht('Automatically Subscribed'))
->setIcon('fa-check-circle lightgreytext');
} else {
- $subscribed = false;
- if ($user->isLoggedIn()) {
- $src_phid = $object->getPHID();
- $edge_type = PhabricatorObjectHasSubscriberEdgeType::EDGECONST;
-
- $edges = id(new PhabricatorEdgeQuery())
- ->withSourcePHIDs(array($src_phid))
- ->withEdgeTypes(array($edge_type))
- ->withDestinationPHIDs(array($user_phid))
- ->execute();
- $subscribed = isset($edges[$src_phid][$edge_type][$user_phid]);
- }
-
$can_interact = PhabricatorPolicyFilter::canInteract($user, $object);
- if ($subscribed) {
+ if ($is_subscribed) {
$sub_action = id(new PhabricatorActionView())
->setWorkflow(true)
->setRenderAsForm(true)
@@ -89,8 +98,26 @@
}
}
+ $mute_action = id(new PhabricatorActionView())
+ ->setWorkflow(true)
+ ->setHref('/subscriptions/mute/'.$object->getPHID().'/')
+ ->setDisabled(!$user_phid);
+
+ if (!$is_muted) {
+ $mute_action
+ ->setName(pht('Mute Notifications'))
+ ->setIcon('fa-volume-up');
+ } else {
+ $mute_action
+ ->setName(pht('Unmute Notifications'))
+ ->setIcon('fa-volume-off')
+ ->setColor(PhabricatorActionView::RED);
+ }
+
+
$actions = $event->getValue('actions');
$actions[] = $sub_action;
+ $actions[] = $mute_action;
$event->setValue('actions', $actions);
}
diff --git a/src/applications/transactions/edges/PhabricatorMutedByEdgeType.php b/src/applications/transactions/edges/PhabricatorMutedByEdgeType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/transactions/edges/PhabricatorMutedByEdgeType.php
@@ -0,0 +1,16 @@
+<?php
+
+final class PhabricatorMutedByEdgeType
+ extends PhabricatorEdgeType {
+
+ const EDGECONST = 68;
+
+ public function getInverseEdgeConstant() {
+ return PhabricatorMutedEdgeType::EDGECONST;
+ }
+
+ public function shouldWriteInverseTransactions() {
+ return true;
+ }
+
+}
diff --git a/src/applications/transactions/edges/PhabricatorMutedEdgeType.php b/src/applications/transactions/edges/PhabricatorMutedEdgeType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/transactions/edges/PhabricatorMutedEdgeType.php
@@ -0,0 +1,16 @@
+<?php
+
+final class PhabricatorMutedEdgeType
+ extends PhabricatorEdgeType {
+
+ const EDGECONST = 67;
+
+ public function getInverseEdgeConstant() {
+ return PhabricatorMutedByEdgeType::EDGECONST;
+ }
+
+ public function shouldWriteInverseTransactions() {
+ return true;
+ }
+
+}
diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
--- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
+++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php
@@ -78,6 +78,7 @@
private $oldCC = array();
private $mailRemovedPHIDs = array();
private $mailUnexpandablePHIDs = array();
+ private $mailMutedPHIDs = array();
private $transactionQueue = array();
@@ -1211,6 +1212,14 @@
// but were removed by this change.
$this->applyOldRecipientLists();
+ if ($object instanceof PhabricatorSubscribableInterface) {
+ $this->mailMutedPHIDs = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $object->getPHID(),
+ PhabricatorMutedByEdgeType::EDGECONST);
+ } else {
+ $this->mailMutedPHIDs = array();
+ }
+
$mail_xactions = $this->getTransactionsForMail($object, $xactions);
$stamps = $this->newMailStamps($object, $xactions);
foreach ($stamps as $stamp) {
@@ -2662,6 +2671,11 @@
$mail_xactions);
}
+ $muted_phids = $this->mailMutedPHIDs;
+ if (!is_array($muted_phids)) {
+ $muted_phids = array();
+ }
+
$mail
->setSensitiveContent(false)
->setFrom($this->getActingAsPHID())
@@ -2670,6 +2684,7 @@
->setThreadID($this->getMailThreadID($object), $this->getIsNewObject())
->setRelatedPHID($object->getPHID())
->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs())
+ ->setMutedPHIDs($muted_phids)
->setForceHeraldMailRecipientPHIDs($this->heraldForcedEmailPHIDs)
->setMailTags($mail_tags)
->setIsBulk(true)
@@ -3186,6 +3201,18 @@
$related_phids = $this->feedRelatedPHIDs;
$subscribed_phids = $this->feedNotifyPHIDs;
+ // Remove muted users from the subscription list so they don't get
+ // notifications, either.
+ $muted_phids = $this->mailMutedPHIDs;
+ if (!is_array($muted_phids)) {
+ $muted_phids = array();
+ }
+ $subscribed_phids = array_fuse($subscribed_phids);
+ foreach ($muted_phids as $muted_phid) {
+ unset($subscribed_phids[$muted_phid]);
+ }
+ $subscribed_phids = array_values($subscribed_phids);
+
$story_type = $this->getFeedStoryType();
$story_data = $this->getFeedStoryData($object, $xactions);
@@ -3632,6 +3659,7 @@
'mustEncrypt',
'mailStamps',
'mailUnexpandablePHIDs',
+ 'mailMutedPHIDs',
);
}
diff --git a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php
--- a/src/applications/transactions/storage/PhabricatorApplicationTransaction.php
+++ b/src/applications/transactions/storage/PhabricatorApplicationTransaction.php
@@ -643,6 +643,8 @@
case PhabricatorObjectMentionsObjectEdgeType::EDGECONST:
case ManiphestTaskHasDuplicateTaskEdgeType::EDGECONST:
case ManiphestTaskIsDuplicateOfTaskEdgeType::EDGECONST:
+ case PhabricatorMutedEdgeType::EDGECONST:
+ case PhabricatorMutedByEdgeType::EDGECONST:
return true;
break;
case PhabricatorObjectMentionedByObjectEdgeType::EDGECONST:
diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php
--- a/src/view/layout/PhabricatorActionView.php
+++ b/src/view/layout/PhabricatorActionView.php
@@ -21,6 +21,7 @@
private $order;
private $color;
private $type;
+ private $highlight;
const TYPE_DIVIDER = 'type-divider';
const TYPE_LABEL = 'label';
@@ -72,6 +73,15 @@
return $this->href;
}
+ public function setHighlight($highlight) {
+ $this->highlight = $highlight;
+ return $this;
+ }
+
+ public function getHighlight() {
+ return $this->highlight;
+ }
+
public function setIcon($icon) {
$this->icon = $icon;
return $this;
diff --git a/webroot/rsrc/css/phui/phui-action-list.css b/webroot/rsrc/css/phui/phui-action-list.css
--- a/webroot/rsrc/css/phui/phui-action-list.css
+++ b/webroot/rsrc/css/phui/phui-action-list.css
@@ -95,15 +95,20 @@
color: {$sky};
}
-.device-desktop .phabricator-action-view-href.action-item-red:hover
- .phabricator-action-view-item {
- background-color: {$sh-redbackground};
- color: {$sh-redtext};
+.phabricator-action-view.action-item-red {
+ background-color: {$sh-redbackground};
+}
+
+.phabricator-action-view.action-item-red .phabricator-action-view-item,
+.phabricator-action-view.action-item-red .phabricator-action-view-icon {
+ color: {$sh-redtext};
}
-.device-desktop .phabricator-action-view-href.action-item-red:hover
+.device-desktop .phabricator-action-view.action-item-red:hover
+ .phabricator-action-view-item,
+.device-desktop .phabricator-action-view.action-item-red:hover
.phabricator-action-view-icon {
- color: {$red};
+ color: {$red};
}
.phabricator-action-view-label .phabricator-action-view-item,

File Metadata

Mime Type
text/plain
Expires
Sun, Mar 16, 11:20 PM (1 w, 2 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7616377
Default Alt Text
D19033.diff (19 KB)

Event Timeline