mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-26 07:20:57 +01:00
a4a1143b18
Summary: Ref T13266. See <https://discourse.phabricator-community.org/t/notification-page-throws-unrecoverable-fatal-error/2651/>. The "notifications" query currently uses offset paging for no apparent reason (just a legacy issue?), so some of the paging code is only reachable internally. - Stop it from using offset paging, since modern cursor paging is fine here (and Feed has used cursor paging for a long time). - Fix the non-offset paging to work like Feed. Also: - Remove a couple of stub methods with no callsites after cursor refactoring. Test Plan: - Set things up so I had more than 100 notifications and some in the first 100 were policy filtered, to reproduce the issue (I just made `FeedStory` return `NO_ONE` as a visibility policy). - Applied the patch, notifications now page cleanly. - Verified that "Next Page" took me to the right place in the result list. Reviewers: amckinley Reviewed By: amckinley Subscribers: hskiba Maniphest Tasks: T13266 Differential Revision: https://secure.phabricator.com/D20455
169 lines
4 KiB
PHP
169 lines
4 KiB
PHP
<?php
|
|
|
|
/**
|
|
* @task config Configuring the Query
|
|
* @task exec Query Execution
|
|
*/
|
|
final class PhabricatorNotificationQuery
|
|
extends PhabricatorCursorPagedPolicyAwareQuery {
|
|
|
|
private $userPHIDs;
|
|
private $keys;
|
|
private $unread;
|
|
|
|
|
|
/* -( Configuring the Query )---------------------------------------------- */
|
|
|
|
|
|
public function withUserPHIDs(array $user_phids) {
|
|
$this->userPHIDs = $user_phids;
|
|
return $this;
|
|
}
|
|
|
|
public function withKeys(array $keys) {
|
|
$this->keys = $keys;
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Filter results by read/unread status. Note that `true` means to return
|
|
* only unread notifications, while `false` means to return only //read//
|
|
* notifications. The default is `null`, which returns both.
|
|
*
|
|
* @param mixed True or false to filter results by read status. Null to remove
|
|
* the filter.
|
|
* @return this
|
|
* @task config
|
|
*/
|
|
public function withUnread($unread) {
|
|
$this->unread = $unread;
|
|
return $this;
|
|
}
|
|
|
|
|
|
/* -( Query Execution )---------------------------------------------------- */
|
|
|
|
|
|
protected function loadPage() {
|
|
$story_table = new PhabricatorFeedStoryData();
|
|
$notification_table = new PhabricatorFeedStoryNotification();
|
|
|
|
$conn = $story_table->establishConnection('r');
|
|
|
|
$data = queryfx_all(
|
|
$conn,
|
|
'SELECT story.*, notification.hasViewed FROM %R notification
|
|
JOIN %R story ON notification.chronologicalKey = story.chronologicalKey
|
|
%Q
|
|
ORDER BY notification.chronologicalKey DESC
|
|
%Q',
|
|
$notification_table,
|
|
$story_table,
|
|
$this->buildWhereClause($conn),
|
|
$this->buildLimitClause($conn));
|
|
|
|
$viewed_map = ipull($data, 'hasViewed', 'chronologicalKey');
|
|
|
|
$stories = PhabricatorFeedStory::loadAllFromRows(
|
|
$data,
|
|
$this->getViewer());
|
|
|
|
foreach ($stories as $key => $story) {
|
|
$story->setHasViewed($viewed_map[$key]);
|
|
}
|
|
|
|
return $stories;
|
|
}
|
|
|
|
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
|
|
$where = parent::buildWhereClauseParts($conn);
|
|
|
|
if ($this->userPHIDs !== null) {
|
|
$where[] = qsprintf(
|
|
$conn,
|
|
'notification.userPHID IN (%Ls)',
|
|
$this->userPHIDs);
|
|
}
|
|
|
|
if ($this->unread !== null) {
|
|
$where[] = qsprintf(
|
|
$conn,
|
|
'notification.hasViewed = %d',
|
|
(int)!$this->unread);
|
|
}
|
|
|
|
if ($this->keys !== null) {
|
|
$where[] = qsprintf(
|
|
$conn,
|
|
'notification.chronologicalKey IN (%Ls)',
|
|
$this->keys);
|
|
}
|
|
|
|
return $where;
|
|
}
|
|
|
|
protected function willFilterPage(array $stories) {
|
|
foreach ($stories as $key => $story) {
|
|
if (!$story->isVisibleInNotifications()) {
|
|
unset($stories[$key]);
|
|
}
|
|
}
|
|
|
|
return $stories;
|
|
}
|
|
|
|
protected function getDefaultOrderVector() {
|
|
return array('key');
|
|
}
|
|
|
|
public function getBuiltinOrders() {
|
|
return array(
|
|
'newest' => array(
|
|
'vector' => array('key'),
|
|
'name' => pht('Creation (Newest First)'),
|
|
'aliases' => array('created'),
|
|
),
|
|
'oldest' => array(
|
|
'vector' => array('-key'),
|
|
'name' => pht('Creation (Oldest First)'),
|
|
),
|
|
);
|
|
}
|
|
|
|
public function getOrderableColumns() {
|
|
return array(
|
|
'key' => array(
|
|
'table' => 'notification',
|
|
'column' => 'chronologicalKey',
|
|
'type' => 'string',
|
|
'unique' => true,
|
|
),
|
|
);
|
|
}
|
|
|
|
protected function applyExternalCursorConstraintsToQuery(
|
|
PhabricatorCursorPagedPolicyAwareQuery $subquery,
|
|
$cursor) {
|
|
$subquery->withKeys(array($cursor));
|
|
}
|
|
|
|
protected function newExternalCursorStringForResult($object) {
|
|
return $object->getChronologicalKey();
|
|
}
|
|
|
|
protected function newPagingMapFromPartialObject($object) {
|
|
return array(
|
|
'key' => $object->getChronologicalKey(),
|
|
);
|
|
}
|
|
|
|
protected function getPrimaryTableAlias() {
|
|
return 'notification';
|
|
}
|
|
|
|
public function getQueryApplicationClass() {
|
|
return 'PhabricatorNotificationsApplication';
|
|
}
|
|
|
|
}
|