@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PhabricatorNotificationPanelController
4 extends PhabricatorNotificationController {
5
6 public function handleRequest(AphrontRequest $request) {
7 $viewer = $request->getViewer();
8
9 $unread_count = $viewer->getUnreadNotificationCount();
10
11 $warning = $this->prunePhantomNotifications($unread_count);
12
13 $query = id(new PhabricatorNotificationQuery())
14 ->setViewer($viewer)
15 ->withUserPHIDs(array($viewer->getPHID()))
16 ->setLimit(10);
17
18 $stories = $query->execute();
19
20 $clear_ui_class = 'phabricator-notification-clear-all';
21 $clear_uri = new PhutilURI('/notification/clear/');
22 if ($stories) {
23 $builder = id(new PhabricatorNotificationBuilder($stories))
24 ->setUser($viewer);
25
26 $notifications_view = $builder->buildView();
27 $content = $notifications_view->render();
28 $clear_uri->replaceQueryParam(
29 'chronoKey',
30 head($stories)->getChronologicalKey());
31 } else {
32 $content = phutil_tag_div(
33 'phabricator-notification no-notifications',
34 pht('You have no notifications.'));
35 $clear_ui_class .= ' disabled';
36 }
37 $clear_ui = javelin_tag(
38 'a',
39 array(
40 'sigil' => 'workflow',
41 'href' => (string)$clear_uri,
42 'class' => $clear_ui_class,
43 ),
44 pht('Mark All Read'));
45
46 $notifications_link = phutil_tag(
47 'a',
48 array(
49 'href' => '/notification/',
50 ),
51 pht('Notifications'));
52
53 $connection_status = new PhabricatorNotificationStatusView();
54
55 $connection_ui = phutil_tag(
56 'div',
57 array(
58 'class' => 'phabricator-notification-footer',
59 ),
60 $connection_status);
61
62 $header = phutil_tag(
63 'div',
64 array(
65 'class' => 'phabricator-notification-header',
66 ),
67 array(
68 $notifications_link,
69 $clear_ui,
70 ));
71
72 $content = hsprintf(
73 '%s%s%s%s',
74 $header,
75 $warning,
76 $content,
77 $connection_ui);
78
79 $json = array(
80 'content' => $content,
81 'number' => (int)$unread_count,
82 );
83
84 return id(new AphrontAjaxResponse())->setContent($json);
85 }
86
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
91 // read.
92
93 $viewer = $this->getViewer();
94
95 if (!$unread_count) {
96 return null;
97 }
98
99 $table = new PhabricatorFeedStoryNotification();
100 $conn = $table->establishConnection('r');
101
102 $rows = queryfx_all(
103 $conn,
104 'SELECT chronologicalKey, primaryObjectPHID FROM %T
105 WHERE userPHID = %s AND hasViewed = 0',
106 $table->getTableName(),
107 $viewer->getPHID());
108 if (!$rows) {
109 return null;
110 }
111
112 $map = array();
113 foreach ($rows as $row) {
114 $map[$row['primaryObjectPHID']][] = $row['chronologicalKey'];
115 }
116
117 $handles = $viewer->loadHandles(array_keys($map));
118 $purge_keys = array();
119 foreach ($handles as $handle) {
120 $phid = $handle->getPHID();
121 if ($handle->isComplete()) {
122 continue;
123 }
124
125 foreach ($map[$phid] as $chronological_key) {
126 $purge_keys[] = $chronological_key;
127 }
128 }
129
130 if (!$purge_keys) {
131 return null;
132 }
133
134 $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
135
136 $conn = $table->establishConnection('w');
137 queryfx(
138 $conn,
139 'UPDATE %T SET hasViewed = 1
140 WHERE userPHID = %s AND chronologicalKey IN (%Ls)',
141 $table->getTableName(),
142 $viewer->getPHID(),
143 $purge_keys);
144
145 PhabricatorUserCache::clearCache(
146 PhabricatorUserNotificationCountCacheType::KEY_COUNT,
147 $viewer->getPHID());
148
149 unset($unguarded);
150
151 return phutil_tag(
152 'div',
153 array(
154 'class' => 'phabricator-notification phabricator-notification-warning',
155 ),
156 pht(
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)));
160 }
161
162
163}