@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
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+83 -130
+4
conf/default.conf.php
··· 1010 1010 // pages using iframes. These feeds are completely public, and a login is not 1011 1011 // required to view them! This is intended for things like open source 1012 1012 // projects that want to expose an activity feed on the project homepage. 1013 + // 1014 + // NOTE: You must also set `policy.allow-public` to true for this setting 1015 + // to work properly. 1013 1016 'feed.public' => false, 1014 1017 1015 1018 ··· 1019 1022 // EC2 credentials here. 1020 1023 'amazon-ec2.access-key' => null, 1021 1024 'amazon-ec2.secret-key' => null, 1025 + 1022 1026 1023 1027 // -- Customization --------------------------------------------------------- // 1024 1028
+2 -1
src/applications/conduit/method/feed/ConduitAPI_feed_query_Method.php
··· 88 88 $query = id(new PhabricatorFeedQuery()) 89 89 ->setLimit($limit) 90 90 ->setFilterPHIDs($filter_phids) 91 - ->setAfter($after); 91 + ->setViewer($user) 92 + ->setAfterID($after); 92 93 $stories = $query->execute(); 93 94 94 95 if ($stories) {
+6 -46
src/applications/directory/controller/PhabricatorDirectoryMainController.php
··· 401 401 $user_phid = $user->getPHID(); 402 402 403 403 $feed_query = new PhabricatorFeedQuery(); 404 + $feed_query->setViewer($user); 404 405 if ($phids) { 405 406 $feed_query->setFilterPHIDs($phids); 406 407 } 407 408 408 - // TODO: All this limit stuff should probably be consolidated into the 409 - // feed query? 410 - 411 - $old_link = null; 412 - $new_link = null; 413 - 414 - $feed_query->setAfter($request->getStr('after')); 415 - $feed_query->setBefore($request->getStr('before')); 416 - $limit = 500; 417 - 418 - // Grab one more story than we intend to display so we can figure out 419 - // if we need to render an "Older Posts" link or not (with reasonable 420 - // accuracy, at least). 421 - $feed_query->setLimit($limit + 1); 422 - $feed = $feed_query->execute(); 423 - $extra_row = (count($feed) == $limit + 1); 424 - 425 - $have_new = ($request->getStr('before')) || 426 - ($request->getStr('after') && $extra_row); 427 - 428 - $have_old = ($request->getStr('after')) || 429 - ($request->getStr('before') && $extra_row) || 430 - (!$request->getStr('before') && 431 - !$request->getStr('after') && 432 - $extra_row); 433 - $feed = array_slice($feed, 0, $limit, $preserve_keys = true); 409 + $pager = new AphrontIDPagerView(); 410 + $pager->readFromRequest($request); 411 + $pager->setPageSize(200); 434 412 435 - if ($have_old) { 436 - $old_link = phutil_render_tag( 437 - 'a', 438 - array( 439 - 'href' => '?before='.end($feed)->getChronologicalKey(), 440 - 'class' => 'phabricator-feed-older-link', 441 - ), 442 - "Older Stories \xC2\xBB"); 443 - } 444 - if ($have_new) { 445 - $new_link = phutil_render_tag( 446 - 'a', 447 - array( 448 - 'href' => '?after='.reset($feed)->getChronologicalKey(), 449 - 'class' => 'phabricator-feed-newer-link', 450 - ), 451 - "\xC2\xAB Newer Stories"); 452 - } 413 + $feed = $feed_query->executeWithPager($pager); 453 414 454 415 $builder = new PhabricatorFeedBuilder($feed); 455 416 $builder->setUser($user); ··· 464 425 '</div>'. 465 426 $feed_view->render(). 466 427 '<div class="phabricator-feed-frame">'. 467 - $new_link. 468 - $old_link. 428 + $pager->render(). 469 429 '</div>'. 470 430 '</div>'; 471 431 }
+42 -66
src/applications/feed/PhabricatorFeedQuery.php
··· 16 16 * limitations under the License. 17 17 */ 18 18 19 - final class PhabricatorFeedQuery { 19 + final class PhabricatorFeedQuery extends PhabricatorIDPagedPolicyQuery { 20 20 21 21 private $filterPHIDs; 22 - private $limit = 100; 23 - private $after; 24 - private $before; 25 22 26 23 public function setFilterPHIDs(array $phids) { 27 24 $this->filterPHIDs = $phids; 28 25 return $this; 29 26 } 30 27 31 - public function setLimit($limit) { 32 - $this->limit = $limit; 33 - return $this; 34 - } 28 + public function loadPage() { 35 29 36 - public function setAfter($after) { 37 - $this->after = $after; 38 - return $this; 39 - } 30 + $story_table = new PhabricatorFeedStoryData(); 31 + $conn = $story_table->establishConnection('r'); 40 32 41 - public function setBefore($before) { 42 - $this->before = $before; 43 - return $this; 33 + $data = queryfx_all( 34 + $conn, 35 + 'SELECT story.* FROM %T story %Q %Q %Q %Q %Q', 36 + $story_table->getTableName(), 37 + $this->buildJoinClause($conn), 38 + $this->buildWhereClause($conn), 39 + $this->buildGroupClause($conn), 40 + $this->buildOrderClause($conn), 41 + $this->buildLimitClause($conn)); 42 + 43 + $results = PhabricatorFeedStory::loadAllFromRows($data); 44 + 45 + return $this->processResults($results); 44 46 } 45 47 46 - public function execute() { 48 + private function buildJoinClause(AphrontDatabaseConnection $conn_r) { 49 + // NOTE: We perform this join unconditionally (even if we have no filter 50 + // PHIDs) to omit rows which have no story references. These story data 51 + // rows are notifications or realtime alerts. 47 52 48 53 $ref_table = new PhabricatorFeedStoryReference(); 49 - $story_table = new PhabricatorFeedStoryData(); 54 + return qsprintf( 55 + $conn_r, 56 + 'JOIN %T ref ON ref.chronologicalKey = story.chronologicalKey', 57 + $ref_table->getTableName()); 58 + } 50 59 51 - $conn = $story_table->establishConnection('r'); 60 + private function buildWhereClause(AphrontDatabaseConnection $conn_r) { 61 + $where = array(); 52 62 53 - $where = array(); 54 63 if ($this->filterPHIDs) { 55 64 $where[] = qsprintf( 56 - $conn, 65 + $conn_r, 57 66 'ref.objectPHID IN (%Ls)', 58 67 $this->filterPHIDs); 59 68 } 60 69 61 - // For "before" queries, we can just add a constraint to the WHERE clause. 62 - // For "after" queries, we must also reverse the result ordering, since 63 - // otherwise we'll always grab the first page of results if there's a limit. 64 - // After MySQL applies the limit, we reverse the page in PHP (below) to 65 - // ensure consistent ordering. 66 - 67 - $order = 'DESC'; 70 + $where[] = $this->buildPagingClause($conn_r); 68 71 69 - if ($this->after) { 70 - $where[] = qsprintf( 71 - $conn, 72 - 'ref.chronologicalKey > %s', 73 - $this->after); 74 - $order = 'ASC'; 75 - } 72 + return $this->formatWhereClause($where); 73 + } 76 74 77 - if ($this->before) { 78 - $where[] = qsprintf( 79 - $conn, 80 - 'ref.chronologicalKey < %s', 81 - $this->before); 82 - } 83 - 84 - if ($where) { 85 - $where = 'WHERE ('.implode(') AND (', $where).')'; 86 - } else { 87 - $where = ''; 88 - } 89 - 90 - $data = queryfx_all( 91 - $conn, 92 - 'SELECT story.* FROM %T ref 93 - JOIN %T story ON ref.chronologicalKey = story.chronologicalKey 94 - %Q 95 - GROUP BY ref.chronologicalKey 96 - ORDER BY ref.chronologicalKey %Q 97 - LIMIT %d', 98 - $ref_table->getTableName(), 99 - $story_table->getTableName(), 100 - $where, 101 - $order, 102 - $this->limit); 75 + private function buildGroupClause(AphrontDatabaseConnection $conn_r) { 76 + return qsprintf( 77 + $conn_r, 78 + 'GROUP BY ref.chronologicalKey'); 79 + } 103 80 104 - if ($order != 'DESC') { 105 - // If we did order ASC to pull 'after' data, reverse the result set so 106 - // that stories are returned in a consistent (descending) order. 107 - $data = array_reverse($data); 108 - } 81 + protected function getPagingColumn() { 82 + return 'ref.chronologicalKey'; 83 + } 109 84 110 - return PhabricatorFeedStory::loadAllFromRows($data); 85 + protected function getPagingValue($item) { 86 + return $item->getChronologicalKey(); 111 87 } 112 88 113 89 }
+3 -4
src/applications/feed/controller/PhabricatorFeedPublicStreamController.php
··· 24 24 } 25 25 26 26 public function processRequest() { 27 - 28 27 if (!PhabricatorEnv::getEnvConfig('feed.public')) { 29 28 return new Aphront404Response(); 30 29 } 31 30 32 - // TODO: Profile images won't render correctly for logged-out users. 33 - 34 31 $request = $this->getRequest(); 32 + $viewer = $request->getUser(); 35 33 36 34 $query = new PhabricatorFeedQuery(); 35 + $query->setViewer($viewer); 37 36 $stories = $query->execute(); 38 37 39 38 $builder = new PhabricatorFeedBuilder($stories); 40 39 $builder 41 40 ->setFramed(true) 42 - ->setUser($request->getUser()); 41 + ->setUser($viewer); 43 42 44 43 $view = $builder->buildView(); 45 44
+16 -1
src/applications/feed/story/PhabricatorFeedStory.php
··· 23 23 * 24 24 * @task load Loading Stories 25 25 */ 26 - abstract class PhabricatorFeedStory { 26 + abstract class PhabricatorFeedStory implements PhabricatorPolicyInterface { 27 27 28 28 private $data; 29 29 private $hasViewed; ··· 74 74 return $stories; 75 75 } 76 76 77 + public function getCapabilities() { 78 + return array( 79 + PhabricatorPolicyCapability::CAN_VIEW, 80 + ); 81 + } 82 + 83 + public function getPolicy($capability) { 84 + return PhabricatorEnv::getEnvConfig('feed.public') 85 + ? PhabricatorPolicies::POLICY_PUBLIC 86 + : PhabricatorPolicies::POLICY_USER; 87 + } 88 + 89 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 90 + return false; 91 + } 77 92 78 93 public function setPrimaryObjectPHID($primary_object_phid) { 79 94 $this->primaryObjectPHID = $primary_object_phid;
+4 -1
src/applications/people/controller/PhabricatorPeopleProfileController.php
··· 204 204 } 205 205 206 206 private function renderUserFeed(PhabricatorUser $user) { 207 + $viewer = $this->getRequest()->getUser(); 208 + 207 209 $query = new PhabricatorFeedQuery(); 208 210 $query->setFilterPHIDs( 209 211 array( 210 212 $user->getPHID(), 211 213 )); 214 + $query->setViewer($viewer); 212 215 $stories = $query->execute(); 213 216 214 217 $builder = new PhabricatorFeedBuilder($stories); 215 - $builder->setUser($this->getRequest()->getUser()); 218 + $builder->setUser($viewer); 216 219 $view = $builder->buildView(); 217 220 218 221 return
+2 -7
src/applications/project/controller/PhabricatorProjectProfileController.php
··· 78 78 array( 79 79 $project->getPHID(), 80 80 )); 81 + $query->setViewer($this->getRequest()->getUser()); 81 82 $stories = $query->execute(); 82 83 83 84 $content .= $this->renderStories($stories); ··· 243 244 244 245 $query = new PhabricatorFeedQuery(); 245 246 $query->setFilterPHIDs(array($project->getPHID())); 247 + $query->setViewer($this->getRequest()->getUser()); 246 248 $stories = $query->execute(); 247 249 248 250 if (!$stories) { 249 251 return 'There are no stories about this project.'; 250 252 } 251 - 252 - $query = new PhabricatorFeedQuery(); 253 - $query->setFilterPHIDs( 254 - array( 255 - $project->getPHID(), 256 - )); 257 - $stories = $query->execute(); 258 253 259 254 return $this->renderStories($stories); 260 255 }
+4 -4
src/infrastructure/query/policy/PhabricatorIDPagedPolicyQuery.php
··· 65 65 if ($this->beforeID) { 66 66 return qsprintf( 67 67 $conn_r, 68 - '%C > %s', 68 + '%Q > %s', 69 69 $this->getPagingColumn(), 70 70 $this->beforeID); 71 71 } else if ($this->afterID) { 72 72 return qsprintf( 73 73 $conn_r, 74 - '%C < %s', 74 + '%Q < %s', 75 75 $this->getPagingColumn(), 76 76 $this->afterID); 77 77 } ··· 83 83 if ($this->beforeID) { 84 84 return qsprintf( 85 85 $conn_r, 86 - 'ORDER BY %C ASC', 86 + 'ORDER BY %Q ASC', 87 87 $this->getPagingColumn()); 88 88 } else { 89 89 return qsprintf( 90 90 $conn_r, 91 - 'ORDER BY %C DESC', 91 + 'ORDER BY %Q DESC', 92 92 $this->getPagingColumn()); 93 93 } 94 94 }