Page MenuHomePhabricator

D15065.diff
No OneTemporary

D15065.diff

diff --git a/resources/sql/autopatches/20160119.project.1.silence.sql b/resources/sql/autopatches/20160119.project.1.silence.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20160119.project.1.silence.sql
@@ -0,0 +1,8 @@
+/* PhabricatorObjectHasUnsubscriberEdgeType::EDGECONST = 23 */
+/* PhabricatorProjectSilencedEdgeType::EDGECONST = 61 */
+
+/* This is converting existing unsubscribes into disabled mail. */
+
+INSERT IGNORE INTO {$NAMESPACE}_project.edge (src, type, dst, dateCreated)
+ SELECT src, 61, dst, dateCreated FROM {$NAMESPACE}_project.edge
+ WHERE type = 23;
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
@@ -2925,6 +2925,8 @@
'PhabricatorProjectSchemaSpec' => 'applications/project/storage/PhabricatorProjectSchemaSpec.php',
'PhabricatorProjectSearchEngine' => 'applications/project/query/PhabricatorProjectSearchEngine.php',
'PhabricatorProjectSearchField' => 'applications/project/searchfield/PhabricatorProjectSearchField.php',
+ 'PhabricatorProjectSilenceController' => 'applications/project/controller/PhabricatorProjectSilenceController.php',
+ 'PhabricatorProjectSilencedEdgeType' => 'applications/project/edge/PhabricatorProjectSilencedEdgeType.php',
'PhabricatorProjectSlug' => 'applications/project/storage/PhabricatorProjectSlug.php',
'PhabricatorProjectStandardCustomField' => 'applications/project/customfield/PhabricatorProjectStandardCustomField.php',
'PhabricatorProjectStatus' => 'applications/project/constants/PhabricatorProjectStatus.php',
@@ -7237,7 +7239,6 @@
'PhabricatorFlaggableInterface',
'PhabricatorPolicyInterface',
'PhabricatorExtendedPolicyInterface',
- 'PhabricatorSubscribableInterface',
'PhabricatorCustomFieldInterface',
'PhabricatorDestructibleInterface',
'PhabricatorFulltextInterface',
@@ -7329,6 +7330,8 @@
'PhabricatorProjectSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorProjectSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorProjectSearchField' => 'PhabricatorSearchTokenizerField',
+ 'PhabricatorProjectSilenceController' => 'PhabricatorProjectController',
+ 'PhabricatorProjectSilencedEdgeType' => 'PhabricatorEdgeType',
'PhabricatorProjectSlug' => 'PhabricatorProjectDAO',
'PhabricatorProjectStandardCustomField' => array(
'PhabricatorProjectCustomField',
diff --git a/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php b/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php
--- a/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php
+++ b/src/applications/metamta/query/PhabricatorMetaMTAMemberQuery.php
@@ -42,20 +42,48 @@
$projects = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withPHIDs($phids)
+ ->needMembers(true)
+ ->needWatchers(true)
->execute();
- $subscribers = id(new PhabricatorSubscribersQuery())
- ->withObjectPHIDs($phids)
- ->execute();
+ $edge_type = PhabricatorProjectSilencedEdgeType::EDGECONST;
+
+ $edge_query = id(new PhabricatorEdgeQuery())
+ ->withSourcePHIDs($phids)
+ ->withEdgeTypes(
+ array(
+ $edge_type,
+ ));
+
+ $edge_query->execute();
$projects = mpull($projects, null, 'getPHID');
foreach ($phids as $phid) {
$project = idx($projects, $phid);
+
if (!$project) {
$results[$phid] = array();
- } else {
- $results[$phid] = idx($subscribers, $phid, array());
+ continue;
}
+
+ // Recipients are members who haven't silenced the project, plus
+ // watchers.
+
+ $members = $project->getMemberPHIDs();
+ $members = array_fuse($members);
+
+ $watchers = $project->getWatcherPHIDs();
+ $watchers = array_fuse($watchers);
+
+ $silenced = $edge_query->getDestinationPHIDs(
+ array($phid),
+ array($edge_type));
+ $silenced = array_fuse($silenced);
+
+ $result_map = array_diff_key($members, $silenced);
+ $result_map = $result_map + $watchers;
+
+ $results[$phid] = array_values($result_map);
}
break;
default:
diff --git a/src/applications/project/application/PhabricatorProjectApplication.php b/src/applications/project/application/PhabricatorProjectApplication.php
--- a/src/applications/project/application/PhabricatorProjectApplication.php
+++ b/src/applications/project/application/PhabricatorProjectApplication.php
@@ -89,6 +89,8 @@
'history/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectHistoryController',
'(?P<action>watch|unwatch)/(?P<id>[1-9]\d*)/'
=> 'PhabricatorProjectWatchController',
+ 'silence/(?P<id>[1-9]\d*)/'
+ => 'PhabricatorProjectSilenceController',
),
'/tag/' => array(
'(?P<slug>[^/]+)/' => 'PhabricatorProjectViewController',
diff --git a/src/applications/project/controller/PhabricatorProjectMembersViewController.php b/src/applications/project/controller/PhabricatorProjectMembersViewController.php
--- a/src/applications/project/controller/PhabricatorProjectMembersViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectMembersViewController.php
@@ -110,6 +110,49 @@
$descriptions[PhabricatorPolicyCapability::CAN_JOIN]);
}
+ $viewer_phid = $viewer->getPHID();
+
+ if ($project->isUserWatcher($viewer_phid)) {
+ $watch_item = id(new PHUIStatusItemView())
+ ->setIcon('fa-eye green')
+ ->setTarget(phutil_tag('strong', array(), pht('Watching')))
+ ->setNote(
+ pht(
+ 'You will receive mail about changes made to any related '.
+ 'object.'));
+
+ $watch_status = id(new PHUIStatusListView())
+ ->addItem($watch_item);
+
+ $view->addProperty(pht('Watching'), $watch_status);
+ }
+
+ if ($project->isUserMember($viewer_phid)) {
+ $is_silenced = $this->isProjectSilenced($project);
+ if ($is_silenced) {
+ $mail_icon = 'fa-envelope-o grey';
+ $mail_target = pht('Disabled');
+ $mail_note = pht(
+ 'When mail is sent to project members, you will not receive '.
+ 'a copy.');
+ } else {
+ $mail_icon = 'fa-envelope-o green';
+ $mail_target = pht('Enabled');
+ $mail_note = pht(
+ 'You will receive mail that is sent to project members.');
+ }
+
+ $mail_item = id(new PHUIStatusItemView())
+ ->setIcon($mail_icon)
+ ->setTarget(phutil_tag('strong', array(), $mail_target))
+ ->setNote($mail_note);
+
+ $mail_status = id(new PHUIStatusListView())
+ ->addItem($mail_item);
+
+ $view->addProperty(pht('Mail to Members'), $mail_status);
+ }
+
return $view;
}
@@ -136,7 +179,9 @@
$can_leave = $supports_edit && (!$is_locked || $can_edit);
- if (!$project->isUserMember($viewer->getPHID())) {
+ $viewer_phid = $viewer->getPHID();
+
+ if (!$project->isUserMember($viewer_phid)) {
$view->addAction(
id(new PhabricatorActionView())
->setHref('/project/update/'.$project->getID().'/join/')
@@ -170,6 +215,23 @@
->setName(pht('Unwatch Project')));
}
+ $can_silence = $project->isUserMember($viewer_phid);
+ $is_silenced = $this->isProjectSilenced($project);
+
+ if ($is_silenced) {
+ $silence_text = pht('Enable Mail');
+ } else {
+ $silence_text = pht('Disable Mail');
+ }
+
+ $view->addAction(
+ id(new PhabricatorActionView())
+ ->setName($silence_text)
+ ->setIcon('fa-envelope-o')
+ ->setHref("/project/silence/{$id}/")
+ ->setWorkflow(true)
+ ->setDisabled(!$can_silence));
+
$can_add = $can_edit && $supports_edit;
$view->addAction(
@@ -202,4 +264,20 @@
return $view;
}
+ private function isProjectSilenced(PhabricatorProject $project) {
+ $viewer = $this->getViewer();
+
+ $viewer_phid = $viewer->getPHID();
+ if (!$viewer_phid) {
+ return false;
+ }
+
+ $edge_type = PhabricatorProjectSilencedEdgeType::EDGECONST;
+ $silenced = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $project->getPHID(),
+ $edge_type);
+ $silenced = array_fuse($silenced);
+ return isset($silenced[$viewer_phid]);
+ }
+
}
diff --git a/src/applications/project/controller/PhabricatorProjectSilenceController.php b/src/applications/project/controller/PhabricatorProjectSilenceController.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/controller/PhabricatorProjectSilenceController.php
@@ -0,0 +1,87 @@
+<?php
+
+final class PhabricatorProjectSilenceController
+ extends PhabricatorProjectController {
+
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $request->getViewer();
+ $id = $request->getURIData('id');
+ $action = $request->getURIData('action');
+
+ $project = id(new PhabricatorProjectQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($id))
+ ->needMembers(true)
+ ->executeOne();
+ if (!$project) {
+ return new Aphront404Response();
+ }
+
+ $edge_type = PhabricatorProjectSilencedEdgeType::EDGECONST;
+ $done_uri = "/project/members/{$id}/";
+ $viewer_phid = $viewer->getPHID();
+
+ if (!$project->isUserMember($viewer_phid)) {
+ return $this->newDialog()
+ ->setTitle(pht('Not a Member'))
+ ->appendParagraph(
+ pht(
+ 'You are not a project member, so you do not receive mail sent '.
+ 'to members of this project.'))
+ ->addCancelButton($done_uri);
+ }
+
+ $silenced = PhabricatorEdgeQuery::loadDestinationPHIDs(
+ $project->getPHID(),
+ $edge_type);
+ $silenced = array_fuse($silenced);
+ $is_silenced = isset($silenced[$viewer_phid]);
+
+ if ($request->isDialogFormPost()) {
+ if ($is_silenced) {
+ $edge_action = '-';
+ } else {
+ $edge_action = '+';
+ }
+
+ $xactions = array();
+ $xactions[] = id(new PhabricatorProjectTransaction())
+ ->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
+ ->setMetadataValue('edge:type', $edge_type)
+ ->setNewValue(
+ array(
+ $edge_action => array($viewer_phid => $viewer_phid),
+ ));
+
+ $editor = id(new PhabricatorProjectTransactionEditor($project))
+ ->setActor($viewer)
+ ->setContentSourceFromRequest($request)
+ ->setContinueOnNoEffect(true)
+ ->setContinueOnMissingFields(true)
+ ->applyTransactions($project, $xactions);
+
+ return id(new AphrontRedirectResponse())->setURI($done_uri);
+ }
+
+ if ($is_silenced) {
+ $title = pht('Enable Mail');
+ $body = pht(
+ 'When mail is sent to members of this project, you will receive a '.
+ 'copy.');
+ $button = pht('Enable Project Mail');
+ } else {
+ $title = pht('Disable Mail');
+ $body = pht(
+ 'When mail is sent to members of this project, you will no longer '.
+ 'receive a copy.');
+ $button = pht('Disable Project Mail');
+ }
+
+ return $this->newDialog()
+ ->setTitle($title)
+ ->appendParagraph($body)
+ ->addCancelButton($done_uri)
+ ->addSubmitButton($button);
+ }
+
+}
diff --git a/src/applications/project/controller/PhabricatorProjectWatchController.php b/src/applications/project/controller/PhabricatorProjectWatchController.php
--- a/src/applications/project/controller/PhabricatorProjectWatchController.php
+++ b/src/applications/project/controller/PhabricatorProjectWatchController.php
@@ -30,11 +30,9 @@
switch ($action) {
case 'watch':
$edge_action = '+';
- $force_subscribe = true;
break;
case 'unwatch':
$edge_action = '-';
- $force_subscribe = false;
break;
}
diff --git a/src/applications/project/edge/PhabricatorProjectSilencedEdgeType.php b/src/applications/project/edge/PhabricatorProjectSilencedEdgeType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/project/edge/PhabricatorProjectSilencedEdgeType.php
@@ -0,0 +1,8 @@
+<?php
+
+final class PhabricatorProjectSilencedEdgeType
+ extends PhabricatorEdgeType {
+
+ const EDGECONST = 61;
+
+}
diff --git a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php
--- a/src/applications/project/editor/PhabricatorProjectTransactionEditor.php
+++ b/src/applications/project/editor/PhabricatorProjectTransactionEditor.php
@@ -178,51 +178,6 @@
return parent::applyCustomExternalTransaction($object, $xaction);
}
- protected function applyBuiltinExternalTransaction(
- PhabricatorLiskDAO $object,
- PhabricatorApplicationTransaction $xaction) {
-
- switch ($xaction->getTransactionType()) {
- case PhabricatorTransactions::TYPE_EDGE:
- $edge_type = $xaction->getMetadataValue('edge:type');
- switch ($edge_type) {
- case PhabricatorProjectProjectHasMemberEdgeType::EDGECONST:
- case PhabricatorObjectHasWatcherEdgeType::EDGECONST:
- $edge_const = PhabricatorProjectProjectHasMemberEdgeType::EDGECONST;
- if ($edge_type != $edge_const) {
- break;
- }
-
- $old = $xaction->getOldValue();
- $new = $xaction->getNewValue();
-
- // When adding members, we add subscriptions. When removing
- // members, we remove subscriptions.
- $add = array_keys(array_diff_key($new, $old));
- $rem = array_keys(array_diff_key($old, $new));
-
- // NOTE: The subscribe is "explicit" because there's no implicit
- // unsubscribe, so Join -> Leave -> Join doesn't resubscribe you
- // if we use an implicit subscribe, even though you never willfully
- // unsubscribed. Not sure if adding implicit unsubscribe (which
- // would not write the unsubscribe row) is justified to deal with
- // this, which is a fairly weird edge case and pretty arguable both
- // ways.
-
- id(new PhabricatorSubscriptionsEditor())
- ->setActor($this->requireActor())
- ->setObject($object)
- ->subscribeExplicit($add)
- ->unsubscribe($rem)
- ->save();
- break;
- }
- break;
- }
-
- return parent::applyBuiltinExternalTransaction($object, $xaction);
- }
-
protected function validateAllTransactions(
PhabricatorLiskDAO $object,
array $xactions) {
@@ -584,6 +539,10 @@
);
}
+ protected function getMailCc(PhabricatorLiskDAO $object) {
+ return array();
+ }
+
public function getMailTagsMap() {
return array(
PhabricatorProjectTransaction::MAILTAG_METADATA =>
@@ -592,8 +551,6 @@
pht('Project membership changes.'),
PhabricatorProjectTransaction::MAILTAG_WATCHERS =>
pht('Project watcher list changes.'),
- PhabricatorProjectTransaction::MAILTAG_SUBSCRIBERS =>
- pht('Project subscribers change.'),
PhabricatorProjectTransaction::MAILTAG_OTHER =>
pht('Other project activity not listed above occurs.'),
);
diff --git a/src/applications/project/engine/PhabricatorProjectEditEngine.php b/src/applications/project/engine/PhabricatorProjectEditEngine.php
--- a/src/applications/project/engine/PhabricatorProjectEditEngine.php
+++ b/src/applications/project/engine/PhabricatorProjectEditEngine.php
@@ -148,7 +148,6 @@
'icon',
'color',
'slugs',
- 'subscriberPHIDs',
));
return array(
diff --git a/src/applications/project/storage/PhabricatorProject.php b/src/applications/project/storage/PhabricatorProject.php
--- a/src/applications/project/storage/PhabricatorProject.php
+++ b/src/applications/project/storage/PhabricatorProject.php
@@ -6,7 +6,6 @@
PhabricatorFlaggableInterface,
PhabricatorPolicyInterface,
PhabricatorExtendedPolicyInterface,
- PhabricatorSubscribableInterface,
PhabricatorCustomFieldInterface,
PhabricatorDestructibleInterface,
PhabricatorFulltextInterface,
@@ -179,7 +178,6 @@
return $extended;
}
-
public function isUserMember($user_phid) {
if ($this->memberPHIDs !== self::ATTACHABLE) {
return in_array($user_phid, $this->memberPHIDs);
@@ -536,19 +534,6 @@
}
-
-/* -( PhabricatorSubscribableInterface )----------------------------------- */
-
-
- public function isAutomaticallySubscribed($phid) {
- return false;
- }
-
- public function shouldShowSubscribersProperty() {
- return false;
- }
-
-
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
diff --git a/src/applications/project/storage/PhabricatorProjectTransaction.php b/src/applications/project/storage/PhabricatorProjectTransaction.php
--- a/src/applications/project/storage/PhabricatorProjectTransaction.php
+++ b/src/applications/project/storage/PhabricatorProjectTransaction.php
@@ -18,7 +18,6 @@
const MAILTAG_METADATA = 'project-metadata';
const MAILTAG_MEMBERS = 'project-members';
- const MAILTAG_SUBSCRIBERS = 'project-subscribers';
const MAILTAG_WATCHERS = 'project-watchers';
const MAILTAG_OTHER = 'project-other';
@@ -382,9 +381,6 @@
case self::TYPE_COLOR:
$tags[] = self::MAILTAG_METADATA;
break;
- case PhabricatorTransactions::TYPE_SUBSCRIBERS:
- $tags[] = self::MAILTAG_SUBSCRIBERS;
- break;
case PhabricatorTransactions::TYPE_EDGE:
$type = $this->getMetadata('edge:type');
$type = head($type);

File Metadata

Mime Type
text/plain
Expires
Tue, Oct 22, 10:09 AM (3 w, 6 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6739594
Default Alt Text
D15065.diff (17 KB)

Event Timeline