Correct Aphlict websocket URI construction after PHP8 compatibility changes
[phabricator.git] / src / applications / notification / query / PhabricatorNotificationQuery.php
blob2cd97f25c8c1aaf8bec27ee3299ad03b0d4eaaad
1 <?php
3 /**
4 * @task config Configuring the Query
5 * @task exec Query Execution
6 */
7 final class PhabricatorNotificationQuery
8 extends PhabricatorCursorPagedPolicyAwareQuery {
10 private $userPHIDs;
11 private $keys;
12 private $unread;
15 /* -( Configuring the Query )---------------------------------------------- */
18 public function withUserPHIDs(array $user_phids) {
19 $this->userPHIDs = $user_phids;
20 return $this;
23 public function withKeys(array $keys) {
24 $this->keys = $keys;
25 return $this;
29 /**
30 * Filter results by read/unread status. Note that `true` means to return
31 * only unread notifications, while `false` means to return only //read//
32 * notifications. The default is `null`, which returns both.
34 * @param mixed True or false to filter results by read status. Null to remove
35 * the filter.
36 * @return this
37 * @task config
39 public function withUnread($unread) {
40 $this->unread = $unread;
41 return $this;
45 /* -( Query Execution )---------------------------------------------------- */
48 protected function loadPage() {
49 $story_table = new PhabricatorFeedStoryData();
50 $notification_table = new PhabricatorFeedStoryNotification();
52 $conn = $story_table->establishConnection('r');
54 $data = queryfx_all(
55 $conn,
56 'SELECT story.*, notification.hasViewed FROM %R notification
57 JOIN %R story ON notification.chronologicalKey = story.chronologicalKey
59 ORDER BY notification.chronologicalKey DESC
60 %Q',
61 $notification_table,
62 $story_table,
63 $this->buildWhereClause($conn),
64 $this->buildLimitClause($conn));
66 return $data;
69 protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
70 $where = parent::buildWhereClauseParts($conn);
72 if ($this->userPHIDs !== null) {
73 $where[] = qsprintf(
74 $conn,
75 'notification.userPHID IN (%Ls)',
76 $this->userPHIDs);
79 if ($this->unread !== null) {
80 $where[] = qsprintf(
81 $conn,
82 'notification.hasViewed = %d',
83 (int)!$this->unread);
86 if ($this->keys !== null) {
87 $where[] = qsprintf(
88 $conn,
89 'notification.chronologicalKey IN (%Ls)',
90 $this->keys);
93 return $where;
96 protected function willFilterPage(array $rows) {
97 // See T13623. The policy model here is outdated and awkward.
99 // Users may have notifications about objects they can no longer see.
100 // Two ways this can arise: destroy an object; or change an object's
101 // view policy to exclude a user.
103 // "PhabricatorFeedStory::loadAllFromRows()" does its own policy filtering.
104 // This doesn't align well with modern query sequencing, but we should be
105 // able to get away with it by loading here.
107 // See T13623. Although most queries for notifications return unique
108 // stories, this isn't a guarantee.
109 $story_map = ipull($rows, null, 'chronologicalKey');
111 $viewer = $this->getViewer();
112 $stories = PhabricatorFeedStory::loadAllFromRows($story_map, $viewer);
113 $stories = mpull($stories, null, 'getChronologicalKey');
115 $results = array();
116 foreach ($rows as $row) {
117 $story_key = $row['chronologicalKey'];
118 $has_viewed = $row['hasViewed'];
120 if (!isset($stories[$story_key])) {
121 // NOTE: We can't call "didRejectResult()" here because we don't have
122 // a policy object to pass.
123 continue;
126 $story = id(clone $stories[$story_key])
127 ->setHasViewed($has_viewed);
129 if (!$story->isVisibleInNotifications()) {
130 continue;
133 $results[] = $story;
136 return $results;
139 protected function getDefaultOrderVector() {
140 return array('key');
143 public function getBuiltinOrders() {
144 return array(
145 'newest' => array(
146 'vector' => array('key'),
147 'name' => pht('Creation (Newest First)'),
148 'aliases' => array('created'),
150 'oldest' => array(
151 'vector' => array('-key'),
152 'name' => pht('Creation (Oldest First)'),
157 public function getOrderableColumns() {
158 return array(
159 'key' => array(
160 'table' => 'notification',
161 'column' => 'chronologicalKey',
162 'type' => 'string',
163 'unique' => true,
168 protected function applyExternalCursorConstraintsToQuery(
169 PhabricatorCursorPagedPolicyAwareQuery $subquery,
170 $cursor) {
172 $subquery
173 ->withKeys(array($cursor))
174 ->setLimit(1);
178 protected function newExternalCursorStringForResult($object) {
179 return $object->getChronologicalKey();
182 protected function newPagingMapFromPartialObject($object) {
183 return array(
184 'key' => $object['chronologicalKey'],
188 protected function getPrimaryTableAlias() {
189 return 'notification';
192 public function getQueryApplicationClass() {
193 return 'PhabricatorNotificationsApplication';