mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-28 16:30:59 +01:00
Adds base notification application
Summary: First diff in a series of diffs to add notifications to Phabricator. This is the notification application ONLY. This commit does not include the changes to other applications that makes them add notifications. As such, no notifications will be generated beyond the initial database import. Test Plan: This is part of the notifications architecture which has been running on http://theoryphabricator.com for the past several months. Reviewers: epriestley, btrahan, ddfisher Reviewed By: epriestley CC: allenjohnashton, keebuhm, aran, Korvin, jungejason, nh Maniphest Tasks: T974 Differential Revision: https://secure.phabricator.com/D2571
This commit is contained in:
parent
692296a4d4
commit
3a6ee79190
16 changed files with 540 additions and 49 deletions
|
@ -179,6 +179,8 @@ return array(
|
|||
// extend AphrontMySQLDatabaseConnectionBase.
|
||||
'mysql.implementation' => 'AphrontMySQLDatabaseConnection',
|
||||
|
||||
// -- Notifications ----//
|
||||
'notification.enabled' => false,
|
||||
|
||||
// -- Email ----------------------------------------------------------------- //
|
||||
|
||||
|
|
8
resources/sql/patches/temp.notifications.sql
Normal file
8
resources/sql/patches/temp.notifications.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE if not exists phabricator_feed.feed_storynotification (
|
||||
userPHID varchar(64) not null collate utf8_bin,
|
||||
primaryObjectPHID varchar(64) not null collate utf8_bin,
|
||||
chronologicalKey BIGINT UNSIGNED NOT NULL,
|
||||
hasViewed boolean not null,
|
||||
UNIQUE KEY (userPHID, chronologicalKey),
|
||||
KEY (userPHID, hasViewed, primaryObjectPHID)
|
||||
);
|
|
@ -525,6 +525,9 @@ phutil_register_library_map(array(
|
|||
'ManiphestView' => 'applications/maniphest/view/ManiphestView.php',
|
||||
'MetaMTAConstants' => 'applications/metamta/constants/MetaMTAConstants.php',
|
||||
'MetaMTANotificationType' => 'applications/metamta/constants/MetaMTANotificationType.php',
|
||||
'NotificationMessage' => 'applications/notification/constants/message/NotificationMessage.php',
|
||||
'NotificationPathname' => 'applications/notification/constants/pathname/NotificationPathname.php',
|
||||
'NotificationType' => 'applications/notification/constants/type/NotificationType.php',
|
||||
'OwnersPackageReplyHandler' => 'applications/owners/OwnersPackageReplyHandler.php',
|
||||
'PackageCreateMail' => 'applications/owners/mail/PackageCreateMail.php',
|
||||
'PackageDeleteMail' => 'applications/owners/mail/PackageDeleteMail.php',
|
||||
|
@ -631,6 +634,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFeedStoryData' => 'applications/feed/storage/PhabricatorFeedStoryData.php',
|
||||
'PhabricatorFeedStoryDifferential' => 'applications/feed/story/PhabricatorFeedStoryDifferential.php',
|
||||
'PhabricatorFeedStoryManiphest' => 'applications/feed/story/PhabricatorFeedStoryManiphest.php',
|
||||
'PhabricatorFeedStoryNotification' => 'applications/notification/storage/PhabricatorFeedStoryNotification.php',
|
||||
'PhabricatorFeedStoryPhriction' => 'applications/feed/story/PhabricatorFeedStoryPhriction.php',
|
||||
'PhabricatorFeedStoryProject' => 'applications/feed/story/PhabricatorFeedStoryProject.php',
|
||||
'PhabricatorFeedStoryPublisher' => 'applications/feed/PhabricatorFeedStoryPublisher.php',
|
||||
|
@ -733,6 +737,16 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMetaMTAWorker' => 'applications/metamta/PhabricatorMetaMTAWorker.php',
|
||||
'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php',
|
||||
'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php',
|
||||
'PhabricatorNotificationBuilder' => 'applications/notification/builder/notification/PhabricatorNotificationBuilder.php',
|
||||
'PhabricatorNotificationConstants' => 'applications/notification/constants/base/PhabricatorNotificationConstants.php',
|
||||
'PhabricatorNotificationController' => 'applications/notification/controller/base/PhabricatorNotificationController.php',
|
||||
'PhabricatorNotificationPanelController' => 'applications/notification/controller/panel/PhabricatorNotificationPanelController.php',
|
||||
'PhabricatorNotificationQuery' => 'applications/notification/PhabricatorNotificationQuery.php',
|
||||
'PhabricatorNotificationStory' => 'applications/notification/base/PhabricatorNotificationStory.php',
|
||||
'PhabricatorNotificationStoryTypeConstants' => 'applications/notification/constants/story/PhabricatorNotificationStoryTypeConstants.php',
|
||||
'PhabricatorNotificationStoryView' => 'applications/notification/view/story/PhabricatorNotificationStoryView.php',
|
||||
'PhabricatorNotificationTestController' => 'applications/notification/controller/test/PhabricatorNotificationTestController.php',
|
||||
'PhabricatorNotificationView' => 'applications/notification/view/base/PhabricatorNotificationView.php',
|
||||
'PhabricatorNotificationsController' => 'applications/notifications/controller/PhabricatorNotificationsController.php',
|
||||
'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/PhabricatorOAuthClientAuthorization.php',
|
||||
'PhabricatorOAuthClientAuthorizationBaseController' => 'applications/oauthserver/controller/clientauthorization/PhabricatorOAuthClientAuthorizationBaseController.php',
|
||||
|
@ -1511,6 +1525,9 @@ phutil_register_library_map(array(
|
|||
'ManiphestTransactionType' => 'ManiphestConstants',
|
||||
'ManiphestView' => 'AphrontView',
|
||||
'MetaMTANotificationType' => 'MetaMTAConstants',
|
||||
'NotificationMessage' => 'PhabricatorNotificationConstants',
|
||||
'NotificationPathname' => 'PhabricatorNotificationConstants',
|
||||
'NotificationType' => 'PhabricatorNotificationConstants',
|
||||
'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'PackageCreateMail' => 'PackageMail',
|
||||
'PackageDeleteMail' => 'PackageMail',
|
||||
|
@ -1604,6 +1621,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFeedStoryData' => 'PhabricatorFeedDAO',
|
||||
'PhabricatorFeedStoryDifferential' => 'PhabricatorFeedStory',
|
||||
'PhabricatorFeedStoryManiphest' => 'PhabricatorFeedStory',
|
||||
'PhabricatorFeedStoryNotification' => 'PhabricatorFeedDAO',
|
||||
'PhabricatorFeedStoryPhriction' => 'PhabricatorFeedStory',
|
||||
'PhabricatorFeedStoryProject' => 'PhabricatorFeedStory',
|
||||
'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO',
|
||||
|
@ -1688,6 +1706,12 @@ phutil_register_library_map(array(
|
|||
'PhabricatorMetaMTAWorker' => 'PhabricatorWorker',
|
||||
'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController',
|
||||
'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine',
|
||||
'PhabricatorNotificationController' => 'PhabricatorController',
|
||||
'PhabricatorNotificationPanelController' => 'PhabricatorNotificationController',
|
||||
'PhabricatorNotificationStoryTypeConstants' => 'PhabricatorNotificationConstants',
|
||||
'PhabricatorNotificationStoryView' => 'PhabricatorNotificationView',
|
||||
'PhabricatorNotificationTestController' => 'PhabricatorNotificationController',
|
||||
'PhabricatorNotificationView' => 'AphrontView',
|
||||
'PhabricatorNotificationsController' => 'PhabricatorController',
|
||||
'PhabricatorOAuthClientAuthorization' => 'PhabricatorOAuthServerDAO',
|
||||
'PhabricatorOAuthClientAuthorizationBaseController' => 'PhabricatorOAuthServerController',
|
||||
|
|
|
@ -421,8 +421,7 @@ class AphrontDefaultApplicationConfiguration
|
|||
'PhabricatorChatLogChannelLogController',
|
||||
),
|
||||
|
||||
'/aphlict/' => 'PhabricatorAphlictTestPageController',
|
||||
|
||||
'/notification/test/' => 'PhabricatorNotificationTestController',
|
||||
'/flag/' => array(
|
||||
'' => 'PhabricatorFlagListController',
|
||||
'view/(?P<view>[^/]+)/' => 'PhabricatorFlagListController',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -23,12 +23,23 @@ final class PhabricatorFeedStoryPublisher {
|
|||
private $storyData;
|
||||
private $storyTime;
|
||||
private $storyAuthorPHID;
|
||||
private $primaryObjectPHID;
|
||||
private $subscribedPHIDs;
|
||||
|
||||
public function setRelatedPHIDs(array $phids) {
|
||||
$this->relatedPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setSubscribedPHIDs(array $phids) {
|
||||
$this->subscribedPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
public function setPrimaryObjectPHID($phid) {
|
||||
$this->primaryObjectPHID = $phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setStoryType($story_type) {
|
||||
$this->storyType = $story_type;
|
||||
return $this;
|
||||
|
@ -85,9 +96,43 @@ final class PhabricatorFeedStoryPublisher {
|
|||
$ref->getTableName(),
|
||||
implode(', ', $sql));
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
||||
$this->insertNotifications($chrono_key);
|
||||
}
|
||||
return $story;
|
||||
}
|
||||
|
||||
|
||||
private function insertNotifications($chrono_key) {
|
||||
if (!$this->primaryObjectPHID) {
|
||||
throw
|
||||
new Exception("Call setPrimaryObjectPHID() before Publishing!");
|
||||
}
|
||||
if ($this->subscribedPHIDs) {
|
||||
$notif = new PhabricatorFeedStoryNotification();
|
||||
$sql = array();
|
||||
$conn = $notif->establishConnection('w');
|
||||
|
||||
foreach (array_unique($this->subscribedPHIDs) as $user_phid) {
|
||||
$sql[] = qsprintf(
|
||||
$conn,
|
||||
'(%s, %s, %s, %d)',
|
||||
$this->primaryObjectPHID,
|
||||
$user_phid,
|
||||
$chrono_key,
|
||||
0);
|
||||
}
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
'INSERT INTO %T
|
||||
(primaryObjectPHID, userPHID, chronologicalKey, hasViewed)
|
||||
VALUES %Q',
|
||||
$notif->getTableName(),
|
||||
implode(', ', $sql));
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
abstract class PhabricatorFeedStory {
|
||||
|
||||
private $data;
|
||||
|
||||
private $hasViewed;
|
||||
private $handles;
|
||||
private $framed;
|
||||
|
||||
|
@ -33,6 +33,15 @@ abstract class PhabricatorFeedStory {
|
|||
return array();
|
||||
}
|
||||
|
||||
public function setHasViewed($has_viewed) {
|
||||
$this->hasViewed = $has_viewed;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getHasViewed() {
|
||||
return $this->hasViewed;
|
||||
}
|
||||
|
||||
public function getRequiredObjectPHIDs() {
|
||||
return array();
|
||||
}
|
||||
|
|
|
@ -16,72 +16,48 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorFeedStoryManiphest extends PhabricatorFeedStory {
|
||||
final class PhabricatorFeedStoryManiphest
|
||||
extends PhabricatorFeedStory {
|
||||
|
||||
public function getRequiredHandlePHIDs() {
|
||||
$data = $this->getStoryData();
|
||||
return array_filter(
|
||||
array(
|
||||
array(
|
||||
$this->getStoryData()->getAuthorPHID(),
|
||||
$data->getValue('taskPHID'),
|
||||
$data->getValue('ownerPHID'),
|
||||
));
|
||||
));
|
||||
}
|
||||
|
||||
public function getRequiredObjectPHIDs() {
|
||||
return array(
|
||||
$this->getStoryData()->getAuthorPHID(),
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
public function renderView() {
|
||||
$data = $this->getStoryData();
|
||||
|
||||
$author_phid = $data->getAuthorPHID();
|
||||
$owner_phid = $data->getValue('ownerPHID');
|
||||
$task_phid = $data->getValue('taskPHID');
|
||||
|
||||
$action = $data->getValue('action');
|
||||
|
||||
$view = new PhabricatorFeedStoryView();
|
||||
|
||||
$verb = ManiphestAction::getActionPastTenseVerb($action);
|
||||
$extra = null;
|
||||
switch ($action) {
|
||||
case ManiphestAction::ACTION_ASSIGN:
|
||||
if ($owner_phid) {
|
||||
$extra =
|
||||
' to '.
|
||||
$this->linkTo($owner_phid);
|
||||
} else {
|
||||
$verb = 'placed';
|
||||
$extra = ' up for grabs';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
$title =
|
||||
$this->linkTo($author_phid).
|
||||
" {$verb} task ".
|
||||
$this->linkTo($task_phid);
|
||||
$title .= $extra;
|
||||
$title .= '.';
|
||||
|
||||
$view->setTitle($title);
|
||||
|
||||
switch ($action) {
|
||||
case ManiphestAction::ACTION_CREATE:
|
||||
$full_size = true;
|
||||
break;
|
||||
default:
|
||||
$full_size = false;
|
||||
break;
|
||||
}
|
||||
|
||||
$view->setEpoch($data->getEpoch());
|
||||
|
||||
$action = $this->getLineForData($data);
|
||||
$view->setTitle($action);
|
||||
$view->setEpoch($data->getEpoch());
|
||||
|
||||
|
||||
switch ($action) {
|
||||
case ManiphestAction::ACTION_CREATE:
|
||||
$full_size = true;
|
||||
break;
|
||||
default:
|
||||
$full_size = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($full_size) {
|
||||
$view->setImage($this->getHandle($author_phid)->getImageURI());
|
||||
$view->setImage($this->getHandle($this->getAuthorPHID())->getImageURI());
|
||||
$content = $this->renderSummary($data->getValue('description'));
|
||||
$view->appendChild($content);
|
||||
} else {
|
||||
|
@ -91,4 +67,46 @@ final class PhabricatorFeedStoryManiphest extends PhabricatorFeedStory {
|
|||
return $view;
|
||||
}
|
||||
|
||||
|
||||
public function renderNotificationView() {
|
||||
$data = $this->getStoryData();
|
||||
|
||||
$view = new PhabricatorNotificationStoryView();
|
||||
|
||||
$view->setEpoch($data->getEpoch());
|
||||
$view->setTitle($this->getLineForData($data));
|
||||
$view->setEpoch($data->getEpoch());
|
||||
$view->setViewed($this->getHasViewed());
|
||||
|
||||
return $view;
|
||||
}
|
||||
|
||||
private function getLineForData($data) {
|
||||
$actor_phid = $data->getAuthorPHID();
|
||||
$owner_phid = $data->getValue('ownerPHID');
|
||||
$task_phid = $data->getValue('taskPHID');
|
||||
$action = $data->getValue('action');
|
||||
$description = $data->getValue('description');
|
||||
$comments = phutil_escape_html(
|
||||
phutil_utf8_shorten(
|
||||
$data->getValue('comments'),
|
||||
140));
|
||||
|
||||
$actor_link = $this->linkTo($actor_phid);
|
||||
$task_link = $this->linkTo($task_phid);
|
||||
$owner_link = $this->linkTo($owner_phid);
|
||||
$verb = ManiphestAction::getActionPastTenseVerb($action);
|
||||
|
||||
$one_line = "{$actor_link} {$verb} {$task_link}";
|
||||
|
||||
switch ($action) {
|
||||
case ManiphestAction::ACTION_ASSIGN:
|
||||
$one_line .= " to {$owner_link}";
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return $one_line;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -510,6 +510,9 @@ final class ManiphestTaskDetailController extends ManiphestController {
|
|||
$transaction_view->setAuxiliaryFields($aux_fields);
|
||||
$transaction_view->setMarkupEngine($engine);
|
||||
|
||||
PhabricatorFeedStoryNotification::updateObjectNotificationViews(
|
||||
$user, $task->getPHID());
|
||||
|
||||
return $this->buildStandardPageResponse(
|
||||
array(
|
||||
$context_bar,
|
||||
|
|
|
@ -314,7 +314,8 @@ final class ManiphestTransactionEditor {
|
|||
if ($transaction->hasComments()) {
|
||||
$comments = $transaction->getComments();
|
||||
}
|
||||
switch ($transaction->getTransactionType()) {
|
||||
$type = $transaction->getTransactionType();
|
||||
switch ($type) {
|
||||
case ManiphestTransactionType::TYPE_OWNER:
|
||||
$actions[] = ManiphestAction::ACTION_ASSIGN;
|
||||
break;
|
||||
|
@ -323,6 +324,8 @@ final class ManiphestTransactionEditor {
|
|||
$actions[] = ManiphestAction::ACTION_CLOSE;
|
||||
} else if ($this->isCreate($transactions)) {
|
||||
$actions[] = ManiphestAction::ACTION_CREATE;
|
||||
} else {
|
||||
$actions[] = ManiphestAction::ACTION_REOPEN;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
@ -335,6 +338,7 @@ final class ManiphestTransactionEditor {
|
|||
$actor_phid = head($transactions)->getAuthorPHID();
|
||||
$author_phid = $task->getAuthorPHID();
|
||||
|
||||
|
||||
id(new PhabricatorFeedStoryPublisher())
|
||||
->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_MANIPHEST)
|
||||
->setStoryData(array(
|
||||
|
@ -357,6 +361,17 @@ final class ManiphestTransactionEditor {
|
|||
$owner_phid,
|
||||
)),
|
||||
$task->getProjectPHIDs()))
|
||||
->setPrimaryObjectPHID($task->getPHID())
|
||||
->setSubscribedPHIDs(
|
||||
array_merge(
|
||||
array_filter(
|
||||
array(
|
||||
$author_phid,
|
||||
$owner_phid,
|
||||
$actor_phid
|
||||
)
|
||||
),
|
||||
$task->getCCPHIDs()))
|
||||
->publish();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorNotificationQuery {
|
||||
|
||||
private $limit = 100;
|
||||
private $userPHID;
|
||||
|
||||
|
||||
public function setLimit($limit) {
|
||||
$this->limit = $limit;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setUserPHID($user_phid) {
|
||||
$this->userPHID = $user_phid;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function execute() {
|
||||
if (!$this->userPHID) {
|
||||
throw new Exception("Call setUser() before executing the query");
|
||||
}
|
||||
|
||||
//TODO throw an exception if no user
|
||||
$story_table = new PhabricatorFeedStoryData();
|
||||
$notification_table = new PhabricatorFeedStoryNotification();
|
||||
|
||||
$conn = $story_table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn,
|
||||
"SELECT story.*, notif.hasViewed FROM %T notif
|
||||
JOIN %T story ON notif.chronologicalKey = story.chronologicalKey
|
||||
WHERE notif.userPHID = %s
|
||||
ORDER BY notif.chronologicalKey desc
|
||||
LIMIT %d",
|
||||
$notification_table->getTableName(),
|
||||
$story_table->getTableName(),
|
||||
$this->userPHID,
|
||||
$this->limit);
|
||||
|
||||
$viewed_map = ipull($data, 'hasViewed', 'chronologicalKey');
|
||||
$data = $story_table->loadAllFromArray($data);
|
||||
|
||||
$stories = array();
|
||||
|
||||
foreach ($data as $story_data) {
|
||||
$class = $story_data->getStoryType();
|
||||
try {
|
||||
if (!class_exists($class) ||
|
||||
!is_subclass_of($class, 'PhabricatorFeedStory')) {
|
||||
$class = 'PhabricatorFeedStoryUnknown';
|
||||
}
|
||||
} catch (PhutilMissingSymbolException $ex) {
|
||||
$class = 'PhabricatorFeedStoryUnknown';
|
||||
}
|
||||
$story = newv($class, array($story_data));
|
||||
$story->setHasViewed($viewed_map[$story->getChronologicalKey()]);
|
||||
$stories[] = $story;
|
||||
}
|
||||
|
||||
return $stories;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorNotificationBuilder {
|
||||
|
||||
private $stories;
|
||||
|
||||
public function __construct(array $stories) {
|
||||
$this->stories = $stories;
|
||||
}
|
||||
|
||||
public function buildView() {
|
||||
|
||||
$stories = $this->stories;
|
||||
|
||||
$handles = array();
|
||||
$objects = array();
|
||||
|
||||
if ($stories) {
|
||||
$handle_phids = array_mergev(mpull($stories, 'getRequiredHandlePHIDs'));
|
||||
$handles = id(new PhabricatorObjectHandleData($handle_phids))
|
||||
->loadHandles();
|
||||
}
|
||||
|
||||
$null_view = new AphrontNullView();
|
||||
|
||||
foreach ($stories as $story) {
|
||||
$story->setHandles($handles);
|
||||
$view = $story->renderNotificationView();
|
||||
$null_view->appendChild($view);
|
||||
}
|
||||
|
||||
return id(new AphrontNullView())->appendChild(
|
||||
'<div class="phabricator-notification-frame">'.
|
||||
$null_view->render().
|
||||
'</div>');
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
abstract class PhabricatorNotificationController
|
||||
extends PhabricatorController {
|
||||
|
||||
public function buildStandardPageResponse($view, array $data) {
|
||||
|
||||
$page = $this->buildStandardPageView();
|
||||
|
||||
$page->setApplicationName('Notification');
|
||||
$page->setBaseURI('/notification/');
|
||||
$page->setTitle(idx($data, 'title'));
|
||||
$page->setGlyph('!');
|
||||
$page->appendChild($view);
|
||||
|
||||
$response = new AphrontWebpageResponse();
|
||||
return $response->setContent($page->render());
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorNotificationTestController
|
||||
extends PhabricatorNotificationController {
|
||||
|
||||
public function processRequest() {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$query = new PhabricatorNotificationQuery();
|
||||
$query->setUserPHID($user->getPHID());
|
||||
|
||||
$stories = $query->execute();
|
||||
|
||||
$builder = new PhabricatorNotificationBuilder($stories);
|
||||
$notifications_view = $builder->buildView();
|
||||
|
||||
$num_unconsumed = 0;
|
||||
|
||||
foreach ($stories as $story) {
|
||||
if (!$story->getHasViewed()) {
|
||||
$num_unconsumed++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$json = array(
|
||||
$notifications_view->render()
|
||||
);
|
||||
|
||||
|
||||
return $this->buildStandardPageResponse(
|
||||
$json,
|
||||
array('title' => 'Notification Test Page'));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorFeedStoryNotification extends PhabricatorFeedDAO {
|
||||
|
||||
protected $userPHID;
|
||||
protected $primaryObjectPHID;
|
||||
protected $chronologicalKey;
|
||||
protected $hasViewed;
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_IDS => self::IDS_MANUAL,
|
||||
self::CONFIG_TIMESTAMPS => false,
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
static public function updateObjectNotificationViews(PhabricatorUser $user,
|
||||
$object_phid) {
|
||||
|
||||
if (PhabricatorEnv::getEnvConfig('notification.enabled')) {
|
||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
|
||||
$notification_table = new PhabricatorFeedStoryNotification();
|
||||
$conn = $notification_table->establishConnection('w');
|
||||
|
||||
queryfx(
|
||||
$conn,
|
||||
"UPDATE %T
|
||||
SET hasViewed = 1
|
||||
WHERE userPHID = %s
|
||||
AND primaryObjectPHID = %s
|
||||
AND hasViewed = 0",
|
||||
$notification_table->getTableName(),
|
||||
$user->getPHID(),
|
||||
$object_phid);
|
||||
|
||||
unset($unguarded);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
abstract class PhabricatorNotificationView extends AphrontView {
|
||||
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
final class PhabricatorNotificationStoryView
|
||||
extends PhabricatorNotificationView {
|
||||
|
||||
private $title;
|
||||
private $phid;
|
||||
private $epoch;
|
||||
private $viewed;
|
||||
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setEpoch($epoch) {
|
||||
$this->epoch = $epoch;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setViewed($viewed) {
|
||||
$this->viewed = $viewed;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
|
||||
$title = $this->title;
|
||||
if (!$this->viewed) {
|
||||
$title = '<b>'.$title.'</b>';
|
||||
}
|
||||
|
||||
$head = phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'phabricator-notification-story-head',
|
||||
),
|
||||
nonempty($title, 'Untitled Story'));
|
||||
|
||||
|
||||
return phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' =>
|
||||
'phabricator-notification '.
|
||||
'phabricator-notification-story-one-line'),
|
||||
$head);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue