From 5b74b8b765ad04b5b3611c54d4b75efb5e6b5a9b Mon Sep 17 00:00:00 2001 From: epriestley Date: Fri, 5 Oct 2012 13:18:05 -0700 Subject: [PATCH] Add basic "Subscriptions" application Summary: Basic infrastructure for generalizing subscriptions/CCs for T1808, T1514 and T1663. - Implement `PhabricatorSubscribableInterface` and you'll get a subscribe/unsubscribe button for free. - If there are any auto-subscribed users (like the question author) you can specify them; this makes more sense for Tasks and Revisions than Ponder probably, but maybe the author should be auto-subscribed. - Subscriptions are either "explicit" (the user clicked 'subscribe') or "implicit" (the user did something which causes them to become subscribed naturally). If a user unsubscribes, they'll no longer be added by implicit subscriptions. This may or may not be relevant to Ponder but is an existing Herald feature in Differential. - Helper method on PhabricatorSubscribersQuery to load subscribers. - This doesn't handle actually sending email, etc. I think that's all so application-specific that it doesn't belong here. - Now seems to work. Test Plan: {F20552} {F20553} Reviewers: pieter, btrahan Reviewed By: pieter CC: aran Maniphest Tasks: T1663, T1514, T1808 Differential Revision: https://secure.phabricator.com/D3637 --- scripts/celerity/generate_sprites.php | 27 ++-- src/__phutil_library_map__.php | 11 ++ .../handle/PhabricatorObjectHandleData.php | 8 ++ .../PhabricatorApplicationSubscriptions.php | 41 ++++++ ...PhabricatorSubscriptionsEditController.php | 106 +++++++++++++++ .../editor/PhabricatorSubscriptionsEditor.php | 128 ++++++++++++++++++ ...habricatorSubscriptionsUIEventListener.php | 100 ++++++++++++++ .../PhabricatorSubscribableInterface.php | 32 +++++ .../query/PhabricatorSubscribersQuery.php | 66 +++++++++ .../edges/constants/PhabricatorEdgeConfig.php | 12 ++ webroot/rsrc/css/autosprite.css | 12 ++ webroot/rsrc/image/autosprite.png | Bin 115392 -> 115840 bytes 12 files changed, 531 insertions(+), 12 deletions(-) create mode 100644 src/applications/subscriptions/application/PhabricatorApplicationSubscriptions.php create mode 100644 src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php create mode 100644 src/applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php create mode 100644 src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php create mode 100644 src/applications/subscriptions/interface/PhabricatorSubscribableInterface.php create mode 100644 src/applications/subscriptions/query/PhabricatorSubscribersQuery.php diff --git a/scripts/celerity/generate_sprites.php b/scripts/celerity/generate_sprites.php index 724c41a370..c5a780480b 100755 --- a/scripts/celerity/generate_sprites.php +++ b/scripts/celerity/generate_sprites.php @@ -169,18 +169,21 @@ $action_template = id(new PhutilSprite()) ->setSourceSize(16, 16); $action_map = array( - 'file' => 'icon/page_white_text.png', - 'fork' => 'icon/arrow_branch.png', - 'edit' => 'icon/page_white_edit.png', - 'flag-0' => 'icon/flag-0.png', - 'flag-1' => 'icon/flag-1.png', - 'flag-2' => 'icon/flag-2.png', - 'flag-3' => 'icon/flag-3.png', - 'flag-4' => 'icon/flag-4.png', - 'flag-5' => 'icon/flag-5.png', - 'flag-6' => 'icon/flag-6.png', - 'flag-7' => 'icon/flag-7.png', - 'flag-ghost' => 'icon/flag-ghost.png', + 'file' => 'icon/page_white_text.png', + 'fork' => 'icon/arrow_branch.png', + 'edit' => 'icon/page_white_edit.png', + 'flag-0' => 'icon/flag-0.png', + 'flag-1' => 'icon/flag-1.png', + 'flag-2' => 'icon/flag-2.png', + 'flag-3' => 'icon/flag-3.png', + 'flag-4' => 'icon/flag-4.png', + 'flag-5' => 'icon/flag-5.png', + 'flag-6' => 'icon/flag-6.png', + 'flag-7' => 'icon/flag-7.png', + 'flag-ghost' => 'icon/flag-ghost.png', + 'subscribe-auto' => 'icon/unsubscribe.png', + 'subscribe-add' => 'icon/subscribe.png', + 'subscribe-delete' => 'icon/unsubscribe.png', ); foreach ($action_map as $icon => $source) { diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index f2449925de..0de55c77f5 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -581,6 +581,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationSettings' => 'applications/settings/application/PhabricatorApplicationSettings.php', 'PhabricatorApplicationSlowvote' => 'applications/slowvote/application/PhabricatorApplicationSlowvote.php', 'PhabricatorApplicationStatusView' => 'applications/meta/view/PhabricatorApplicationStatusView.php', + 'PhabricatorApplicationSubscriptions' => 'applications/subscriptions/application/PhabricatorApplicationSubscriptions.php', 'PhabricatorApplicationUIExamples' => 'applications/uiexample/application/PhabricatorApplicationUIExamples.php', 'PhabricatorApplicationsListController' => 'applications/meta/controller/PhabricatorApplicationsListController.php', 'PhabricatorAuditActionConstants' => 'applications/audit/constants/PhabricatorAuditActionConstants.php', @@ -1078,6 +1079,11 @@ phutil_register_library_map(array( 'PhabricatorStorageManagementUpgradeWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementUpgradeWorkflow.php', 'PhabricatorStorageManagementWorkflow' => 'infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php', 'PhabricatorStoragePatch' => 'infrastructure/storage/management/PhabricatorStoragePatch.php', + 'PhabricatorSubscribableInterface' => 'applications/subscriptions/interface/PhabricatorSubscribableInterface.php', + 'PhabricatorSubscribersQuery' => 'applications/subscriptions/query/PhabricatorSubscribersQuery.php', + 'PhabricatorSubscriptionsEditController' => 'applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php', + 'PhabricatorSubscriptionsEditor' => 'applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php', + 'PhabricatorSubscriptionsUIEventListener' => 'applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php', 'PhabricatorSymbolNameLinter' => 'infrastructure/lint/hook/PhabricatorSymbolNameLinter.php', 'PhabricatorSyntaxHighlighter' => 'infrastructure/markup/PhabricatorSyntaxHighlighter.php', 'PhabricatorTaskmasterDaemon' => 'infrastructure/daemon/workers/PhabricatorTaskmasterDaemon.php', @@ -1691,6 +1697,7 @@ phutil_register_library_map(array( 'ManiphestTaskPriority' => 'ManiphestConstants', 'ManiphestTaskProject' => 'ManiphestDAO', 'ManiphestTaskProjectsView' => 'ManiphestView', + 'ManiphestTaskQuery' => 'PhabricatorQuery', 'ManiphestTaskStatus' => 'ManiphestConstants', 'ManiphestTaskSubscriber' => 'ManiphestDAO', 'ManiphestTaskSummaryView' => 'ManiphestView', @@ -1746,6 +1753,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationSettings' => 'PhabricatorApplication', 'PhabricatorApplicationSlowvote' => 'PhabricatorApplication', 'PhabricatorApplicationStatusView' => 'AphrontView', + 'PhabricatorApplicationSubscriptions' => 'PhabricatorApplication', 'PhabricatorApplicationUIExamples' => 'PhabricatorApplication', 'PhabricatorApplicationsListController' => 'PhabricatorController', 'PhabricatorAuditAddCommentController' => 'PhabricatorAuditController', @@ -2185,6 +2193,9 @@ phutil_register_library_map(array( 'PhabricatorStorageManagementStatusWorkflow' => 'PhabricatorStorageManagementWorkflow', 'PhabricatorStorageManagementUpgradeWorkflow' => 'PhabricatorStorageManagementWorkflow', 'PhabricatorStorageManagementWorkflow' => 'PhutilArgumentWorkflow', + 'PhabricatorSubscribersQuery' => 'PhabricatorQuery', + 'PhabricatorSubscriptionsEditController' => 'PhabricatorController', + 'PhabricatorSubscriptionsUIEventListener' => 'PhutilEventListener', 'PhabricatorSymbolNameLinter' => 'ArcanistXHPASTLintNamingHook', 'PhabricatorTaskmasterDaemon' => 'PhabricatorDaemon', 'PhabricatorTestCase' => 'ArcanistPhutilTestCase', diff --git a/src/applications/phid/handle/PhabricatorObjectHandleData.php b/src/applications/phid/handle/PhabricatorObjectHandleData.php index 8d8767df9a..1d1beb0b98 100644 --- a/src/applications/phid/handle/PhabricatorObjectHandleData.php +++ b/src/applications/phid/handle/PhabricatorObjectHandleData.php @@ -102,6 +102,14 @@ final class PhabricatorObjectHandleData { $objects[$revision->getPHID()] = $revision; } break; + case PhabricatorPHIDConstants::PHID_TYPE_QUES: + $questions = id(new PonderQuestionQuery()) + ->withPHIDs($phids) + ->execute(); + foreach ($questions as $question) { + $objects[$question->getPHID()] = $question; + } + break; } } diff --git a/src/applications/subscriptions/application/PhabricatorApplicationSubscriptions.php b/src/applications/subscriptions/application/PhabricatorApplicationSubscriptions.php new file mode 100644 index 0000000000..0e6da7e68b --- /dev/null +++ b/src/applications/subscriptions/application/PhabricatorApplicationSubscriptions.php @@ -0,0 +1,41 @@ + array( + '(?Padd|delete)/'. + '(?P[^/]+)/' => 'PhabricatorSubscriptionsEditController', + ), + ); + } + +} + diff --git a/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php new file mode 100644 index 0000000000..bcfe19a368 --- /dev/null +++ b/src/applications/subscriptions/controller/PhabricatorSubscriptionsEditController.php @@ -0,0 +1,106 @@ +phid = idx($data, 'phid'); + $this->action = idx($data, 'action'); + } + + public function processRequest() { + $request = $this->getRequest(); + + if (!$request->isFormPost()) { + return new Aphront400Response(); + } + + switch ($this->action) { + case 'add': + $is_add = true; + break; + case 'delete': + $is_add = false; + break; + default: + return new Aphront400Response(); + } + + $user = $request->getUser(); + $phid = $this->phid; + + // TODO: This is a policy test because `loadObjects()` is not currently + // policy-aware. Once it is, we can collapse this. + $handle = PhabricatorObjectHandleData::loadOneHandle($phid, $user); + if (!$handle->isComplete()) { + return new Aphront404Response(); + } + + $objects = id(new PhabricatorObjectHandleData(array($phid))) + ->loadObjects(); + $object = idx($objects, $phid); + + if (!($object instanceof PhabricatorSubscribableInterface)) { + return $this->buildErrorResponse( + pht('Bad Object'), + pht('This object is not subscribable.'), + $handle->getURI()); + } + + if ($object->isAutomaticallySubscribed($user->getPHID())) { + return $this->buildErrorResponse( + pht('Automatically Subscribed'), + pht('You are automatically subscribed to this object.'), + $handle->getURI()); + } + + $editor = id(new PhabricatorSubscriptionsEditor()) + ->setUser($user) + ->setObject($object); + + if ($is_add) { + $editor->subscribeExplicit(array($user->getPHID()), $explicit = true); + } else { + $editor->unsubscribe(array($user->getPHID())); + } + + $editor->save(); + + // TODO: We should just render the "Unsubscribe" action and swap it out + // in the document for Ajax requests. + return id(new AphrontReloadResponse())->setURI($handle->getURI()); + } + + private function buildErrorResponse($title, $message, $uri) { + $request = $this->getRequest(); + $user = $request->getUser(); + + $dialog = id(new AphrontDialogView()) + ->setUser($user) + ->setTitle($title) + ->appendChild($message) + ->addCancelButton($uri); + + return id(new AphrontDialogResponse())->setDialog($dialog); + } + +} diff --git a/src/applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php b/src/applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php new file mode 100644 index 0000000000..8b01034fbf --- /dev/null +++ b/src/applications/subscriptions/editor/PhabricatorSubscriptionsEditor.php @@ -0,0 +1,128 @@ +object = $object; + return $this; + } + + public function setUser(PhabricatorUser $user) { + $this->user = $user; + return $this; + } + + + /** + * Add explicit subscribers. These subscribers have explicitly subscribed + * (or been subscribed) to the object, and will be added even if they + * had previously unsubscribed. + * + * @param list List of PHIDs to explicitly subscribe. + * @return this + */ + public function subscribeExplicit(array $phids) { + $this->explicitSubscribePHIDs += array_fill_keys($phids, true); + return $this; + } + + + /** + * Add implicit subscribers. These subscribers have taken some action which + * implicitly subscribes them (e.g., adding a comment) but it will be + * suppressed if they've previously unsubscribed from the object. + * + * @param list List of PHIDs to implicitly subscribe. + * @return this + */ + public function subscribeImplicit(array $phids) { + $this->implicitSubscribePHIDs += array_fill_keys($phids, true); + return $this; + } + + + /** + * Unsubscribe PHIDs and mark them as unsubscribed, so implicit subscriptions + * will not resubscribe them. + * + * @param list List of PHIDs to unsubscribe. + * @return this + */ + public function unsubscribe(array $phids) { + $this->unsubscribePHIDs += array_fill_keys($phids, true); + return $this; + } + + + public function save() { + if (!$this->object) { + throw new Exception('Call setObject() before save()!'); + } + if (!$this->user) { + throw new Exception('Call setUser() before save()!'); + } + + $src = $this->object->getPHID(); + + if ($this->implicitSubscribePHIDs) { + $unsub = PhabricatorEdgeQuery::loadDestinationPHIDs( + $src, + PhabricatorEdgeConfig::TYPE_OBJECT_HAS_UNSUBSCRIBER); + $unsub = array_fill_keys($unsub, true); + $this->implicitSubscribePHIDs = array_diff_key( + $this->implicitSubscribePHIDs, + $unsub); + } + + $add = $this->implicitSubscribePHIDs + $this->explicitSubscribePHIDs; + $del = $this->unsubscribePHIDs; + + // If a PHID is marked for both subscription and unsubscription, treat + // unsubscription as the stronger action. + $add = array_diff_key($add, $del); + + if ($add || $del) { + $u_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_UNSUBSCRIBER; + $s_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_SUBSCRIBER; + + $editor = id(new PhabricatorEdgeEditor()) + ->setUser($this->user); + + foreach ($add as $phid => $ignored) { + $editor->removeEdge($src, $u_type, $phid); + $editor->addEdge($src, $s_type, $phid); + } + + foreach ($del as $phid => $ignored) { + $editor->removeEdge($src, $s_type, $phid); + $editor->addEdge($src, $u_type, $phid); + } + + $editor->save(); + } + } + +} diff --git a/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php new file mode 100644 index 0000000000..27c9dc8fb8 --- /dev/null +++ b/src/applications/subscriptions/events/PhabricatorSubscriptionsUIEventListener.php @@ -0,0 +1,100 @@ +listen(PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS); + } + + public function handleEvent(PhutilEvent $event) { + switch ($event->getType()) { + case PhabricatorEventType::TYPE_UI_DIDRENDERACTIONS: + $this->handleActionEvent($event); + break; + } + } + + private function handleActionEvent($event) { + $user = $event->getUser(); + $object = $event->getValue('object'); + + if (!$object || !$object->getPHID()) { + // No object, or the object has no PHID yet. No way to subscribe. + return; + } + + if (!($object instanceof PhabricatorSubscribableInterface)) { + // This object isn't subscribable. + return; + } + + if ($object->isAutomaticallySubscribed($user->getPHID())) { + $sub_action = id(new PhabricatorActionView()) + ->setWorkflow(true) + ->setUser($user) + ->setDisabled(true) + ->setRenderAsForm(true) + ->setHref('/subscriptions/add/'.$object->getPHID().'/') + ->setName(phutil_escape_html('Automatically Subscribed')) + ->setIcon('subscribe-auto'); + } else { + $subscribed = false; + if ($user->isLoggedIn()) { + $src_phid = $object->getPHID(); + $dst_phid = $user->getPHID(); + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_SUBSCRIBER; + + $edges = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(array($src_phid)) + ->withEdgeTypes(array($edge_type)) + ->withDestinationPHIDs(array($user->getPHID())) + ->execute(); + $subscribed = isset($edges[$src_phid][$edge_type][$dst_phid]); + } + + if ($subscribed) { + $sub_action = id(new PhabricatorActionView()) + ->setUser($user) + ->setWorkflow(true) + ->setRenderAsForm(true) + ->setHref('/subscriptions/delete/'.$object->getPHID().'/') + ->setName(phutil_escape_html('Unsubscribe')) + ->setIcon('subscribe-delete'); + } else { + $sub_action = id(new PhabricatorActionView()) + ->setUser($user) + ->setWorkflow(true) + ->setRenderAsForm(true) + ->setHref('/subscriptions/add/'.$object->getPHID().'/') + ->setName(phutil_escape_html('Subscribe')) + ->setIcon('subscribe-add'); + } + + if (!$user->isLoggedIn()) { + $sub_action->setDisabled(true); + } + } + + $actions = $event->getValue('actions'); + $actions[] = $sub_action; + $event->setValue('actions', $actions); + } + +} diff --git a/src/applications/subscriptions/interface/PhabricatorSubscribableInterface.php b/src/applications/subscriptions/interface/PhabricatorSubscribableInterface.php new file mode 100644 index 0000000000..173e3b8ca4 --- /dev/null +++ b/src/applications/subscriptions/interface/PhabricatorSubscribableInterface.php @@ -0,0 +1,32 @@ +withObjectPHIDs(array($phid)) + ->execute(); + return $subscribers[$phid]; + } + + public function withObjectPHIDs(array $object_phids) { + $this->objectPHIDs = $object_phids; + return $this; + } + + public function withSubscriberPHIDs(array $subscriber_phids) { + $this->subscriberPHIDs = $subscriber_phids; + return $this; + } + + public function execute() { + $query = new PhabricatorEdgeQuery(); + + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_SUBSCRIBER; + + $query->withSourcePHIDs($this->objectPHIDs); + $query->withEdgeTypes(array($edge_type)); + + if ($this->subscriberPHIDs) { + $query->withDestinationPHIDs($this->subscriberPHIDs); + } + + $edges = $query->execute(); + + $results = array_fill_keys($this->objectPHIDs, array()); + foreach ($edges as $src => $edge_types) { + foreach ($edge_types[$edge_type] as $dst => $data) { + $results[$src][] = $dst; + } + } + + return $results; + } + + +} diff --git a/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php b/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php index 74feeaef5f..0f4a6db72e 100644 --- a/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php +++ b/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php @@ -49,6 +49,12 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { const TYPE_ANSWER_HAS_VOTING_USER = 19; const TYPE_VOTING_USER_HAS_ANSWER = 20; + const TYPE_OBJECT_HAS_SUBSCRIBER = 21; + const TYPE_SUBSCRIBED_TO_OBJECT = 22; + + const TYPE_OBJECT_HAS_UNSUBSCRIBER = 23; + const TYPE_UNSUBSCRIBED_FROM_OBJECT = 24; + const TYPE_TEST_NO_CYCLE = 9000; public static function getInverse($edge_type) { @@ -82,6 +88,12 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { self::TYPE_QUESTION_HAS_VOTING_USER, self::TYPE_ANSWER_HAS_VOTING_USER => self::TYPE_VOTING_USER_HAS_ANSWER, self::TYPE_VOTING_USER_HAS_ANSWER => self::TYPE_ANSWER_HAS_VOTING_USER, + + self::TYPE_OBJECT_HAS_SUBSCRIBER => self::TYPE_SUBSCRIBED_TO_OBJECT, + self::TYPE_SUBSCRIBED_TO_OBJECT => self::TYPE_OBJECT_HAS_SUBSCRIBER, + + self::TYPE_OBJECT_HAS_UNSUBSCRIBER => self::TYPE_UNSUBSCRIBED_FROM_OBJECT, + self::TYPE_UNSUBSCRIBED_FROM_OBJECT => self::TYPE_OBJECT_HAS_UNSUBSCRIBER, ); return idx($map, $edge_type); diff --git a/webroot/rsrc/css/autosprite.css b/webroot/rsrc/css/autosprite.css index a640820ee1..9730129455 100644 --- a/webroot/rsrc/css/autosprite.css +++ b/webroot/rsrc/css/autosprite.css @@ -363,3 +363,15 @@ .action-flag-ghost { background-position: 0px -3444px; } + +.action-subscribe-auto { + background-position: 0px -3461px; +} + +.action-subscribe-add { + background-position: 0px -3478px; +} + +.action-subscribe-delete { + background-position: 0px -3495px; +} diff --git a/webroot/rsrc/image/autosprite.png b/webroot/rsrc/image/autosprite.png index 81042a8a9b2c20d5ffc0bd0a3d68f1c86c7957b6..dac2284d7d67090fd2331ee7f719214c25a42a36 100644 GIT binary patch delta 10732 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

3Z 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