mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-03 02:18:24 +01:00
Consolidate feed query code
Summary: Simplify FeedQuery by making it extend from PhabricatorIDPagedPolicyQuery Test Plan: Looked at feed on home, projects, user profile, and called `feed.query`. Reviewers: btrahan Reviewed By: btrahan CC: aran Differential Revision: https://secure.phabricator.com/D2905
This commit is contained in:
parent
3a453f2cce
commit
310cf00fc3
9 changed files with 86 additions and 133 deletions
|
@ -1010,6 +1010,9 @@ return array(
|
||||||
// pages using iframes. These feeds are completely public, and a login is not
|
// pages using iframes. These feeds are completely public, and a login is not
|
||||||
// required to view them! This is intended for things like open source
|
// required to view them! This is intended for things like open source
|
||||||
// projects that want to expose an activity feed on the project homepage.
|
// projects that want to expose an activity feed on the project homepage.
|
||||||
|
//
|
||||||
|
// NOTE: You must also set `policy.allow-public` to true for this setting
|
||||||
|
// to work properly.
|
||||||
'feed.public' => false,
|
'feed.public' => false,
|
||||||
|
|
||||||
|
|
||||||
|
@ -1020,6 +1023,7 @@ return array(
|
||||||
'amazon-ec2.access-key' => null,
|
'amazon-ec2.access-key' => null,
|
||||||
'amazon-ec2.secret-key' => null,
|
'amazon-ec2.secret-key' => null,
|
||||||
|
|
||||||
|
|
||||||
// -- Customization --------------------------------------------------------- //
|
// -- Customization --------------------------------------------------------- //
|
||||||
|
|
||||||
// Paths to additional phutil libraries to load.
|
// Paths to additional phutil libraries to load.
|
||||||
|
|
|
@ -88,7 +88,8 @@ final class ConduitAPI_feed_query_Method extends ConduitAPIMethod {
|
||||||
$query = id(new PhabricatorFeedQuery())
|
$query = id(new PhabricatorFeedQuery())
|
||||||
->setLimit($limit)
|
->setLimit($limit)
|
||||||
->setFilterPHIDs($filter_phids)
|
->setFilterPHIDs($filter_phids)
|
||||||
->setAfter($after);
|
->setViewer($user)
|
||||||
|
->setAfterID($after);
|
||||||
$stories = $query->execute();
|
$stories = $query->execute();
|
||||||
|
|
||||||
if ($stories) {
|
if ($stories) {
|
||||||
|
|
|
@ -401,55 +401,16 @@ final class PhabricatorDirectoryMainController
|
||||||
$user_phid = $user->getPHID();
|
$user_phid = $user->getPHID();
|
||||||
|
|
||||||
$feed_query = new PhabricatorFeedQuery();
|
$feed_query = new PhabricatorFeedQuery();
|
||||||
|
$feed_query->setViewer($user);
|
||||||
if ($phids) {
|
if ($phids) {
|
||||||
$feed_query->setFilterPHIDs($phids);
|
$feed_query->setFilterPHIDs($phids);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: All this limit stuff should probably be consolidated into the
|
$pager = new AphrontIDPagerView();
|
||||||
// feed query?
|
$pager->readFromRequest($request);
|
||||||
|
$pager->setPageSize(200);
|
||||||
|
|
||||||
$old_link = null;
|
$feed = $feed_query->executeWithPager($pager);
|
||||||
$new_link = null;
|
|
||||||
|
|
||||||
$feed_query->setAfter($request->getStr('after'));
|
|
||||||
$feed_query->setBefore($request->getStr('before'));
|
|
||||||
$limit = 500;
|
|
||||||
|
|
||||||
// Grab one more story than we intend to display so we can figure out
|
|
||||||
// if we need to render an "Older Posts" link or not (with reasonable
|
|
||||||
// accuracy, at least).
|
|
||||||
$feed_query->setLimit($limit + 1);
|
|
||||||
$feed = $feed_query->execute();
|
|
||||||
$extra_row = (count($feed) == $limit + 1);
|
|
||||||
|
|
||||||
$have_new = ($request->getStr('before')) ||
|
|
||||||
($request->getStr('after') && $extra_row);
|
|
||||||
|
|
||||||
$have_old = ($request->getStr('after')) ||
|
|
||||||
($request->getStr('before') && $extra_row) ||
|
|
||||||
(!$request->getStr('before') &&
|
|
||||||
!$request->getStr('after') &&
|
|
||||||
$extra_row);
|
|
||||||
$feed = array_slice($feed, 0, $limit, $preserve_keys = true);
|
|
||||||
|
|
||||||
if ($have_old) {
|
|
||||||
$old_link = phutil_render_tag(
|
|
||||||
'a',
|
|
||||||
array(
|
|
||||||
'href' => '?before='.end($feed)->getChronologicalKey(),
|
|
||||||
'class' => 'phabricator-feed-older-link',
|
|
||||||
),
|
|
||||||
"Older Stories \xC2\xBB");
|
|
||||||
}
|
|
||||||
if ($have_new) {
|
|
||||||
$new_link = phutil_render_tag(
|
|
||||||
'a',
|
|
||||||
array(
|
|
||||||
'href' => '?after='.reset($feed)->getChronologicalKey(),
|
|
||||||
'class' => 'phabricator-feed-newer-link',
|
|
||||||
),
|
|
||||||
"\xC2\xAB Newer Stories");
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder = new PhabricatorFeedBuilder($feed);
|
$builder = new PhabricatorFeedBuilder($feed);
|
||||||
$builder->setUser($user);
|
$builder->setUser($user);
|
||||||
|
@ -464,8 +425,7 @@ final class PhabricatorDirectoryMainController
|
||||||
'</div>'.
|
'</div>'.
|
||||||
$feed_view->render().
|
$feed_view->render().
|
||||||
'<div class="phabricator-feed-frame">'.
|
'<div class="phabricator-feed-frame">'.
|
||||||
$new_link.
|
$pager->render().
|
||||||
$old_link.
|
|
||||||
'</div>'.
|
'</div>'.
|
||||||
'</div>';
|
'</div>';
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,98 +16,74 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
final class PhabricatorFeedQuery {
|
final class PhabricatorFeedQuery extends PhabricatorIDPagedPolicyQuery {
|
||||||
|
|
||||||
private $filterPHIDs;
|
private $filterPHIDs;
|
||||||
private $limit = 100;
|
|
||||||
private $after;
|
|
||||||
private $before;
|
|
||||||
|
|
||||||
public function setFilterPHIDs(array $phids) {
|
public function setFilterPHIDs(array $phids) {
|
||||||
$this->filterPHIDs = $phids;
|
$this->filterPHIDs = $phids;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setLimit($limit) {
|
public function loadPage() {
|
||||||
$this->limit = $limit;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setAfter($after) {
|
|
||||||
$this->after = $after;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setBefore($before) {
|
|
||||||
$this->before = $before;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function execute() {
|
|
||||||
|
|
||||||
$ref_table = new PhabricatorFeedStoryReference();
|
|
||||||
$story_table = new PhabricatorFeedStoryData();
|
$story_table = new PhabricatorFeedStoryData();
|
||||||
|
|
||||||
$conn = $story_table->establishConnection('r');
|
$conn = $story_table->establishConnection('r');
|
||||||
|
|
||||||
|
$data = queryfx_all(
|
||||||
|
$conn,
|
||||||
|
'SELECT story.* FROM %T story %Q %Q %Q %Q %Q',
|
||||||
|
$story_table->getTableName(),
|
||||||
|
$this->buildJoinClause($conn),
|
||||||
|
$this->buildWhereClause($conn),
|
||||||
|
$this->buildGroupClause($conn),
|
||||||
|
$this->buildOrderClause($conn),
|
||||||
|
$this->buildLimitClause($conn));
|
||||||
|
|
||||||
|
$results = PhabricatorFeedStory::loadAllFromRows($data);
|
||||||
|
|
||||||
|
return $this->processResults($results);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildJoinClause(AphrontDatabaseConnection $conn_r) {
|
||||||
|
// NOTE: We perform this join unconditionally (even if we have no filter
|
||||||
|
// PHIDs) to omit rows which have no story references. These story data
|
||||||
|
// rows are notifications or realtime alerts.
|
||||||
|
|
||||||
|
$ref_table = new PhabricatorFeedStoryReference();
|
||||||
|
return qsprintf(
|
||||||
|
$conn_r,
|
||||||
|
'JOIN %T ref ON ref.chronologicalKey = story.chronologicalKey',
|
||||||
|
$ref_table->getTableName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||||
$where = array();
|
$where = array();
|
||||||
|
|
||||||
if ($this->filterPHIDs) {
|
if ($this->filterPHIDs) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn_r,
|
||||||
'ref.objectPHID IN (%Ls)',
|
'ref.objectPHID IN (%Ls)',
|
||||||
$this->filterPHIDs);
|
$this->filterPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
// For "before" queries, we can just add a constraint to the WHERE clause.
|
$where[] = $this->buildPagingClause($conn_r);
|
||||||
// For "after" queries, we must also reverse the result ordering, since
|
|
||||||
// otherwise we'll always grab the first page of results if there's a limit.
|
|
||||||
// After MySQL applies the limit, we reverse the page in PHP (below) to
|
|
||||||
// ensure consistent ordering.
|
|
||||||
|
|
||||||
$order = 'DESC';
|
return $this->formatWhereClause($where);
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->after) {
|
private function buildGroupClause(AphrontDatabaseConnection $conn_r) {
|
||||||
$where[] = qsprintf(
|
return qsprintf(
|
||||||
$conn,
|
$conn_r,
|
||||||
'ref.chronologicalKey > %s',
|
'GROUP BY ref.chronologicalKey');
|
||||||
$this->after);
|
}
|
||||||
$order = 'ASC';
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($this->before) {
|
protected function getPagingColumn() {
|
||||||
$where[] = qsprintf(
|
return 'ref.chronologicalKey';
|
||||||
$conn,
|
}
|
||||||
'ref.chronologicalKey < %s',
|
|
||||||
$this->before);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($where) {
|
protected function getPagingValue($item) {
|
||||||
$where = 'WHERE ('.implode(') AND (', $where).')';
|
return $item->getChronologicalKey();
|
||||||
} else {
|
|
||||||
$where = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
$data = queryfx_all(
|
|
||||||
$conn,
|
|
||||||
'SELECT story.* FROM %T ref
|
|
||||||
JOIN %T story ON ref.chronologicalKey = story.chronologicalKey
|
|
||||||
%Q
|
|
||||||
GROUP BY ref.chronologicalKey
|
|
||||||
ORDER BY ref.chronologicalKey %Q
|
|
||||||
LIMIT %d',
|
|
||||||
$ref_table->getTableName(),
|
|
||||||
$story_table->getTableName(),
|
|
||||||
$where,
|
|
||||||
$order,
|
|
||||||
$this->limit);
|
|
||||||
|
|
||||||
if ($order != 'DESC') {
|
|
||||||
// If we did order ASC to pull 'after' data, reverse the result set so
|
|
||||||
// that stories are returned in a consistent (descending) order.
|
|
||||||
$data = array_reverse($data);
|
|
||||||
}
|
|
||||||
|
|
||||||
return PhabricatorFeedStory::loadAllFromRows($data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,22 +24,21 @@ final class PhabricatorFeedPublicStreamController
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processRequest() {
|
public function processRequest() {
|
||||||
|
|
||||||
if (!PhabricatorEnv::getEnvConfig('feed.public')) {
|
if (!PhabricatorEnv::getEnvConfig('feed.public')) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Profile images won't render correctly for logged-out users.
|
|
||||||
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
$query = new PhabricatorFeedQuery();
|
$query = new PhabricatorFeedQuery();
|
||||||
|
$query->setViewer($viewer);
|
||||||
$stories = $query->execute();
|
$stories = $query->execute();
|
||||||
|
|
||||||
$builder = new PhabricatorFeedBuilder($stories);
|
$builder = new PhabricatorFeedBuilder($stories);
|
||||||
$builder
|
$builder
|
||||||
->setFramed(true)
|
->setFramed(true)
|
||||||
->setUser($request->getUser());
|
->setUser($viewer);
|
||||||
|
|
||||||
$view = $builder->buildView();
|
$view = $builder->buildView();
|
||||||
|
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
*
|
*
|
||||||
* @task load Loading Stories
|
* @task load Loading Stories
|
||||||
*/
|
*/
|
||||||
abstract class PhabricatorFeedStory {
|
abstract class PhabricatorFeedStory implements PhabricatorPolicyInterface {
|
||||||
|
|
||||||
private $data;
|
private $data;
|
||||||
private $hasViewed;
|
private $hasViewed;
|
||||||
|
@ -74,6 +74,21 @@ abstract class PhabricatorFeedStory {
|
||||||
return $stories;
|
return $stories;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getCapabilities() {
|
||||||
|
return array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy($capability) {
|
||||||
|
return PhabricatorEnv::getEnvConfig('feed.public')
|
||||||
|
? PhabricatorPolicies::POLICY_PUBLIC
|
||||||
|
: PhabricatorPolicies::POLICY_USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function setPrimaryObjectPHID($primary_object_phid) {
|
public function setPrimaryObjectPHID($primary_object_phid) {
|
||||||
$this->primaryObjectPHID = $primary_object_phid;
|
$this->primaryObjectPHID = $primary_object_phid;
|
||||||
|
|
|
@ -204,15 +204,18 @@ final class PhabricatorPeopleProfileController
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderUserFeed(PhabricatorUser $user) {
|
private function renderUserFeed(PhabricatorUser $user) {
|
||||||
|
$viewer = $this->getRequest()->getUser();
|
||||||
|
|
||||||
$query = new PhabricatorFeedQuery();
|
$query = new PhabricatorFeedQuery();
|
||||||
$query->setFilterPHIDs(
|
$query->setFilterPHIDs(
|
||||||
array(
|
array(
|
||||||
$user->getPHID(),
|
$user->getPHID(),
|
||||||
));
|
));
|
||||||
|
$query->setViewer($viewer);
|
||||||
$stories = $query->execute();
|
$stories = $query->execute();
|
||||||
|
|
||||||
$builder = new PhabricatorFeedBuilder($stories);
|
$builder = new PhabricatorFeedBuilder($stories);
|
||||||
$builder->setUser($this->getRequest()->getUser());
|
$builder->setUser($viewer);
|
||||||
$view = $builder->buildView();
|
$view = $builder->buildView();
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
|
@ -78,6 +78,7 @@ final class PhabricatorProjectProfileController
|
||||||
array(
|
array(
|
||||||
$project->getPHID(),
|
$project->getPHID(),
|
||||||
));
|
));
|
||||||
|
$query->setViewer($this->getRequest()->getUser());
|
||||||
$stories = $query->execute();
|
$stories = $query->execute();
|
||||||
|
|
||||||
$content .= $this->renderStories($stories);
|
$content .= $this->renderStories($stories);
|
||||||
|
@ -243,19 +244,13 @@ final class PhabricatorProjectProfileController
|
||||||
|
|
||||||
$query = new PhabricatorFeedQuery();
|
$query = new PhabricatorFeedQuery();
|
||||||
$query->setFilterPHIDs(array($project->getPHID()));
|
$query->setFilterPHIDs(array($project->getPHID()));
|
||||||
|
$query->setViewer($this->getRequest()->getUser());
|
||||||
$stories = $query->execute();
|
$stories = $query->execute();
|
||||||
|
|
||||||
if (!$stories) {
|
if (!$stories) {
|
||||||
return 'There are no stories about this project.';
|
return 'There are no stories about this project.';
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = new PhabricatorFeedQuery();
|
|
||||||
$query->setFilterPHIDs(
|
|
||||||
array(
|
|
||||||
$project->getPHID(),
|
|
||||||
));
|
|
||||||
$stories = $query->execute();
|
|
||||||
|
|
||||||
return $this->renderStories($stories);
|
return $this->renderStories($stories);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,13 +65,13 @@ abstract class PhabricatorIDPagedPolicyQuery extends PhabricatorPolicyQuery {
|
||||||
if ($this->beforeID) {
|
if ($this->beforeID) {
|
||||||
return qsprintf(
|
return qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'%C > %s',
|
'%Q > %s',
|
||||||
$this->getPagingColumn(),
|
$this->getPagingColumn(),
|
||||||
$this->beforeID);
|
$this->beforeID);
|
||||||
} else if ($this->afterID) {
|
} else if ($this->afterID) {
|
||||||
return qsprintf(
|
return qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'%C < %s',
|
'%Q < %s',
|
||||||
$this->getPagingColumn(),
|
$this->getPagingColumn(),
|
||||||
$this->afterID);
|
$this->afterID);
|
||||||
}
|
}
|
||||||
|
@ -83,12 +83,12 @@ abstract class PhabricatorIDPagedPolicyQuery extends PhabricatorPolicyQuery {
|
||||||
if ($this->beforeID) {
|
if ($this->beforeID) {
|
||||||
return qsprintf(
|
return qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'ORDER BY %C ASC',
|
'ORDER BY %Q ASC',
|
||||||
$this->getPagingColumn());
|
$this->getPagingColumn());
|
||||||
} else {
|
} else {
|
||||||
return qsprintf(
|
return qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'ORDER BY %C DESC',
|
'ORDER BY %Q DESC',
|
||||||
$this->getPagingColumn());
|
$this->getPagingColumn());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue