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

Use ApplicationSearch in Slowvote

Summary: Ref T2625. Ref T603. Make the vote list policy-aware, mobile-friendly, and use ApplicationSearch.

Test Plan: {F50022}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T603, T2625

Differential Revision: https://secure.phabricator.com/D6445

+180 -175
+8 -2
src/__phutil_library_map__.php
··· 1533 1533 'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/PhabricatorSlowvotePoll.php', 1534 1534 'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/PhabricatorSlowvotePollController.php', 1535 1535 'PhabricatorSlowvoteQuery' => 'applications/slowvote/query/PhabricatorSlowvoteQuery.php', 1536 + 'PhabricatorSlowvoteSearchEngine' => 'applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php', 1536 1537 'PhabricatorSlowvoteVoteController' => 'applications/slowvote/controller/PhabricatorSlowvoteVoteController.php', 1537 1538 'PhabricatorSlug' => 'infrastructure/util/PhabricatorSlug.php', 1538 1539 'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php', ··· 3482 3483 'PhabricatorSlowvoteController' => 'PhabricatorController', 3483 3484 'PhabricatorSlowvoteCreateController' => 'PhabricatorSlowvoteController', 3484 3485 'PhabricatorSlowvoteDAO' => 'PhabricatorLiskDAO', 3485 - 'PhabricatorSlowvoteListController' => 'PhabricatorSlowvoteController', 3486 + 'PhabricatorSlowvoteListController' => 3487 + array( 3488 + 0 => 'PhabricatorSlowvoteController', 3489 + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', 3490 + ), 3486 3491 'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO', 3487 3492 'PhabricatorSlowvotePoll' => 3488 3493 array( ··· 3490 3495 1 => 'PhabricatorPolicyInterface', 3491 3496 ), 3492 3497 'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController', 3493 - 'PhabricatorSlowvoteQuery' => 'PhabricatorPolicyAwareCursorPagedQuery', 3498 + 'PhabricatorSlowvoteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 3499 + 'PhabricatorSlowvoteSearchEngine' => 'PhabricatorApplicationSearchEngine', 3494 3500 'PhabricatorSlowvoteVoteController' => 'PhabricatorSlowvoteController', 3495 3501 'PhabricatorSlugTestCase' => 'PhabricatorTestCase', 3496 3502 'PhabricatorSortTableExample' => 'PhabricatorUIExample',
+2 -1
src/applications/slowvote/application/PhabricatorApplicationSlowvote.php
··· 40 40 return array( 41 41 '/V(?P<id>[1-9]\d*)' => 'PhabricatorSlowvotePollController', 42 42 '/vote/' => array( 43 - '(?:view/(?P<view>\w+)/)?' => 'PhabricatorSlowvoteListController', 43 + '(?:query/(?P<queryKey>[^/]+)/)?' 44 + => 'PhabricatorSlowvoteListController', 44 45 'create/' => 'PhabricatorSlowvoteCreateController', 45 46 '(?P<id>[1-9]\d*)/' => 'PhabricatorSlowvoteVoteController', 46 47 ),
+14 -40
src/applications/slowvote/controller/PhabricatorSlowvoteController.php
··· 5 5 */ 6 6 abstract class PhabricatorSlowvoteController extends PhabricatorController { 7 7 8 - const VIEW_ALL = 'all'; 9 - const VIEW_CREATED = 'created'; 10 - const VIEW_VOTED = 'voted'; 11 - 12 - public function buildStandardPageResponse($view, array $data) { 13 - $page = $this->buildStandardPageView(); 14 - 15 - $page->setApplicationName(pht('Slowvote')); 16 - $page->setBaseURI('/vote/'); 17 - $page->setTitle(idx($data, 'title')); 18 - $page->setGlyph("\xE2\x9C\x94"); 8 + public function buildSideNavView($for_app = false) { 9 + $user = $this->getRequest()->getUser(); 19 10 20 - $page->appendChild($view); 21 - 22 - $response = new AphrontWebpageResponse(); 23 - return $response->setContent($page->render()); 24 - } 25 - 26 - public function buildSideNavView($filter = null, $for_app = false) { 27 - 28 - $views = $this->getViews(); 29 - $side_nav = new AphrontSideNavFilterView(); 30 - $side_nav->setBaseURI(new PhutilURI('/vote/view/')); 31 - foreach ($views as $key => $name) { 32 - $side_nav->addFilter($key, $name); 33 - } 34 - if ($filter) { 35 - $side_nav->selectFilter($filter, null); 36 - } 11 + $nav = new AphrontSideNavFilterView(); 12 + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); 37 13 38 14 if ($for_app) { 39 - $side_nav->addFilter('', pht('Create Question'), 15 + $nav->addFilter('', pht('Create Poll'), 40 16 $this->getApplicationURI('create/')); 41 17 } 42 18 43 - return $side_nav; 19 + id(new PhabricatorSlowvoteSearchEngine()) 20 + ->setViewer($user) 21 + ->addNavigationItems($nav->getMenu()); 22 + 23 + $nav->selectFilter(null); 24 + 25 + return $nav; 44 26 } 45 27 46 28 public function buildApplicationMenu() { 47 - return $this->buildSideNavView(null, true)->getMenu(); 29 + return $this->buildSideNavView(true)->getMenu(); 48 30 } 49 31 50 32 public function buildApplicationCrumbs() { ··· 52 34 53 35 $crumbs->addAction( 54 36 id(new PHUIListItemView()) 55 - ->setName(pht('Create Question')) 37 + ->setName(pht('Create Poll')) 56 38 ->setHref($this->getApplicationURI('create/')) 57 39 ->setIcon('create')); 58 40 59 41 return $crumbs; 60 - } 61 - 62 - public function getViews() { 63 - return array( 64 - self::VIEW_ALL => pht('All Slowvotes'), 65 - self::VIEW_CREATED => pht('Created'), 66 - self::VIEW_VOTED => pht('Voted In'), 67 - ); 68 42 } 69 43 70 44 }
+37 -128
src/applications/slowvote/controller/PhabricatorSlowvoteListController.php
··· 4 4 * @group slowvote 5 5 */ 6 6 final class PhabricatorSlowvoteListController 7 - extends PhabricatorSlowvoteController { 7 + extends PhabricatorSlowvoteController 8 + implements PhabricatorApplicationSearchResultsControllerInterface { 8 9 9 - private $view; 10 + private $queryKey; 11 + 12 + public function shouldAllowPublic() { 13 + return true; 14 + } 10 15 11 16 public function willProcessRequest(array $data) { 12 - $this->view = idx($data, 'view', parent::VIEW_ALL); 17 + $this->queryKey = idx($data, 'queryKey'); 13 18 } 14 19 15 20 public function processRequest() { 16 - 17 21 $request = $this->getRequest(); 18 - $user = $request->getUser(); 19 - 20 - $view = $this->view; 21 - $views = $this->getViews(); 22 + $controller = id(new PhabricatorApplicationSearchController($request)) 23 + ->setQueryKey($this->queryKey) 24 + ->setSearchEngine(new PhabricatorSlowvoteSearchEngine()) 25 + ->setNavigation($this->buildSideNavView()); 22 26 23 - $side_nav = $this->buildSideNavView($view); 27 + return $this->delegateToController($controller); 28 + } 24 29 25 - $pager = new AphrontPagerView(); 26 - $pager->setOffset($request->getInt('page')); 27 - $pager->setURI($request->getRequestURI(), 'page'); 30 + public function renderResultsList( 31 + array $polls, 32 + PhabricatorSavedQuery $query) { 33 + assert_instances_of($polls, 'PhabricatorSlowvotePoll'); 34 + $viewer = $this->getRequest()->getUser(); 28 35 29 - $polls = $this->loadPolls($pager, $view); 36 + $list = id(new PhabricatorObjectItemListView()) 37 + ->setUser($viewer); 30 38 31 39 $phids = mpull($polls, 'getAuthorPHID'); 32 40 $handles = $this->loadViewerHandles($phids); 33 41 34 - $rows = array(); 35 42 foreach ($polls as $poll) { 36 - $rows[] = array( 37 - 'V'.$poll->getID(), 38 - phutil_tag( 39 - 'a', 40 - array( 41 - 'href' => '/V'.$poll->getID(), 42 - ), 43 - $poll->getQuestion()), 44 - $handles[$poll->getAuthorPHID()]->renderLink(), 45 - phabricator_date($poll->getDateCreated(), $user), 46 - phabricator_time($poll->getDateCreated(), $user), 47 - ); 48 - } 43 + $date_created = phabricator_datetime($poll->getDateCreated(), $viewer); 44 + if ($poll->getAuthorPHID()) { 45 + $author = $handles[$poll->getAuthorPHID()]->renderLink(); 46 + } else { 47 + $author = null; 48 + } 49 49 50 - $table = new AphrontTableView($rows); 51 - $table->setColumnClasses( 52 - array( 53 - '', 54 - 'pri wide', 55 - '', 56 - '', 57 - 'right', 58 - )); 59 - $table->setHeaders( 60 - array( 61 - pht('ID'), 62 - pht('Poll'), 63 - pht('Author'), 64 - pht('Date'), 65 - pht('Time'), 66 - )); 50 + $item = id(new PhabricatorObjectItemView()) 51 + ->setObjectName('V'.$poll->getID()) 52 + ->setHeader($poll->getQuestion()) 53 + ->setHref('/V'.$poll->getID()) 54 + ->addIcon('none', $date_created); 67 55 68 - switch ($view) { 69 - case self::VIEW_ALL: 70 - $table_header = 71 - pht('Slowvotes Not Yet Consumed by the Ravages of Time'); 72 - break; 73 - case self::VIEW_CREATED: 74 - $table_header = 75 - pht('Slowvotes Birthed from Your Noblest of Great Minds'); 76 - break; 77 - case self::VIEW_VOTED: 78 - $table_header = 79 - pht('Slowvotes Within Which You Express Your Mighty Opinion'); 80 - break; 81 - } 56 + if ($author) { 57 + $item->addByline(pht('Author: %s', $author)); 58 + } 82 59 83 - $panel = new AphrontPanelView(); 84 - $panel->setHeader($table_header); 85 - $panel->setNoBackground(); 86 - $panel->appendChild($table); 87 - $panel->appendChild($pager); 88 - 89 - $side_nav->appendChild($panel); 90 - 91 - $crumbs = $this->buildApplicationCrumbs($this->buildSideNavView()); 92 - $crumbs->addCrumb( 93 - id(new PhabricatorCrumbView()) 94 - ->setName($views[$view]) 95 - ->setHref($this->getApplicationURI())); 96 - $side_nav->setCrumbs($crumbs); 97 - 98 - return $this->buildApplicationPage( 99 - $side_nav, 100 - array( 101 - 'title' => pht('Slowvotes'), 102 - 'device' => true, 103 - )); 104 - } 105 - 106 - private function loadPolls(AphrontPagerView $pager, $view) { 107 - $request = $this->getRequest(); 108 - $user = $request->getUser(); 109 - 110 - $poll = new PhabricatorSlowvotePoll(); 111 - 112 - $conn = $poll->establishConnection('r'); 113 - $offset = $pager->getOffset(); 114 - $limit = $pager->getPageSize() + 1; 115 - 116 - switch ($view) { 117 - case self::VIEW_ALL: 118 - $data = queryfx_all( 119 - $conn, 120 - 'SELECT * FROM %T ORDER BY id DESC LIMIT %d, %d', 121 - $poll->getTableName(), 122 - $offset, 123 - $limit); 124 - break; 125 - case self::VIEW_CREATED: 126 - $data = queryfx_all( 127 - $conn, 128 - 'SELECT * FROM %T WHERE authorPHID = %s ORDER BY id DESC 129 - LIMIT %d, %d', 130 - $poll->getTableName(), 131 - $user->getPHID(), 132 - $offset, 133 - $limit); 134 - break; 135 - case self::VIEW_VOTED: 136 - $choice = new PhabricatorSlowvoteChoice(); 137 - $data = queryfx_all( 138 - $conn, 139 - 'SELECT p.* FROM %T p JOIN %T o 140 - ON o.pollID = p.id 141 - WHERE o.authorPHID = %s 142 - GROUP BY p.id 143 - ORDER BY p.id DESC 144 - LIMIT %d, %d', 145 - $poll->getTableName(), 146 - $choice->getTableName(), 147 - $user->getPHID(), 148 - $offset, 149 - $limit); 150 - break; 60 + $list->addItem($item); 151 61 } 152 62 153 - $data = $pager->sliceResults($data); 154 - return $poll->loadAllFromArray($data); 63 + return $list; 155 64 } 156 65 157 66 }
+29 -4
src/applications/slowvote/query/PhabricatorSlowvoteQuery.php
··· 9 9 private $ids; 10 10 private $phids; 11 11 private $authorPHIDs; 12 + private $withVotesByViewer; 12 13 13 14 public function withIDs($ids) { 14 15 $this->ids = $ids; ··· 25 26 return $this; 26 27 } 27 28 29 + public function withVotesByViewer($with_vote) { 30 + $this->withVotesByViewer = $with_vote; 31 + return $this; 32 + } 33 + 28 34 public function loadPage() { 29 35 $table = new PhabricatorSlowvotePoll(); 30 36 $conn_r = $table->establishConnection('r'); 31 37 32 38 $data = queryfx_all( 33 39 $conn_r, 34 - 'SELECT * FROM %T %Q %Q %Q', 40 + 'SELECT p.* FROM %T p %Q %Q %Q %Q', 35 41 $table->getTableName(), 42 + $this->buildJoinsClause($conn_r), 36 43 $this->buildWhereClause($conn_r), 37 44 $this->buildOrderClause($conn_r), 38 45 $this->buildLimitClause($conn_r)); ··· 46 53 if ($this->ids) { 47 54 $where[] = qsprintf( 48 55 $conn_r, 49 - 'id IN (%Ld)', 56 + 'p.id IN (%Ld)', 50 57 $this->ids); 51 58 } 52 59 53 60 if ($this->phids) { 54 61 $where[] = qsprintf( 55 62 $conn_r, 56 - 'phid IN (%Ls)', 63 + 'p.phid IN (%Ls)', 57 64 $this->phids); 58 65 } 59 66 60 67 if ($this->authorPHIDs) { 61 68 $where[] = qsprintf( 62 69 $conn_r, 63 - 'authorPHID IN (%Ls)', 70 + 'p.authorPHID IN (%Ls)', 64 71 $this->authorPHIDs); 65 72 } 66 73 67 74 $where[] = $this->buildPagingClause($conn_r); 68 75 return $this->formatWhereClause($where); 76 + } 77 + 78 + private function buildJoinsClause(AphrontDatabaseConnection $conn_r) { 79 + $joins = array(); 80 + 81 + if ($this->withVotesByViewer) { 82 + $joins[] = qsprintf( 83 + $conn_r, 84 + 'JOIN %T vv ON vv.pollID = p.id AND vv.authorPHID = %s', 85 + id(new PhabricatorSlowvoteChoice())->getTableName(), 86 + $this->getViewer()->getPHID()); 87 + } 88 + 89 + return implode(' ', $joins); 90 + } 91 + 92 + protected function getPagingColumn() { 93 + return 'p.id'; 69 94 } 70 95 71 96 }
+90
src/applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php
··· 1 + <?php 2 + 3 + final class PhabricatorSlowvoteSearchEngine 4 + extends PhabricatorApplicationSearchEngine { 5 + 6 + public function buildSavedQueryFromRequest(AphrontRequest $request) { 7 + $saved = new PhabricatorSavedQuery(); 8 + $saved->setParameter( 9 + 'authorPHIDs', 10 + array_values($request->getArr('authors'))); 11 + 12 + $saved->setParameter('voted', $request->getBool('voted')); 13 + 14 + return $saved; 15 + } 16 + 17 + public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 18 + $query = id(new PhabricatorSlowvoteQuery()) 19 + ->withAuthorPHIDs($saved->getParameter('authorPHIDs', array())); 20 + 21 + if ($saved->getParameter('voted')) { 22 + $query->withVotesByViewer(true); 23 + } 24 + 25 + return $query; 26 + } 27 + 28 + public function buildSearchForm( 29 + AphrontFormView $form, 30 + PhabricatorSavedQuery $saved_query) { 31 + $phids = $saved_query->getParameter('authorPHIDs', array()); 32 + $handles = id(new PhabricatorObjectHandleData($phids)) 33 + ->setViewer($this->requireViewer()) 34 + ->loadHandles(); 35 + $author_tokens = mpull($handles, 'getFullName', 'getPHID'); 36 + 37 + $voted = $saved_query->getParameter('voted', false); 38 + 39 + $form 40 + ->appendChild( 41 + id(new AphrontFormTokenizerControl()) 42 + ->setDatasource('/typeahead/common/users/') 43 + ->setName('authors') 44 + ->setLabel(pht('Authors')) 45 + ->setValue($author_tokens)) 46 + ->appendChild( 47 + id(new AphrontFormCheckboxControl()) 48 + ->addCheckbox( 49 + 'voted', 50 + 1, 51 + pht("Show only polls I've voted in."), 52 + $voted)); 53 + } 54 + 55 + protected function getURI($path) { 56 + return '/vote/'.$path; 57 + } 58 + 59 + public function getBuiltinQueryNames() { 60 + $names = array( 61 + 'all' => pht('All Polls'), 62 + ); 63 + 64 + if ($this->requireViewer()->isLoggedIn()) { 65 + $names['authored'] = pht('Authored'); 66 + $names['voted'] = pht('Voted In'); 67 + } 68 + 69 + return $names; 70 + } 71 + 72 + public function buildSavedQueryFromBuiltin($query_key) { 73 + $query = $this->newSavedQuery(); 74 + $query->setQueryKey($query_key); 75 + 76 + switch ($query_key) { 77 + case 'all': 78 + return $query; 79 + case 'authored': 80 + return $query->setParameter( 81 + 'authorPHIDs', 82 + array($this->requireViewer()->getPHID())); 83 + case 'voted': 84 + return $query->setParameter('voted', true); 85 + } 86 + 87 + return parent::buildSavedQueryFromBuiltin($query_key); 88 + } 89 + 90 + }