3 final class PhabricatorNotificationPanelController
4 extends PhabricatorNotificationController
{
6 public function handleRequest(AphrontRequest
$request) {
7 $viewer = $request->getViewer();
9 $unread_count = $viewer->getUnreadNotificationCount();
11 $warning = $this->prunePhantomNotifications($unread_count);
13 $query = id(new PhabricatorNotificationQuery())
15 ->withUserPHIDs(array($viewer->getPHID()))
18 $stories = $query->execute();
20 $clear_ui_class = 'phabricator-notification-clear-all';
21 $clear_uri = id(new PhutilURI('/notification/clear/'));
23 $builder = id(new PhabricatorNotificationBuilder($stories))
26 $notifications_view = $builder->buildView();
27 $content = $notifications_view->render();
28 $clear_uri->replaceQueryParam(
30 head($stories)->getChronologicalKey());
32 $content = phutil_tag_div(
33 'phabricator-notification no-notifications',
34 pht('You have no notifications.'));
35 $clear_ui_class .= ' disabled';
37 $clear_ui = javelin_tag(
40 'sigil' => 'workflow',
41 'href' => (string)$clear_uri,
42 'class' => $clear_ui_class,
44 pht('Mark All Read'));
46 $notifications_link = phutil_tag(
49 'href' => '/notification/',
51 pht('Notifications'));
53 $connection_status = new PhabricatorNotificationStatusView();
55 $connection_ui = phutil_tag(
58 'class' => 'phabricator-notification-footer',
65 'class' => 'phabricator-notification-header',
80 'content' => $content,
81 'number' => (int)$unread_count,
84 return id(new AphrontAjaxResponse())->setContent($json);
87 private function prunePhantomNotifications($unread_count) {
88 // See T8953. If you have an unread notification about an object you
89 // do not have permission to view, it isn't possible to clear it by
90 // visiting the object. Identify these notifications and mark them as
93 $viewer = $this->getViewer();
99 $table = new PhabricatorFeedStoryNotification();
100 $conn = $table->establishConnection('r');
104 'SELECT chronologicalKey, primaryObjectPHID FROM %T
105 WHERE userPHID = %s AND hasViewed = 0',
106 $table->getTableName(),
113 foreach ($rows as $row) {
114 $map[$row['primaryObjectPHID']][] = $row['chronologicalKey'];
117 $handles = $viewer->loadHandles(array_keys($map));
118 $purge_keys = array();
119 foreach ($handles as $handle) {
120 $phid = $handle->getPHID();
121 if ($handle->isComplete()) {
125 foreach ($map[$phid] as $chronological_key) {
126 $purge_keys[] = $chronological_key;
134 $unguarded = AphrontWriteGuard
::beginScopedUnguardedWrites();
136 $conn = $table->establishConnection('w');
139 'UPDATE %T SET hasViewed = 1
140 WHERE userPHID = %s AND chronologicalKey IN (%Ls)',
141 $table->getTableName(),
145 PhabricatorUserCache
::clearCache(
146 PhabricatorUserNotificationCountCacheType
::KEY_COUNT
,
154 'class' => 'phabricator-notification phabricator-notification-warning',
157 '%s notification(s) about objects which no longer exist or which '.
158 'you can no longer see were discarded.',
159 phutil_count($purge_keys)));