diff --git a/resources/sql/patches/054.subscribers.sql b/resources/sql/patches/054.subscribers.sql
new file mode 100644
index 0000000000..263aa1b692
--- /dev/null
+++ b/resources/sql/patches/054.subscribers.sql
@@ -0,0 +1,6 @@
+CREATE TABLE phabricator_maniphest.maniphest_tasksubscriber (
+ taskPHID varchar(64) BINARY NOT NULL,
+ subscriberPHID varchar(64) BINARY NOT NULL,
+ PRIMARY KEY (subscriberPHID, taskPHID),
+ UNIQUE KEY (taskPHID, subscriberPHID)
+);
diff --git a/scripts/search/reindex_maniphest.php b/scripts/search/reindex_maniphest.php
index 62550d56d2..45a99b4788 100755
--- a/scripts/search/reindex_maniphest.php
+++ b/scripts/search/reindex_maniphest.php
@@ -26,6 +26,7 @@ $tasks = id(new ManiphestTask())->loadAll();
echo "Updating relationships for ".count($tasks)." tasks";
foreach ($tasks as $task) {
ManiphestTaskProject::updateTaskProjects($task);
+ ManiphestTaskSubscriber::updateTaskSubscribers($task);
echo '.';
}
echo "\nDone.\n";
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
index 4ce8f7c0ef..cddac1af23 100644
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -277,6 +277,7 @@ phutil_register_library_map(array(
'ManiphestTaskProject' => 'applications/maniphest/storage/taskproject',
'ManiphestTaskQuery' => 'applications/maniphest/query',
'ManiphestTaskStatus' => 'applications/maniphest/constants/status',
+ 'ManiphestTaskSubscriber' => 'applications/maniphest/storage/subscriber',
'ManiphestTaskSummaryView' => 'applications/maniphest/view/tasksummary',
'ManiphestTransaction' => 'applications/maniphest/storage/transaction',
'ManiphestTransactionDetailView' => 'applications/maniphest/view/transactiondetail',
@@ -788,6 +789,7 @@ phutil_register_library_map(array(
'ManiphestTaskPriority' => 'ManiphestConstants',
'ManiphestTaskProject' => 'ManiphestDAO',
'ManiphestTaskStatus' => 'ManiphestConstants',
+ 'ManiphestTaskSubscriber' => 'ManiphestDAO',
'ManiphestTaskSummaryView' => 'ManiphestView',
'ManiphestTransaction' => 'ManiphestDAO',
'ManiphestTransactionDetailView' => 'ManiphestView',
diff --git a/src/applications/maniphest/controller/tasklist/ManiphestTaskListController.php b/src/applications/maniphest/controller/tasklist/ManiphestTaskListController.php
index b57e8c55e2..7303c65dd2 100644
--- a/src/applications/maniphest/controller/tasklist/ManiphestTaskListController.php
+++ b/src/applications/maniphest/controller/tasklist/ManiphestTaskListController.php
@@ -54,9 +54,10 @@ class ManiphestTaskListController extends ManiphestController {
$views = array(
'User Tasks',
- 'action' => 'Assigned',
- 'created' => 'Created',
- 'triage' => 'Need Triage',
+ 'action' => 'Assigned',
+ 'created' => 'Created',
+ 'subscribed' => 'Subscribed',
+ 'triage' => 'Need Triage',
'
',
'All Tasks',
'alltriage' => 'Need Triage',
@@ -70,6 +71,7 @@ class ManiphestTaskListController extends ManiphestController {
$has_filter = array(
'action' => true,
'created' => true,
+ 'subscribed' => true,
'triage' => true,
);
@@ -270,6 +272,9 @@ class ManiphestTaskListController extends ManiphestController {
case 'created':
$query->withAuthors($user_phids);
break;
+ case 'subscribed':
+ $query->withSubscribers($user_phids);
+ break;
case 'triage':
$query->withOwners($user_phids);
$query->withPriority(ManiphestTaskPriority::PRIORITY_TRIAGE);
diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php
index 1db3f02539..08f543dcc3 100644
--- a/src/applications/maniphest/query/ManiphestTaskQuery.php
+++ b/src/applications/maniphest/query/ManiphestTaskQuery.php
@@ -28,6 +28,7 @@ final class ManiphestTaskQuery {
private $ownerPHIDs = array();
private $includeUnowned = null;
private $projectPHIDs = array();
+ private $subscriberPHIDs = array();
private $status = 'status-any';
const STATUS_ANY = 'status-any';
@@ -89,6 +90,11 @@ final class ManiphestTaskQuery {
return $this;
}
+ public function withSubscribers(array $subscribers) {
+ $this->subscriberPHIDs = $subscribers;
+ return $this;
+ }
+
public function setGroupBy($group) {
$this->groupBy = $group;
return $this;
@@ -139,6 +145,7 @@ final class ManiphestTaskQuery {
$where[] = $this->buildPriorityWhereClause($conn);
$where[] = $this->buildAuthorWhereClause($conn);
$where[] = $this->buildOwnerWhereClause($conn);
+ $where[] = $this->buildSubscriberWhereClause($conn);
$where[] = $this->buildProjectWhereClause($conn);
$where = array_filter($where);
@@ -150,6 +157,7 @@ final class ManiphestTaskQuery {
$join = array();
$join[] = $this->buildProjectJoinClause($conn);
+ $join[] = $this->buildSubscriberJoinClause($conn);
$join = array_filter($join);
if ($join) {
@@ -272,6 +280,17 @@ final class ManiphestTaskQuery {
}
}
+ private function buildSubscriberWhereClause($conn) {
+ if (!$this->subscriberPHIDs) {
+ return null;
+ }
+
+ return qsprintf(
+ $conn,
+ 'subscriber.subscriberPHID IN (%Ls)',
+ $this->subscriberPHIDs);
+ }
+
private function buildProjectWhereClause($conn) {
if (!$this->projectPHIDs) {
return null;
@@ -295,6 +314,18 @@ final class ManiphestTaskQuery {
$project_dao->getTableName());
}
+ private function buildSubscriberJoinClause($conn) {
+ if (!$this->subscriberPHIDs) {
+ return null;
+ }
+
+ $subscriber_dao = new ManiphestTaskSubscriber();
+ return qsprintf(
+ $conn,
+ 'JOIN %T subscriber ON subscriber.taskPHID = task.phid',
+ $subscriber_dao->getTableName());
+ }
+
private function buildOrderClause($conn) {
$order = array();
diff --git a/src/applications/maniphest/query/__init__.php b/src/applications/maniphest/query/__init__.php
index 19b4f9fc8c..86a637a392 100644
--- a/src/applications/maniphest/query/__init__.php
+++ b/src/applications/maniphest/query/__init__.php
@@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'applications/maniphest/constants/owner');
+phutil_require_module('phabricator', 'applications/maniphest/storage/subscriber');
phutil_require_module('phabricator', 'applications/maniphest/storage/task');
phutil_require_module('phabricator', 'applications/maniphest/storage/taskproject');
phutil_require_module('phabricator', 'storage/qsprintf');
diff --git a/src/applications/maniphest/storage/subscriber/ManiphestTaskSubscriber.php b/src/applications/maniphest/storage/subscriber/ManiphestTaskSubscriber.php
new file mode 100644
index 0000000000..a5b9222910
--- /dev/null
+++ b/src/applications/maniphest/storage/subscriber/ManiphestTaskSubscriber.php
@@ -0,0 +1,65 @@
+ self::IDS_MANUAL,
+ self::CONFIG_TIMESTAMPS => false,
+ );
+ }
+
+ public static function updateTaskSubscribers(ManiphestTask $task) {
+ $dao = new ManiphestTaskSubscriber();
+ $conn = $dao->establishConnection('w');
+
+ $sql = array();
+ $subscribers = $task->getCCPHIDs();
+ $subscribers[] = $task->getOwnerPHID();
+ $subscribers = array_unique($subscribers);
+
+ foreach ($subscribers as $subscriber_phid) {
+ $sql[] = qsprintf(
+ $conn,
+ '(%s, %s)',
+ $task->getPHID(),
+ $subscriber_phid);
+ }
+
+ queryfx(
+ $conn,
+ 'DELETE FROM %T WHERE taskPHID = %s',
+ $dao->getTableName(),
+ $task->getPHID());
+ if ($sql) {
+ queryfx(
+ $conn,
+ 'INSERT INTO %T (taskPHID, subscriberPHID) VALUES %Q',
+ $dao->getTableName(),
+ implode(', ', $sql));
+ }
+ }
+
+}
diff --git a/src/applications/maniphest/storage/subscriber/__init__.php b/src/applications/maniphest/storage/subscriber/__init__.php
new file mode 100644
index 0000000000..6fe3a8de86
--- /dev/null
+++ b/src/applications/maniphest/storage/subscriber/__init__.php
@@ -0,0 +1,14 @@
+ccPHIDs = $phids;
+ $this->subscribersNeedUpdate = true;
+ return $this;
+ }
+
+ public function setOwnerPHID($phid) {
+ $this->ownerPHID = $phid;
+ $this->subscribersNeedUpdate = true;
+ return $this;
+ }
+
public function save() {
if (!$this->mailKey) {
$this->mailKey = sha1(Filesystem::readRandomBytes(20));
@@ -84,6 +97,13 @@ class ManiphestTask extends ManiphestDAO {
$this->projectsNeedUpdate = false;
}
+ if ($this->subscribersNeedUpdate) {
+ // If we've changed the subscriber PHIDs for this task, update the link
+ // table.
+ ManiphestTaskSubscriber::updateTaskSubscribers($this);
+ $this->subscribersNeedUpdate = false;
+ }
+
return $result;
}
diff --git a/src/applications/maniphest/storage/task/__init__.php b/src/applications/maniphest/storage/task/__init__.php
index 14ba412113..42038f4e2d 100644
--- a/src/applications/maniphest/storage/task/__init__.php
+++ b/src/applications/maniphest/storage/task/__init__.php
@@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'applications/maniphest/storage/base');
+phutil_require_module('phabricator', 'applications/maniphest/storage/subscriber');
phutil_require_module('phabricator', 'applications/maniphest/storage/taskproject');
phutil_require_module('phabricator', 'applications/phid/constants');
phutil_require_module('phabricator', 'applications/phid/storage/phid');