mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-20 20:40:56 +01:00
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
This commit is contained in:
parent
9be755ab12
commit
0630ffffaa
6 changed files with 183 additions and 178 deletions
|
@ -1533,6 +1533,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSlowvotePoll' => 'applications/slowvote/storage/PhabricatorSlowvotePoll.php',
|
||||
'PhabricatorSlowvotePollController' => 'applications/slowvote/controller/PhabricatorSlowvotePollController.php',
|
||||
'PhabricatorSlowvoteQuery' => 'applications/slowvote/query/PhabricatorSlowvoteQuery.php',
|
||||
'PhabricatorSlowvoteSearchEngine' => 'applications/slowvote/query/PhabricatorSlowvoteSearchEngine.php',
|
||||
'PhabricatorSlowvoteVoteController' => 'applications/slowvote/controller/PhabricatorSlowvoteVoteController.php',
|
||||
'PhabricatorSlug' => 'infrastructure/util/PhabricatorSlug.php',
|
||||
'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php',
|
||||
|
@ -3482,7 +3483,11 @@ phutil_register_library_map(array(
|
|||
'PhabricatorSlowvoteController' => 'PhabricatorController',
|
||||
'PhabricatorSlowvoteCreateController' => 'PhabricatorSlowvoteController',
|
||||
'PhabricatorSlowvoteDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorSlowvoteListController' => 'PhabricatorSlowvoteController',
|
||||
'PhabricatorSlowvoteListController' =>
|
||||
array(
|
||||
0 => 'PhabricatorSlowvoteController',
|
||||
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
|
||||
),
|
||||
'PhabricatorSlowvoteOption' => 'PhabricatorSlowvoteDAO',
|
||||
'PhabricatorSlowvotePoll' =>
|
||||
array(
|
||||
|
@ -3490,7 +3495,8 @@ phutil_register_library_map(array(
|
|||
1 => 'PhabricatorPolicyInterface',
|
||||
),
|
||||
'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController',
|
||||
'PhabricatorSlowvoteQuery' => 'PhabricatorPolicyAwareCursorPagedQuery',
|
||||
'PhabricatorSlowvoteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorSlowvoteSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorSlowvoteVoteController' => 'PhabricatorSlowvoteController',
|
||||
'PhabricatorSlugTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorSortTableExample' => 'PhabricatorUIExample',
|
||||
|
|
|
@ -40,7 +40,8 @@ final class PhabricatorApplicationSlowvote extends PhabricatorApplication {
|
|||
return array(
|
||||
'/V(?P<id>[1-9]\d*)' => 'PhabricatorSlowvotePollController',
|
||||
'/vote/' => array(
|
||||
'(?:view/(?P<view>\w+)/)?' => 'PhabricatorSlowvoteListController',
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?'
|
||||
=> 'PhabricatorSlowvoteListController',
|
||||
'create/' => 'PhabricatorSlowvoteCreateController',
|
||||
'(?P<id>[1-9]\d*)/' => 'PhabricatorSlowvoteVoteController',
|
||||
),
|
||||
|
|
|
@ -5,46 +5,28 @@
|
|||
*/
|
||||
abstract class PhabricatorSlowvoteController extends PhabricatorController {
|
||||
|
||||
const VIEW_ALL = 'all';
|
||||
const VIEW_CREATED = 'created';
|
||||
const VIEW_VOTED = 'voted';
|
||||
public function buildSideNavView($for_app = false) {
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
public function buildStandardPageResponse($view, array $data) {
|
||||
$page = $this->buildStandardPageView();
|
||||
|
||||
$page->setApplicationName(pht('Slowvote'));
|
||||
$page->setBaseURI('/vote/');
|
||||
$page->setTitle(idx($data, 'title'));
|
||||
$page->setGlyph("\xE2\x9C\x94");
|
||||
|
||||
$page->appendChild($view);
|
||||
|
||||
$response = new AphrontWebpageResponse();
|
||||
return $response->setContent($page->render());
|
||||
}
|
||||
|
||||
public function buildSideNavView($filter = null, $for_app = false) {
|
||||
|
||||
$views = $this->getViews();
|
||||
$side_nav = new AphrontSideNavFilterView();
|
||||
$side_nav->setBaseURI(new PhutilURI('/vote/view/'));
|
||||
foreach ($views as $key => $name) {
|
||||
$side_nav->addFilter($key, $name);
|
||||
}
|
||||
if ($filter) {
|
||||
$side_nav->selectFilter($filter, null);
|
||||
}
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
if ($for_app) {
|
||||
$side_nav->addFilter('', pht('Create Question'),
|
||||
$nav->addFilter('', pht('Create Poll'),
|
||||
$this->getApplicationURI('create/'));
|
||||
}
|
||||
|
||||
return $side_nav;
|
||||
id(new PhabricatorSlowvoteSearchEngine())
|
||||
->setViewer($user)
|
||||
->addNavigationItems($nav->getMenu());
|
||||
|
||||
$nav->selectFilter(null);
|
||||
|
||||
return $nav;
|
||||
}
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->buildSideNavView(null, true)->getMenu();
|
||||
return $this->buildSideNavView(true)->getMenu();
|
||||
}
|
||||
|
||||
public function buildApplicationCrumbs() {
|
||||
|
@ -52,19 +34,11 @@ abstract class PhabricatorSlowvoteController extends PhabricatorController {
|
|||
|
||||
$crumbs->addAction(
|
||||
id(new PHUIListItemView())
|
||||
->setName(pht('Create Question'))
|
||||
->setName(pht('Create Poll'))
|
||||
->setHref($this->getApplicationURI('create/'))
|
||||
->setIcon('create'));
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
public function getViews() {
|
||||
return array(
|
||||
self::VIEW_ALL => pht('All Slowvotes'),
|
||||
self::VIEW_CREATED => pht('Created'),
|
||||
self::VIEW_VOTED => pht('Voted In'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,154 +4,63 @@
|
|||
* @group slowvote
|
||||
*/
|
||||
final class PhabricatorSlowvoteListController
|
||||
extends PhabricatorSlowvoteController {
|
||||
extends PhabricatorSlowvoteController
|
||||
implements PhabricatorApplicationSearchResultsControllerInterface {
|
||||
|
||||
private $view;
|
||||
private $queryKey;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->view = idx($data, 'view', parent::VIEW_ALL);
|
||||
$this->queryKey = idx($data, 'queryKey');
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$controller = id(new PhabricatorApplicationSearchController($request))
|
||||
->setQueryKey($this->queryKey)
|
||||
->setSearchEngine(new PhabricatorSlowvoteSearchEngine())
|
||||
->setNavigation($this->buildSideNavView());
|
||||
|
||||
$view = $this->view;
|
||||
$views = $this->getViews();
|
||||
return $this->delegateToController($controller);
|
||||
}
|
||||
|
||||
$side_nav = $this->buildSideNavView($view);
|
||||
public function renderResultsList(
|
||||
array $polls,
|
||||
PhabricatorSavedQuery $query) {
|
||||
assert_instances_of($polls, 'PhabricatorSlowvotePoll');
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
|
||||
$pager = new AphrontPagerView();
|
||||
$pager->setOffset($request->getInt('page'));
|
||||
$pager->setURI($request->getRequestURI(), 'page');
|
||||
|
||||
$polls = $this->loadPolls($pager, $view);
|
||||
$list = id(new PhabricatorObjectItemListView())
|
||||
->setUser($viewer);
|
||||
|
||||
$phids = mpull($polls, 'getAuthorPHID');
|
||||
$handles = $this->loadViewerHandles($phids);
|
||||
|
||||
$rows = array();
|
||||
foreach ($polls as $poll) {
|
||||
$rows[] = array(
|
||||
'V'.$poll->getID(),
|
||||
phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/V'.$poll->getID(),
|
||||
),
|
||||
$poll->getQuestion()),
|
||||
$handles[$poll->getAuthorPHID()]->renderLink(),
|
||||
phabricator_date($poll->getDateCreated(), $user),
|
||||
phabricator_time($poll->getDateCreated(), $user),
|
||||
);
|
||||
$date_created = phabricator_datetime($poll->getDateCreated(), $viewer);
|
||||
if ($poll->getAuthorPHID()) {
|
||||
$author = $handles[$poll->getAuthorPHID()]->renderLink();
|
||||
} else {
|
||||
$author = null;
|
||||
}
|
||||
|
||||
$item = id(new PhabricatorObjectItemView())
|
||||
->setObjectName('V'.$poll->getID())
|
||||
->setHeader($poll->getQuestion())
|
||||
->setHref('/V'.$poll->getID())
|
||||
->addIcon('none', $date_created);
|
||||
|
||||
if ($author) {
|
||||
$item->addByline(pht('Author: %s', $author));
|
||||
}
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
$table = new AphrontTableView($rows);
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'',
|
||||
'pri wide',
|
||||
'',
|
||||
'',
|
||||
'right',
|
||||
));
|
||||
$table->setHeaders(
|
||||
array(
|
||||
pht('ID'),
|
||||
pht('Poll'),
|
||||
pht('Author'),
|
||||
pht('Date'),
|
||||
pht('Time'),
|
||||
));
|
||||
|
||||
switch ($view) {
|
||||
case self::VIEW_ALL:
|
||||
$table_header =
|
||||
pht('Slowvotes Not Yet Consumed by the Ravages of Time');
|
||||
break;
|
||||
case self::VIEW_CREATED:
|
||||
$table_header =
|
||||
pht('Slowvotes Birthed from Your Noblest of Great Minds');
|
||||
break;
|
||||
case self::VIEW_VOTED:
|
||||
$table_header =
|
||||
pht('Slowvotes Within Which You Express Your Mighty Opinion');
|
||||
break;
|
||||
}
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
$panel->setHeader($table_header);
|
||||
$panel->setNoBackground();
|
||||
$panel->appendChild($table);
|
||||
$panel->appendChild($pager);
|
||||
|
||||
$side_nav->appendChild($panel);
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView());
|
||||
$crumbs->addCrumb(
|
||||
id(new PhabricatorCrumbView())
|
||||
->setName($views[$view])
|
||||
->setHref($this->getApplicationURI()));
|
||||
$side_nav->setCrumbs($crumbs);
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
$side_nav,
|
||||
array(
|
||||
'title' => pht('Slowvotes'),
|
||||
'device' => true,
|
||||
));
|
||||
}
|
||||
|
||||
private function loadPolls(AphrontPagerView $pager, $view) {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$poll = new PhabricatorSlowvotePoll();
|
||||
|
||||
$conn = $poll->establishConnection('r');
|
||||
$offset = $pager->getOffset();
|
||||
$limit = $pager->getPageSize() + 1;
|
||||
|
||||
switch ($view) {
|
||||
case self::VIEW_ALL:
|
||||
$data = queryfx_all(
|
||||
$conn,
|
||||
'SELECT * FROM %T ORDER BY id DESC LIMIT %d, %d',
|
||||
$poll->getTableName(),
|
||||
$offset,
|
||||
$limit);
|
||||
break;
|
||||
case self::VIEW_CREATED:
|
||||
$data = queryfx_all(
|
||||
$conn,
|
||||
'SELECT * FROM %T WHERE authorPHID = %s ORDER BY id DESC
|
||||
LIMIT %d, %d',
|
||||
$poll->getTableName(),
|
||||
$user->getPHID(),
|
||||
$offset,
|
||||
$limit);
|
||||
break;
|
||||
case self::VIEW_VOTED:
|
||||
$choice = new PhabricatorSlowvoteChoice();
|
||||
$data = queryfx_all(
|
||||
$conn,
|
||||
'SELECT p.* FROM %T p JOIN %T o
|
||||
ON o.pollID = p.id
|
||||
WHERE o.authorPHID = %s
|
||||
GROUP BY p.id
|
||||
ORDER BY p.id DESC
|
||||
LIMIT %d, %d',
|
||||
$poll->getTableName(),
|
||||
$choice->getTableName(),
|
||||
$user->getPHID(),
|
||||
$offset,
|
||||
$limit);
|
||||
break;
|
||||
}
|
||||
|
||||
$data = $pager->sliceResults($data);
|
||||
return $poll->loadAllFromArray($data);
|
||||
return $list;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ final class PhabricatorSlowvoteQuery
|
|||
private $ids;
|
||||
private $phids;
|
||||
private $authorPHIDs;
|
||||
private $withVotesByViewer;
|
||||
|
||||
public function withIDs($ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -25,14 +26,20 @@ final class PhabricatorSlowvoteQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withVotesByViewer($with_vote) {
|
||||
$this->withVotesByViewer = $with_vote;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function loadPage() {
|
||||
$table = new PhabricatorSlowvotePoll();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T %Q %Q %Q',
|
||||
'SELECT p.* FROM %T p %Q %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildJoinsClause($conn_r),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
@ -46,21 +53,21 @@ final class PhabricatorSlowvoteQuery
|
|||
if ($this->ids) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'id IN (%Ld)',
|
||||
'p.id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'phid IN (%Ls)',
|
||||
'p.phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->authorPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'authorPHID IN (%Ls)',
|
||||
'p.authorPHID IN (%Ls)',
|
||||
$this->authorPHIDs);
|
||||
}
|
||||
|
||||
|
@ -68,4 +75,22 @@ final class PhabricatorSlowvoteQuery
|
|||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function buildJoinsClause(AphrontDatabaseConnection $conn_r) {
|
||||
$joins = array();
|
||||
|
||||
if ($this->withVotesByViewer) {
|
||||
$joins[] = qsprintf(
|
||||
$conn_r,
|
||||
'JOIN %T vv ON vv.pollID = p.id AND vv.authorPHID = %s',
|
||||
id(new PhabricatorSlowvoteChoice())->getTableName(),
|
||||
$this->getViewer()->getPHID());
|
||||
}
|
||||
|
||||
return implode(' ', $joins);
|
||||
}
|
||||
|
||||
protected function getPagingColumn() {
|
||||
return 'p.id';
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorSlowvoteSearchEngine
|
||||
extends PhabricatorApplicationSearchEngine {
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
$saved->setParameter(
|
||||
'authorPHIDs',
|
||||
array_values($request->getArr('authors')));
|
||||
|
||||
$saved->setParameter('voted', $request->getBool('voted'));
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$query = id(new PhabricatorSlowvoteQuery())
|
||||
->withAuthorPHIDs($saved->getParameter('authorPHIDs', array()));
|
||||
|
||||
if ($saved->getParameter('voted')) {
|
||||
$query->withVotesByViewer(true);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved_query) {
|
||||
$phids = $saved_query->getParameter('authorPHIDs', array());
|
||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||
->setViewer($this->requireViewer())
|
||||
->loadHandles();
|
||||
$author_tokens = mpull($handles, 'getFullName', 'getPHID');
|
||||
|
||||
$voted = $saved_query->getParameter('voted', false);
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource('/typeahead/common/users/')
|
||||
->setName('authors')
|
||||
->setLabel(pht('Authors'))
|
||||
->setValue($author_tokens))
|
||||
->appendChild(
|
||||
id(new AphrontFormCheckboxControl())
|
||||
->addCheckbox(
|
||||
'voted',
|
||||
1,
|
||||
pht("Show only polls I've voted in."),
|
||||
$voted));
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
return '/vote/'.$path;
|
||||
}
|
||||
|
||||
public function getBuiltinQueryNames() {
|
||||
$names = array(
|
||||
'all' => pht('All Polls'),
|
||||
);
|
||||
|
||||
if ($this->requireViewer()->isLoggedIn()) {
|
||||
$names['authored'] = pht('Authored');
|
||||
$names['voted'] = pht('Voted In');
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromBuiltin($query_key) {
|
||||
$query = $this->newSavedQuery();
|
||||
$query->setQueryKey($query_key);
|
||||
|
||||
switch ($query_key) {
|
||||
case 'all':
|
||||
return $query;
|
||||
case 'authored':
|
||||
return $query->setParameter(
|
||||
'authorPHIDs',
|
||||
array($this->requireViewer()->getPHID()));
|
||||
case 'voted':
|
||||
return $query->setParameter('voted', true);
|
||||
}
|
||||
|
||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in a new issue