mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-11 17:32:41 +01:00
3a3ab08aba
Summary: Revisions you should review usually require faster response than revisions you should update or commit. Test Plan: / /differential/ Reviewers: epriestley Reviewed By: epriestley CC: aran, Korvin Differential Revision: https://secure.phabricator.com/D4606
517 lines
15 KiB
PHP
517 lines
15 KiB
PHP
<?php
|
|
|
|
final class DifferentialRevisionListController extends DifferentialController {
|
|
|
|
private $filter;
|
|
private $username;
|
|
|
|
public function shouldRequireLogin() {
|
|
return !$this->allowsAnonymousAccess();
|
|
}
|
|
|
|
public function willProcessRequest(array $data) {
|
|
$this->filter = idx($data, 'filter');
|
|
$this->username = idx($data, 'username');
|
|
}
|
|
|
|
public function processRequest() {
|
|
$request = $this->getRequest();
|
|
$user = $request->getUser();
|
|
$viewer_is_anonymous = !$user->isLoggedIn();
|
|
|
|
$params = array_filter(
|
|
array(
|
|
'status' => $request->getStr('status'),
|
|
'order' => $request->getStr('order'),
|
|
));
|
|
$params['participants'] = $request->getArr('participants');
|
|
|
|
$default_filter = ($viewer_is_anonymous ? 'all' : 'active');
|
|
$filters = $this->getFilters();
|
|
$this->filter = $this->selectFilter(
|
|
$filters,
|
|
$this->filter,
|
|
$default_filter);
|
|
|
|
// Redirect from search to canonical URL.
|
|
$phid_arr = $request->getArr('view_users');
|
|
if ($phid_arr) {
|
|
$view_users = id(new PhabricatorUser())
|
|
->loadAllWhere('phid IN (%Ls)', $phid_arr);
|
|
|
|
if (count($view_users) == 1) {
|
|
// This is a single user, so generate a pretty URI.
|
|
$uri = new PhutilURI(
|
|
'/differential/filter/'.$this->filter.'/'.
|
|
phutil_escape_uri(reset($view_users)->getUserName()).'/');
|
|
$uri->setQueryParams($params);
|
|
|
|
return id(new AphrontRedirectResponse())->setURI($uri);
|
|
}
|
|
|
|
}
|
|
|
|
$uri = new PhutilURI('/differential/filter/'.$this->filter.'/');
|
|
$uri->setQueryParams($params);
|
|
|
|
$username = '';
|
|
if ($this->username) {
|
|
$view_user = id(new PhabricatorUser())
|
|
->loadOneWhere('userName = %s', $this->username);
|
|
if (!$view_user) {
|
|
return new Aphront404Response();
|
|
}
|
|
$username = phutil_escape_uri($this->username).'/';
|
|
$uri->setPath('/differential/filter/'.$this->filter.'/'.$username);
|
|
$params['view_users'] = array($view_user->getPHID());
|
|
} else {
|
|
$phids = $request->getArr('view_users');
|
|
if ($phids) {
|
|
$params['view_users'] = $phids;
|
|
$uri->setQueryParams($params);
|
|
}
|
|
}
|
|
|
|
// Fill in the defaults we'll actually use for calculations if any
|
|
// parameters are missing.
|
|
$params += array(
|
|
'view_users' => array($user->getPHID()),
|
|
'status' => 'all',
|
|
'order' => 'modified',
|
|
);
|
|
|
|
$side_nav = new AphrontSideNavFilterView();
|
|
$side_nav->setBaseURI(id(clone $uri)->setPath('/differential/filter/'));
|
|
foreach ($filters as $filter) {
|
|
list($filter_name, $display_name) = $filter;
|
|
if ($filter_name) {
|
|
$side_nav->addFilter($filter_name.'/'.$username, $display_name);
|
|
} else {
|
|
$side_nav->addLabel($display_name);
|
|
}
|
|
}
|
|
$side_nav->selectFilter($this->filter.'/'.$username, null);
|
|
|
|
$panels = array();
|
|
$handles = array();
|
|
$controls = $this->getFilterControls($this->filter);
|
|
if ($this->getFilterRequiresUser($this->filter) && !$params['view_users']) {
|
|
// In the anonymous case, we still want to let you see some user's
|
|
// list, but we don't have a default PHID to provide (normally, we use
|
|
// the viewing user's). Show a warning instead.
|
|
$warning = new AphrontErrorView();
|
|
$warning->setSeverity(AphrontErrorView::SEVERITY_WARNING);
|
|
$warning->setTitle(pht('User Required'));
|
|
$warning->appendChild(
|
|
pht('This filter requires that a user be specified above.'));
|
|
$panels[] = $warning;
|
|
} else {
|
|
$query = $this->buildQuery($this->filter, $params);
|
|
|
|
$pager = null;
|
|
if ($this->getFilterAllowsPaging($this->filter)) {
|
|
$pager = new AphrontPagerView();
|
|
$pager->setOffset($request->getInt('page'));
|
|
$pager->setPageSize(1000);
|
|
$pager->setURI($uri, 'page');
|
|
|
|
$query->setOffset($pager->getOffset());
|
|
$query->setLimit($pager->getPageSize() + 1);
|
|
}
|
|
|
|
foreach ($controls as $control) {
|
|
$this->applyControlToQuery($control, $query, $params);
|
|
}
|
|
|
|
$revisions = $query->execute();
|
|
|
|
if ($pager) {
|
|
$revisions = $pager->sliceResults($revisions);
|
|
}
|
|
|
|
$views = $this->buildViews(
|
|
$this->filter,
|
|
$params['view_users'],
|
|
$revisions);
|
|
|
|
$view_objects = array();
|
|
foreach ($views as $view) {
|
|
if (empty($view['special'])) {
|
|
$view_objects[] = $view['view'];
|
|
}
|
|
}
|
|
$phids = mpull($view_objects, 'getRequiredHandlePHIDs');
|
|
$phids[] = $params['view_users'];
|
|
$phids = array_mergev($phids);
|
|
$handles = $this->loadViewerHandles($phids);
|
|
|
|
foreach ($views as $view) {
|
|
if (empty($view['special'])) {
|
|
$view['view']->setHandles($handles);
|
|
}
|
|
$panel = new AphrontPanelView();
|
|
$panel->setHeader($view['title']);
|
|
$panel->appendChild($view['view']);
|
|
if ($pager) {
|
|
$panel->appendChild($pager);
|
|
}
|
|
$panel->setNoBackground();
|
|
$panels[] = $panel;
|
|
}
|
|
}
|
|
|
|
$filter_form = id(new AphrontFormView())
|
|
->setMethod('GET')
|
|
->setAction('/differential/filter/'.$this->filter.'/')
|
|
->setUser($user);
|
|
foreach ($controls as $control) {
|
|
$control_view = $this->renderControl($control, $handles, $uri, $params);
|
|
$filter_form->appendChild($control_view);
|
|
}
|
|
$filter_form
|
|
->addHiddenInput('status', $params['status'])
|
|
->addHiddenInput('order', $params['order'])
|
|
->appendChild(
|
|
id(new AphrontFormSubmitControl())
|
|
->setValue(pht('Filter Revisions')));
|
|
|
|
$filter_view = new AphrontListFilterView();
|
|
$filter_view->appendChild($filter_form);
|
|
|
|
$side_nav->appendChild($filter_view);
|
|
|
|
foreach ($panels as $panel) {
|
|
$side_nav->appendChild($panel);
|
|
}
|
|
|
|
$crumbs = $this->buildApplicationCrumbs();
|
|
$name = $side_nav
|
|
->getMenu()
|
|
->getItem($side_nav->getSelectedFilter())
|
|
->getName();
|
|
$crumbs->addCrumb(
|
|
id(new PhabricatorCrumbView())
|
|
->setName($name)
|
|
->setHref($request->getRequestURI()));
|
|
$side_nav->setCrumbs($crumbs);
|
|
|
|
return $this->buildApplicationPage(
|
|
$side_nav,
|
|
array(
|
|
'title' => pht('Differential Home'),
|
|
));
|
|
}
|
|
|
|
private function getFilters() {
|
|
return array(
|
|
array(null, pht('User Revisions')),
|
|
array('active', pht('Active')),
|
|
array('revisions', pht('Revisions')),
|
|
array('reviews', pht('Reviews')),
|
|
array('subscribed', pht('Subscribed')),
|
|
array('drafts', pht('Draft Reviews')),
|
|
array(null, pht('All Revisions')),
|
|
array('all', pht('All')),
|
|
);
|
|
}
|
|
|
|
private function selectFilter(
|
|
array $filters,
|
|
$requested_filter,
|
|
$default_filter) {
|
|
|
|
// If the user requested a filter, make sure it actually exists.
|
|
if ($requested_filter) {
|
|
foreach ($filters as $filter) {
|
|
if ($filter[0] === $requested_filter) {
|
|
return $requested_filter;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If not, return the default filter.
|
|
return $default_filter;
|
|
}
|
|
|
|
private function getFilterRequiresUser($filter) {
|
|
static $requires = array(
|
|
'active' => true,
|
|
'revisions' => true,
|
|
'reviews' => true,
|
|
'subscribed' => true,
|
|
'drafts' => true,
|
|
'all' => false,
|
|
);
|
|
if (!isset($requires[$filter])) {
|
|
throw new Exception("Unknown filter '{$filter}'!");
|
|
}
|
|
return $requires[$filter];
|
|
}
|
|
|
|
private function getFilterAllowsPaging($filter) {
|
|
static $allows = array(
|
|
'active' => false,
|
|
'revisions' => true,
|
|
'reviews' => true,
|
|
'subscribed' => true,
|
|
'drafts' => true,
|
|
'all' => true,
|
|
);
|
|
if (!isset($allows[$filter])) {
|
|
throw new Exception("Unknown filter '{$filter}'!");
|
|
}
|
|
return $allows[$filter];
|
|
}
|
|
|
|
private function getFilterControls($filter) {
|
|
static $controls = array(
|
|
'active' => array('phid'),
|
|
'revisions' => array('phid', 'participants', 'status', 'order'),
|
|
'reviews' => array('phid', 'participants', 'status', 'order'),
|
|
'subscribed' => array('subscriber', 'status', 'order'),
|
|
'drafts' => array('phid', 'status', 'order'),
|
|
'all' => array('status', 'order'),
|
|
);
|
|
if (!isset($controls[$filter])) {
|
|
throw new Exception("Unknown filter '{$filter}'!");
|
|
}
|
|
return $controls[$filter];
|
|
}
|
|
|
|
private function buildQuery($filter, array $params) {
|
|
$user_phids = $params['view_users'];
|
|
$query = new DifferentialRevisionQuery();
|
|
|
|
$query->needRelationships(true);
|
|
|
|
switch ($filter) {
|
|
case 'active':
|
|
$query->withResponsibleUsers($user_phids);
|
|
$query->withStatus(DifferentialRevisionQuery::STATUS_OPEN);
|
|
$query->setLimit(null);
|
|
break;
|
|
case 'revisions':
|
|
$query->withAuthors($user_phids);
|
|
$query->withReviewers($params['participants']);
|
|
break;
|
|
case 'reviews':
|
|
$query->withReviewers($user_phids);
|
|
$query->withAuthors($params['participants']);
|
|
break;
|
|
case 'subscribed':
|
|
$query->withSubscribers($user_phids);
|
|
break;
|
|
case 'drafts':
|
|
$query->withDraftRepliesByAuthors($user_phids);
|
|
break;
|
|
case 'all':
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown filter '{$filter}'!");
|
|
}
|
|
return $query;
|
|
}
|
|
|
|
private function renderControl(
|
|
$control,
|
|
array $handles,
|
|
PhutilURI $uri,
|
|
array $params) {
|
|
assert_instances_of($handles, 'PhabricatorObjectHandle');
|
|
|
|
switch ($control) {
|
|
case 'subscriber':
|
|
case 'phid':
|
|
$value = mpull(
|
|
array_select_keys($handles, $params['view_users']),
|
|
'getFullName');
|
|
|
|
if ($control == 'subscriber') {
|
|
$source = '/typeahead/common/allmailable/';
|
|
$label = pht('View Subscribers');
|
|
} else {
|
|
$source = '/typeahead/common/accounts/';
|
|
switch ($this->filter) {
|
|
case 'revisions':
|
|
$label = pht('Authors');
|
|
break;
|
|
case 'reviews':
|
|
$label = pht('Reviewers');
|
|
break;
|
|
default:
|
|
$label = pht('View Users');
|
|
break;
|
|
}
|
|
}
|
|
|
|
return id(new AphrontFormTokenizerControl())
|
|
->setDatasource($source)
|
|
->setLabel($label)
|
|
->setName('view_users')
|
|
->setValue($value);
|
|
|
|
case 'participants':
|
|
switch ($this->filter) {
|
|
case 'revisions':
|
|
$label = pht('Reviewers');
|
|
break;
|
|
case 'reviews':
|
|
$label = pht('Authors');
|
|
break;
|
|
}
|
|
$value = mpull(
|
|
array_select_keys($handles, $params['participants']),
|
|
'getFullName');
|
|
return id(new AphrontFormTokenizerControl())
|
|
->setDatasource('/typeahead/common/allmailable/')
|
|
->setLabel($label)
|
|
->setName('participants')
|
|
->setValue($value);
|
|
|
|
case 'status':
|
|
return id(new AphrontFormToggleButtonsControl())
|
|
->setLabel(pht('Status'))
|
|
->setValue($params['status'])
|
|
->setBaseURI($uri, 'status')
|
|
->setButtons(
|
|
array(
|
|
'all' => pht('All'),
|
|
'open' => pht('Open'),
|
|
'closed' => pht('Closed'),
|
|
'abandoned' => pht('Abandoned'),
|
|
));
|
|
|
|
case 'order':
|
|
return id(new AphrontFormToggleButtonsControl())
|
|
->setLabel(pht('Order'))
|
|
->setValue($params['order'])
|
|
->setBaseURI($uri, 'order')
|
|
->setButtons(
|
|
array(
|
|
'modified' => pht('Updated'),
|
|
'created' => pht('Created'),
|
|
));
|
|
|
|
default:
|
|
throw new Exception("Unknown control '{$control}'!");
|
|
}
|
|
}
|
|
|
|
private function applyControlToQuery($control, $query, array $params) {
|
|
switch ($control) {
|
|
case 'phid':
|
|
case 'subscriber':
|
|
case 'participants':
|
|
// Already applied by query construction.
|
|
break;
|
|
case 'status':
|
|
if ($params['status'] == 'open') {
|
|
$query->withStatus(DifferentialRevisionQuery::STATUS_OPEN);
|
|
} else if ($params['status'] == 'closed') {
|
|
$query->withStatus(DifferentialRevisionQuery::STATUS_CLOSED);
|
|
} else if ($params['status'] == 'abandoned') {
|
|
$query->withStatus(DifferentialRevisionQuery::STATUS_ABANDONED);
|
|
}
|
|
break;
|
|
case 'order':
|
|
if ($params['order'] == 'created') {
|
|
$query->setOrder(DifferentialRevisionQuery::ORDER_CREATED);
|
|
}
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown control '{$control}'!");
|
|
}
|
|
}
|
|
|
|
private function buildViews($filter, array $user_phids, array $revisions) {
|
|
assert_instances_of($revisions, 'DifferentialRevision');
|
|
|
|
$user = $this->getRequest()->getUser();
|
|
|
|
$template = id(new DifferentialRevisionListView())
|
|
->setUser($user)
|
|
->setFields(DifferentialRevisionListView::getDefaultFields());
|
|
|
|
$views = array();
|
|
switch ($filter) {
|
|
case 'active':
|
|
list($blocking, $active, $waiting) =
|
|
DifferentialRevisionQuery::splitResponsible(
|
|
$revisions,
|
|
$user_phids);
|
|
|
|
$view = id(clone $template)
|
|
->setHighlightAge(true)
|
|
->setRevisions($blocking)
|
|
->loadAssets();
|
|
$views[] = array(
|
|
'title' => pht('Blocking Others'),
|
|
'view' => $view,
|
|
);
|
|
|
|
$view = id(clone $template)
|
|
->setHighlightAge(true)
|
|
->setRevisions($active)
|
|
->loadAssets();
|
|
$views[] = array(
|
|
'title' => pht('Action Required'),
|
|
'view' => $view,
|
|
);
|
|
|
|
// Flags are sort of private, so only show the flag panel if you're
|
|
// looking at your own requests.
|
|
if (in_array($user->getPHID(), $user_phids)) {
|
|
$flags = id(new PhabricatorFlagQuery())
|
|
->withOwnerPHIDs(array($user->getPHID()))
|
|
->withTypes(array(PhabricatorPHIDConstants::PHID_TYPE_DREV))
|
|
->needHandles(true)
|
|
->execute();
|
|
|
|
if ($flags) {
|
|
$view = id(new PhabricatorFlagListView())
|
|
->setFlags($flags)
|
|
->setUser($user);
|
|
|
|
$views[] = array(
|
|
'title' => pht('Flagged Revisions'),
|
|
'view' => $view,
|
|
'special' => true,
|
|
);
|
|
}
|
|
}
|
|
|
|
$view = id(clone $template)
|
|
->setRevisions($waiting)
|
|
->loadAssets();
|
|
$views[] = array(
|
|
'title' => pht('Waiting On Others'),
|
|
'view' => $view,
|
|
);
|
|
break;
|
|
case 'revisions':
|
|
case 'reviews':
|
|
case 'subscribed':
|
|
case 'drafts':
|
|
case 'all':
|
|
$titles = array(
|
|
'revisions' => pht('Revisions by Author'),
|
|
'reviews' => pht('Revisions by Reviewer'),
|
|
'subscribed' => pht('Revisions by Subscriber'),
|
|
'all' => pht('Revisions'),
|
|
);
|
|
$view = id(clone $template)
|
|
->setRevisions($revisions)
|
|
->loadAssets();
|
|
$views[] = array(
|
|
'title' => idx($titles, $filter),
|
|
'view' => $view,
|
|
);
|
|
break;
|
|
default:
|
|
throw new Exception("Unknown filter '{$filter}'!");
|
|
}
|
|
|
|
return $views;
|
|
}
|
|
|
|
}
|