mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 08:52:39 +01:00
Use ApplicationSearch in ReleephBranchView
Summary: Ref T3721. Releeph currently attempts to implement a flexible, field-driven search for branches, but it's building all of its own infrastructure and it ends up heading down some weird paths. In particular, it loads **every** request and then makes calls into fields to filter them. It also tries to be very very general, which isn't really necessary (for example, I think it's reasonable for us to assume that we won't let you disable the "requestor" field). ApplicationSearch and CustomField provide more scalable approaches to this problem; move search on top of them. The query still ends up doing some filtering in-process, but it's now far more limited in scope and can be denormalized later. Test Plan: {F54304} Reviewers: btrahan Reviewed By: btrahan CC: chad, aran Maniphest Tasks: T3721 Differential Revision: https://secure.phabricator.com/D6758
This commit is contained in:
parent
42a81554ac
commit
23e68ee8cb
13 changed files with 401 additions and 728 deletions
|
@ -854,7 +854,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'aphront-form-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/7793ddd1/rsrc/css/aphront/form-view.css',
|
||||
'uri' => '/res/1be2545a/rsrc/css/aphront/form-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -4172,7 +4172,7 @@ celerity_register_resource_map(array(
|
|||
), array(
|
||||
'packages' =>
|
||||
array(
|
||||
'566968f8' =>
|
||||
'9737ebe0' =>
|
||||
array(
|
||||
'name' => 'core.pkg.css',
|
||||
'symbols' =>
|
||||
|
@ -4220,7 +4220,7 @@ celerity_register_resource_map(array(
|
|||
40 => 'phabricator-property-list-view-css',
|
||||
41 => 'phabricator-tag-view-css',
|
||||
),
|
||||
'uri' => '/res/pkg/566968f8/core.pkg.css',
|
||||
'uri' => '/res/pkg/9737ebe0/core.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'4f81c788' =>
|
||||
|
@ -4411,16 +4411,16 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'reverse' =>
|
||||
array(
|
||||
'aphront-dialog-view-css' => '566968f8',
|
||||
'aphront-error-view-css' => '566968f8',
|
||||
'aphront-form-view-css' => '566968f8',
|
||||
'aphront-list-filter-view-css' => '566968f8',
|
||||
'aphront-pager-view-css' => '566968f8',
|
||||
'aphront-panel-view-css' => '566968f8',
|
||||
'aphront-table-view-css' => '566968f8',
|
||||
'aphront-tokenizer-control-css' => '566968f8',
|
||||
'aphront-tooltip-css' => '566968f8',
|
||||
'aphront-typeahead-control-css' => '566968f8',
|
||||
'aphront-dialog-view-css' => '9737ebe0',
|
||||
'aphront-error-view-css' => '9737ebe0',
|
||||
'aphront-form-view-css' => '9737ebe0',
|
||||
'aphront-list-filter-view-css' => '9737ebe0',
|
||||
'aphront-pager-view-css' => '9737ebe0',
|
||||
'aphront-panel-view-css' => '9737ebe0',
|
||||
'aphront-table-view-css' => '9737ebe0',
|
||||
'aphront-tokenizer-control-css' => '9737ebe0',
|
||||
'aphront-tooltip-css' => '9737ebe0',
|
||||
'aphront-typeahead-control-css' => '9737ebe0',
|
||||
'differential-changeset-view-css' => '09216861',
|
||||
'differential-core-view-css' => '09216861',
|
||||
'differential-inline-comment-editor' => 'd07a3bc2',
|
||||
|
@ -4434,7 +4434,7 @@ celerity_register_resource_map(array(
|
|||
'differential-table-of-contents-css' => '09216861',
|
||||
'diffusion-commit-view-css' => 'c8ce2d88',
|
||||
'diffusion-icons-css' => 'c8ce2d88',
|
||||
'global-drag-and-drop-css' => '566968f8',
|
||||
'global-drag-and-drop-css' => '9737ebe0',
|
||||
'inline-comment-summary-css' => '09216861',
|
||||
'javelin-aphlict' => '4f81c788',
|
||||
'javelin-behavior' => '2dbbb7d1',
|
||||
|
@ -4507,55 +4507,55 @@ celerity_register_resource_map(array(
|
|||
'javelin-util' => '2dbbb7d1',
|
||||
'javelin-vector' => '2dbbb7d1',
|
||||
'javelin-workflow' => '2dbbb7d1',
|
||||
'lightbox-attachment-css' => '566968f8',
|
||||
'lightbox-attachment-css' => '9737ebe0',
|
||||
'maniphest-task-summary-css' => '06bacb9a',
|
||||
'maniphest-transaction-detail-css' => '06bacb9a',
|
||||
'phabricator-action-list-view-css' => '566968f8',
|
||||
'phabricator-application-launch-view-css' => '566968f8',
|
||||
'phabricator-action-list-view-css' => '9737ebe0',
|
||||
'phabricator-application-launch-view-css' => '9737ebe0',
|
||||
'phabricator-busy' => '4f81c788',
|
||||
'phabricator-content-source-view-css' => '09216861',
|
||||
'phabricator-core-css' => '566968f8',
|
||||
'phabricator-crumbs-view-css' => '566968f8',
|
||||
'phabricator-core-css' => '9737ebe0',
|
||||
'phabricator-crumbs-view-css' => '9737ebe0',
|
||||
'phabricator-drag-and-drop-file-upload' => 'd07a3bc2',
|
||||
'phabricator-dropdown-menu' => '4f81c788',
|
||||
'phabricator-file-upload' => '4f81c788',
|
||||
'phabricator-filetree-view-css' => '566968f8',
|
||||
'phabricator-flag-css' => '566968f8',
|
||||
'phabricator-form-view-css' => '566968f8',
|
||||
'phabricator-header-view-css' => '566968f8',
|
||||
'phabricator-filetree-view-css' => '9737ebe0',
|
||||
'phabricator-flag-css' => '9737ebe0',
|
||||
'phabricator-form-view-css' => '9737ebe0',
|
||||
'phabricator-header-view-css' => '9737ebe0',
|
||||
'phabricator-hovercard' => '4f81c788',
|
||||
'phabricator-jump-nav' => '566968f8',
|
||||
'phabricator-jump-nav' => '9737ebe0',
|
||||
'phabricator-keyboard-shortcut' => '4f81c788',
|
||||
'phabricator-keyboard-shortcut-manager' => '4f81c788',
|
||||
'phabricator-main-menu-view' => '566968f8',
|
||||
'phabricator-main-menu-view' => '9737ebe0',
|
||||
'phabricator-menu-item' => '4f81c788',
|
||||
'phabricator-nav-view-css' => '566968f8',
|
||||
'phabricator-nav-view-css' => '9737ebe0',
|
||||
'phabricator-notification' => '4f81c788',
|
||||
'phabricator-notification-css' => '566968f8',
|
||||
'phabricator-notification-menu-css' => '566968f8',
|
||||
'phabricator-object-item-list-view-css' => '566968f8',
|
||||
'phabricator-notification-css' => '9737ebe0',
|
||||
'phabricator-notification-menu-css' => '9737ebe0',
|
||||
'phabricator-object-item-list-view-css' => '9737ebe0',
|
||||
'phabricator-object-selector-css' => '09216861',
|
||||
'phabricator-phtize' => '4f81c788',
|
||||
'phabricator-prefab' => '4f81c788',
|
||||
'phabricator-project-tag-css' => '06bacb9a',
|
||||
'phabricator-property-list-view-css' => '566968f8',
|
||||
'phabricator-remarkup-css' => '566968f8',
|
||||
'phabricator-property-list-view-css' => '9737ebe0',
|
||||
'phabricator-remarkup-css' => '9737ebe0',
|
||||
'phabricator-shaped-request' => 'd07a3bc2',
|
||||
'phabricator-side-menu-view-css' => '566968f8',
|
||||
'phabricator-standard-page-view' => '566968f8',
|
||||
'phabricator-tag-view-css' => '566968f8',
|
||||
'phabricator-side-menu-view-css' => '9737ebe0',
|
||||
'phabricator-standard-page-view' => '9737ebe0',
|
||||
'phabricator-tag-view-css' => '9737ebe0',
|
||||
'phabricator-textareautils' => '4f81c788',
|
||||
'phabricator-tooltip' => '4f81c788',
|
||||
'phabricator-transaction-view-css' => '566968f8',
|
||||
'phabricator-zindex-css' => '566968f8',
|
||||
'phui-button-css' => '566968f8',
|
||||
'phui-form-css' => '566968f8',
|
||||
'phui-icon-view-css' => '566968f8',
|
||||
'phui-spacing-css' => '566968f8',
|
||||
'sprite-apps-large-css' => '566968f8',
|
||||
'sprite-gradient-css' => '566968f8',
|
||||
'sprite-icons-css' => '566968f8',
|
||||
'sprite-menu-css' => '566968f8',
|
||||
'syntax-highlighting-css' => '566968f8',
|
||||
'phabricator-transaction-view-css' => '9737ebe0',
|
||||
'phabricator-zindex-css' => '9737ebe0',
|
||||
'phui-button-css' => '9737ebe0',
|
||||
'phui-form-css' => '9737ebe0',
|
||||
'phui-icon-view-css' => '9737ebe0',
|
||||
'phui-spacing-css' => '9737ebe0',
|
||||
'sprite-apps-large-css' => '9737ebe0',
|
||||
'sprite-gradient-css' => '9737ebe0',
|
||||
'sprite-icons-css' => '9737ebe0',
|
||||
'sprite-menu-css' => '9737ebe0',
|
||||
'syntax-highlighting-css' => '9737ebe0',
|
||||
),
|
||||
));
|
||||
|
|
|
@ -32,7 +32,6 @@ phutil_register_library_map(array(
|
|||
'AphrontFileResponse' => 'aphront/response/AphrontFileResponse.php',
|
||||
'AphrontFormCheckboxControl' => 'view/form/control/AphrontFormCheckboxControl.php',
|
||||
'AphrontFormControl' => 'view/form/control/AphrontFormControl.php',
|
||||
'AphrontFormCountedToggleButtonsControl' => 'view/form/control/AphrontFormCountedToggleButtonsControl.php',
|
||||
'AphrontFormCropControl' => 'view/form/control/AphrontFormCropControl.php',
|
||||
'AphrontFormDateControl' => 'view/form/control/AphrontFormDateControl.php',
|
||||
'AphrontFormDividerControl' => 'view/form/control/AphrontFormDividerControl.php',
|
||||
|
@ -1020,7 +1019,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCountdownSearchEngine' => 'applications/countdown/query/PhabricatorCountdownSearchEngine.php',
|
||||
'PhabricatorCountdownView' => 'applications/countdown/view/PhabricatorCountdownView.php',
|
||||
'PhabricatorCountdownViewController' => 'applications/countdown/controller/PhabricatorCountdownViewController.php',
|
||||
'PhabricatorCountedToggleButtonsExample' => 'applications/uiexample/examples/PhabricatorCountedToggleButtonsExample.php',
|
||||
'PhabricatorCrumbView' => 'view/layout/PhabricatorCrumbView.php',
|
||||
'PhabricatorCrumbsView' => 'view/layout/PhabricatorCrumbsView.php',
|
||||
'PhabricatorCursorPagedPolicyAwareQuery' => 'infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php',
|
||||
|
@ -1996,6 +1994,7 @@ phutil_register_library_map(array(
|
|||
'ReleephRequestMailReceiver' => 'applications/releeph/mail/ReleephRequestMailReceiver.php',
|
||||
'ReleephRequestQuery' => 'applications/releeph/query/ReleephRequestQuery.php',
|
||||
'ReleephRequestReplyHandler' => 'applications/releeph/mail/ReleephRequestReplyHandler.php',
|
||||
'ReleephRequestSearchEngine' => 'applications/releeph/query/ReleephRequestSearchEngine.php',
|
||||
'ReleephRequestStatus' => 'applications/releeph/constants/ReleephRequestStatus.php',
|
||||
'ReleephRequestStatusView' => 'applications/releeph/view/request/ReleephRequestStatusView.php',
|
||||
'ReleephRequestTransaction' => 'applications/releeph/storage/ReleephRequestTransaction.php',
|
||||
|
@ -2062,7 +2061,6 @@ phutil_register_library_map(array(
|
|||
'AphrontFileResponse' => 'AphrontResponse',
|
||||
'AphrontFormCheckboxControl' => 'AphrontFormControl',
|
||||
'AphrontFormControl' => 'AphrontView',
|
||||
'AphrontFormCountedToggleButtonsControl' => 'AphrontFormControl',
|
||||
'AphrontFormCropControl' => 'AphrontFormControl',
|
||||
'AphrontFormDateControl' => 'AphrontFormControl',
|
||||
'AphrontFormDividerControl' => 'AphrontFormControl',
|
||||
|
@ -3088,7 +3086,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCountdownSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorCountdownView' => 'AphrontTagView',
|
||||
'PhabricatorCountdownViewController' => 'PhabricatorCountdownController',
|
||||
'PhabricatorCountedToggleButtonsExample' => 'PhabricatorUIExample',
|
||||
'PhabricatorCrumbView' => 'AphrontView',
|
||||
'PhabricatorCrumbsView' => 'AphrontView',
|
||||
'PhabricatorCursorPagedPolicyAwareQuery' => 'PhabricatorPolicyAwareQuery',
|
||||
|
@ -4129,7 +4126,11 @@ phutil_register_library_map(array(
|
|||
'ReleephBranchNamePreviewController' => 'ReleephController',
|
||||
'ReleephBranchPreviewView' => 'AphrontFormControl',
|
||||
'ReleephBranchQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'ReleephBranchViewController' => 'ReleephProjectController',
|
||||
'ReleephBranchViewController' =>
|
||||
array(
|
||||
0 => 'ReleephProjectController',
|
||||
1 => 'PhabricatorApplicationSearchResultsControllerInterface',
|
||||
),
|
||||
'ReleephCommitFinderException' => 'Exception',
|
||||
'ReleephCommitMessageFieldSpecification' => 'ReleephFieldSpecification',
|
||||
'ReleephController' => 'PhabricatorController',
|
||||
|
@ -4190,6 +4191,7 @@ phutil_register_library_map(array(
|
|||
'ReleephRequestMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'ReleephRequestQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'ReleephRequestReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'ReleephRequestSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'ReleephRequestStatusView' => 'AphrontView',
|
||||
'ReleephRequestTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'ReleephRequestTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
|
|
|
@ -75,7 +75,7 @@ final class PhabricatorApplicationReleeph extends PhabricatorApplication {
|
|||
|
||||
// Branch navigation made pretty, as it's the most common:
|
||||
'(?P<projectName>[^/]+)/(?P<branchName>[^/]+)/' => array(
|
||||
'' => 'ReleephBranchViewController',
|
||||
'(?:query/(?P<queryKey>[^/]+)/)?' => 'ReleephBranchViewController',
|
||||
'edit/' => 'ReleephBranchEditController',
|
||||
'request/' => 'ReleephRequestEditController',
|
||||
'(?P<action>close|re-open)/' => 'ReleephBranchAccessController',
|
||||
|
|
|
@ -1,62 +1,84 @@
|
|||
<?php
|
||||
|
||||
final class ReleephBranchViewController extends ReleephProjectController {
|
||||
final class ReleephBranchViewController extends ReleephProjectController
|
||||
implements PhabricatorApplicationSearchResultsControllerInterface {
|
||||
|
||||
private $queryKey;
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
parent::willProcessRequest($data);
|
||||
$this->queryKey = idx($data, 'queryKey');
|
||||
}
|
||||
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
|
||||
$controller = id(new PhabricatorApplicationSearchController($request))
|
||||
->setQueryKey($this->queryKey)
|
||||
->setSearchEngine($this->getSearchEngine())
|
||||
->setNavigation($this->buildSideNavView());
|
||||
|
||||
return $this->delegateToController($controller);
|
||||
}
|
||||
|
||||
public function renderResultsList(
|
||||
array $requests,
|
||||
PhabricatorSavedQuery $query) {
|
||||
|
||||
assert_instances_of($requests, 'ReleephRequest');
|
||||
$viewer = $this->getRequest()->getUser();
|
||||
|
||||
$releeph_branch = $this->getReleephBranch();
|
||||
$releeph_project = $this->getReleephProject();
|
||||
$all_releeph_requests = $releeph_branch->loadReleephRequests(
|
||||
$request->getUser());
|
||||
|
||||
$selector = $releeph_project->getReleephFieldSelector();
|
||||
$fields = $selector->arrangeFieldsForSelectForm(
|
||||
$selector->getFieldSpecifications());
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setMethod('GET')
|
||||
->setUser($request->getUser());
|
||||
|
||||
$filtered_releeph_requests = $all_releeph_requests;
|
||||
foreach ($fields as $field) {
|
||||
$all_releeph_requests_without_this_field = $all_releeph_requests;
|
||||
foreach ($fields as $other_field) {
|
||||
if ($other_field != $field) {
|
||||
$other_field->selectReleephRequestsHook(
|
||||
$request,
|
||||
$all_releeph_requests_without_this_field);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
$field->appendSelectControlsHook(
|
||||
$form,
|
||||
$request,
|
||||
$all_releeph_requests,
|
||||
$all_releeph_requests_without_this_field);
|
||||
|
||||
$field->selectReleephRequestsHook(
|
||||
$request,
|
||||
$filtered_releeph_requests);
|
||||
}
|
||||
|
||||
$form->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Filter')));
|
||||
// TODO: Really gross.
|
||||
$releeph_branch->populateReleephRequestHandles(
|
||||
$viewer,
|
||||
$requests);
|
||||
|
||||
$list = id(new ReleephRequestHeaderListView())
|
||||
->setOriginType('branch')
|
||||
->setUser($request->getUser())
|
||||
->setUser($viewer)
|
||||
->setAphrontRequest($this->getRequest())
|
||||
->setReleephProject($releeph_project)
|
||||
->setReleephBranch($releeph_branch)
|
||||
->setReleephRequests($filtered_releeph_requests);
|
||||
->setReleephRequests($requests);
|
||||
|
||||
$filter = id(new AphrontListFilterView())
|
||||
->appendChild($form);
|
||||
return $list;
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
public function buildSideNavView($for_app = false) {
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
|
||||
|
||||
|
||||
$this->getSearchEngine()->addNavigationItems($nav->getMenu());
|
||||
|
||||
$nav->selectFilter(null);
|
||||
|
||||
return $nav;
|
||||
}
|
||||
|
||||
private function getSearchEngine() {
|
||||
$branch = $this->getReleephBranch();
|
||||
return id(new ReleephRequestSearchEngine())
|
||||
->setBranch($branch)
|
||||
->setBaseURI($branch->getURI())
|
||||
->setViewer($this->getRequest()->getUser());
|
||||
}
|
||||
|
||||
public function buildApplicationCrumbs() {
|
||||
$releeph_branch = $this->getReleephBranch();
|
||||
$releeph_project = $this->getReleephProject();
|
||||
|
||||
$crumbs = parent::buildApplicationCrumbs()
|
||||
->addCrumb(
|
||||
id(new PhabricatorCrumbView())
|
||||
->setName($releeph_project->getName())
|
||||
|
@ -66,7 +88,6 @@ final class ReleephBranchViewController extends ReleephProjectController {
|
|||
->setName($releeph_branch->getDisplayNameWithDetail())
|
||||
->setHref($releeph_branch->getURI()));
|
||||
|
||||
// Don't show the request button for inactive (closed) branches
|
||||
if ($releeph_branch->isActive()) {
|
||||
$create_uri = $releeph_branch->getURI('request/');
|
||||
$crumbs->addAction(
|
||||
|
@ -76,19 +97,8 @@ final class ReleephBranchViewController extends ReleephProjectController {
|
|||
->setIcon('create'));
|
||||
}
|
||||
|
||||
return $this->buildStandardPageResponse(
|
||||
array(
|
||||
$crumbs,
|
||||
$filter,
|
||||
$list
|
||||
),
|
||||
array(
|
||||
'title' =>
|
||||
$releeph_project->getName().
|
||||
' - '.
|
||||
$releeph_branch->getDisplayName().
|
||||
' requests'
|
||||
));
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -45,11 +45,6 @@ abstract class ReleephFieldSpecification
|
|||
return $this->getStorageKey() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This will be called many times if you are using **Selecting**. In
|
||||
* particular, for N selecting fields, selectReleephRequests() is called
|
||||
* N-squared times, each time for R ReleephRequests.
|
||||
*/
|
||||
final public function getValue() {
|
||||
if ($this->requestValue !== null) {
|
||||
return $this->requestValue;
|
||||
|
@ -174,76 +169,6 @@ abstract class ReleephFieldSpecification
|
|||
public function bulkLoad(array $releeph_requests) {
|
||||
}
|
||||
|
||||
|
||||
/* -( Selecting )---------------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* Append select controls to the given form.
|
||||
*
|
||||
* You are given:
|
||||
*
|
||||
* - the AphrontFormView to append to;
|
||||
*
|
||||
* - the AphrontRequest, so you can make use of the value currently selected
|
||||
* in the form;
|
||||
*
|
||||
* - $all_releeph_requests: an array of all the ReleephRequests without any
|
||||
* selection based filtering; and
|
||||
*
|
||||
* - $all_releeph_requests_without_this_field: an array of ReleephRequests
|
||||
* that have been selected by all the other select controls on this page.
|
||||
*
|
||||
* The example in ReleephLevelFieldSpecification shows how to use these.
|
||||
* $all_releeph_requests lets you find out all the values of a field in all
|
||||
* ReleephRequests, so you can render controls for every known value.
|
||||
*
|
||||
* $all_releeph_requests_without_this_field lets you count how many
|
||||
* ReleephRequests could be affected by this field's select control, after
|
||||
* all the other fields have made their selections.
|
||||
* ReleephLevelFieldSpecification uses this to render a preview count for
|
||||
* each select button, and disables the button completely (but still renders
|
||||
* it) if it couldn't possibly select anything.
|
||||
*/
|
||||
protected function appendSelectControls(
|
||||
AphrontFormView $form,
|
||||
AphrontRequest $request,
|
||||
array $all_releeph_requests,
|
||||
array $all_releeph_requests_without_this_field) {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the $releeph_requests using the data you set with your form
|
||||
* controls, and which is now available in the provided AphrontRequest.
|
||||
*/
|
||||
protected function selectReleephRequests(AphrontRequest $request,
|
||||
array &$releeph_requests) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* If you have PHIDs that can be used in an AphrontFormTokenizerControl,
|
||||
* return true here, return the PHIDs in getSelectablePHIDs(), and return the
|
||||
* URL the Tokenizer should use for the form control in
|
||||
* getSelectTokenizerDatasource().
|
||||
*
|
||||
* This is a cheap alternative to implementing appendSelectControls() and
|
||||
* selectReleephRequests() in full.
|
||||
*/
|
||||
protected function hasSelectablePHIDs() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function getSelectablePHIDs() {
|
||||
throw new ReleephFieldSpecificationIncompleteException($this);
|
||||
}
|
||||
|
||||
protected function getSelectTokenizerDatasource() {
|
||||
throw new ReleephFieldSpecificationIncompleteException($this);
|
||||
}
|
||||
|
||||
|
||||
/* -( Commit Messages )---------------------------------------------------- */
|
||||
|
||||
public function shouldAppearOnCommitMessage() {
|
||||
|
@ -328,106 +253,4 @@ abstract class ReleephFieldSpecification
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
/* -( Implementation )----------------------------------------------------- */
|
||||
|
||||
/**
|
||||
* The "hook" functions ##appendSelectControlsHook()## and
|
||||
* ##selectReleephRequestsHook()## are used with ##hasSelectablePHIDs()##, to
|
||||
* use the tokenizing helpers if ##hasSelectablePHIDs()## returns true.
|
||||
*/
|
||||
public function appendSelectControlsHook(
|
||||
AphrontFormView $form,
|
||||
AphrontRequest $request,
|
||||
array $all_releeph_requests,
|
||||
array $all_releeph_requests_without_this_field) {
|
||||
|
||||
if ($this->hasSelectablePHIDs()) {
|
||||
$this->appendTokenizingSelectControl(
|
||||
$form,
|
||||
$request,
|
||||
$all_releeph_requests,
|
||||
$all_releeph_requests_without_this_field);
|
||||
} else {
|
||||
$this->appendSelectControls(
|
||||
$form,
|
||||
$request,
|
||||
$all_releeph_requests,
|
||||
$all_releeph_requests_without_this_field);
|
||||
}
|
||||
}
|
||||
|
||||
// See above
|
||||
public function selectReleephRequestsHook(AphrontRequest $request,
|
||||
array &$releeph_requests) {
|
||||
|
||||
if ($this->hasSelectablePHIDs()) {
|
||||
$this->selectReleephRequestsFromTokens(
|
||||
$request,
|
||||
$releeph_requests);
|
||||
} else {
|
||||
$this->selectReleephRequests(
|
||||
$request,
|
||||
$releeph_requests);
|
||||
}
|
||||
}
|
||||
|
||||
private function appendTokenizingSelectControl(
|
||||
AphrontFormView $form,
|
||||
AphrontRequest $request,
|
||||
array $all_releeph_requests,
|
||||
array $all_releeph_requests_without_this_field) {
|
||||
|
||||
$key = urlencode(strtolower($this->getName()));
|
||||
$selected_phids = $request->getArr($key);
|
||||
$handles = id(new PhabricatorObjectHandleData($selected_phids))
|
||||
->setViewer($request->getUser())
|
||||
->loadHandles();
|
||||
|
||||
$tokens = array();
|
||||
foreach ($selected_phids as $phid) {
|
||||
$tokens[$phid] = $handles[$phid]->getFullName();
|
||||
}
|
||||
|
||||
$datasource = $this->getSelectTokenizerDatasource();
|
||||
$control =
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource($datasource)
|
||||
->setName($key)
|
||||
->setLabel($this->getName())
|
||||
->setValue($tokens);
|
||||
|
||||
$form->appendChild($control);
|
||||
}
|
||||
|
||||
private function selectReleephRequestsFromTokens(AphrontRequest $request,
|
||||
array &$releeph_requests) {
|
||||
|
||||
$key = urlencode(strtolower($this->getName()));
|
||||
$selected_phids = $request->getArr($key);
|
||||
if (!$selected_phids) {
|
||||
return;
|
||||
}
|
||||
|
||||
$selected_phid_lookup = array();
|
||||
foreach ($selected_phids as $phid) {
|
||||
$selected_phid_lookup[$phid] = $phid;
|
||||
}
|
||||
|
||||
$filtered = array();
|
||||
foreach ($releeph_requests as $releeph_request) {
|
||||
$rq_phids = $this
|
||||
->setReleephRequest($releeph_request)
|
||||
->getSelectablePHIDs();
|
||||
foreach ($rq_phids as $rq_phid) {
|
||||
if (idx($selected_phid_lookup, $rq_phid)) {
|
||||
$filtered[] = $releeph_request;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$releeph_requests = $filtered;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,13 +19,6 @@ abstract class ReleephLevelFieldSpecification
|
|||
abstract public function getNameForLevel($level);
|
||||
abstract public function getDescriptionForLevel($level);
|
||||
|
||||
/**
|
||||
* Use getCanonicalLevel() to convert old, unsupported levels to new ones.
|
||||
*/
|
||||
protected function getCanonicalLevel($misc_level) {
|
||||
return $misc_level;
|
||||
}
|
||||
|
||||
public function getStorageKey() {
|
||||
$class = get_class($this);
|
||||
throw new ReleephFieldSpecificationIncompleteException(
|
||||
|
@ -34,16 +27,18 @@ abstract class ReleephLevelFieldSpecification
|
|||
}
|
||||
|
||||
public function renderValueForHeaderView() {
|
||||
$raw_level = $this->getValue();
|
||||
$level = $this->getCanonicalLevel($raw_level);
|
||||
return $this->getNameForLevel($level);
|
||||
return $this->getNameForLevel($this->getValue());
|
||||
}
|
||||
|
||||
public function renderEditControl() {
|
||||
$control_name = $this->getRequiredStorageKey();
|
||||
$all_levels = $this->getLevels();
|
||||
|
||||
$level = $this->getCanonicalLevel($this->getValue());
|
||||
$level = $request->getStr($control_name);
|
||||
if (!$level) {
|
||||
$level = $this->getValue();
|
||||
}
|
||||
|
||||
if (!$level) {
|
||||
$level = $this->getDefaultLevel();
|
||||
}
|
||||
|
@ -141,83 +136,4 @@ abstract class ReleephLevelFieldSpecification
|
|||
return idx($this->nameMap, $name);
|
||||
}
|
||||
|
||||
protected function appendSelectControls(
|
||||
AphrontFormView $form,
|
||||
AphrontRequest $request,
|
||||
array $all_releeph_requests,
|
||||
array $all_releeph_requests_without_this_field) {
|
||||
|
||||
$buttons = array(null => 'All');
|
||||
|
||||
// Add in known level/names
|
||||
foreach ($this->getLevels() as $level) {
|
||||
$name = $this->getNameForLevel($level);
|
||||
$buttons[$name] = $name;
|
||||
}
|
||||
|
||||
// Add in any names we've seen in the wild, as well.
|
||||
foreach ($all_releeph_requests as $releeph_request) {
|
||||
$raw_level = $this->setReleephRequest($releeph_request)->getValue();
|
||||
if (!$raw_level) {
|
||||
// The ReleephRequest might not have a level set
|
||||
continue;
|
||||
}
|
||||
$level = $this->getCanonicalLevel($raw_level);
|
||||
$name = $this->getNameForLevel($level);
|
||||
$buttons[$name] = $name;
|
||||
}
|
||||
|
||||
$key = $this->getRequiredStorageKey();
|
||||
$current = $request->getStr($key);
|
||||
|
||||
$counters = array(null => count($all_releeph_requests_without_this_field));
|
||||
foreach ($all_releeph_requests_without_this_field as $releeph_request) {
|
||||
$raw_level = $this->setReleephRequest($releeph_request)->getValue();
|
||||
if (!$raw_level) {
|
||||
// The ReleephRequest might not have a level set
|
||||
continue;
|
||||
}
|
||||
$level = $this->getCanonicalLevel($raw_level);
|
||||
$name = $this->getNameForLevel($level);
|
||||
|
||||
if (!isset($counters[$name])) {
|
||||
$counters[$name] = 0;
|
||||
}
|
||||
$counters[$name]++;
|
||||
}
|
||||
|
||||
$control = id(new AphrontFormCountedToggleButtonsControl())
|
||||
->setLabel($this->getName())
|
||||
->setValue($current)
|
||||
->setBaseURI($request->getRequestURI(), $key)
|
||||
->setButtons($buttons)
|
||||
->setCounters($counters);
|
||||
|
||||
$form
|
||||
->appendChild($control)
|
||||
->addHiddenInput($key, $current);
|
||||
}
|
||||
|
||||
protected function selectReleephRequests(AphrontRequest $request,
|
||||
array &$releeph_requests) {
|
||||
$key = $this->getRequiredStorageKey();
|
||||
$current = $request->getStr($key);
|
||||
|
||||
if (!$current) {
|
||||
return;
|
||||
}
|
||||
|
||||
$filtered = array();
|
||||
foreach ($releeph_requests as $releeph_request) {
|
||||
$raw_level = $this->setReleephRequest($releeph_request)->getValue();
|
||||
$level = $this->getCanonicalLevel($raw_level);
|
||||
$name = $this->getNameForLevel($level);
|
||||
if ($name == $current) {
|
||||
$filtered[] = $releeph_request;
|
||||
}
|
||||
}
|
||||
|
||||
$releeph_requests = $filtered;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,20 +26,6 @@ final class ReleephRequestorFieldSpecification
|
|||
->render();
|
||||
}
|
||||
|
||||
public function hasSelectablePHIDs() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getSelectTokenizerDatasource() {
|
||||
return '/typeahead/common/users/';
|
||||
}
|
||||
|
||||
public function getSelectablePHIDs() {
|
||||
return array(
|
||||
$this->getReleephRequest()->getRequestUserPHID(),
|
||||
);
|
||||
}
|
||||
|
||||
public function shouldAppearOnCommitMessage() {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -17,77 +17,4 @@ final class ReleephStatusFieldSpecification
|
|||
->render();
|
||||
}
|
||||
|
||||
private static $filters = array(
|
||||
'req' => ReleephRequestStatus::STATUS_REQUESTED,
|
||||
'app' => ReleephRequestStatus::STATUS_NEEDS_PICK,
|
||||
'rej' => ReleephRequestStatus::STATUS_REJECTED,
|
||||
'abn' => ReleephRequestStatus::STATUS_ABANDONED,
|
||||
'mer' => ReleephRequestStatus::STATUS_PICKED,
|
||||
'rrq' => ReleephRequestStatus::STATUS_NEEDS_REVERT,
|
||||
'rev' => ReleephRequestStatus::STATUS_REVERTED,
|
||||
);
|
||||
|
||||
protected function appendSelectControls(
|
||||
AphrontFormView $form,
|
||||
AphrontRequest $request,
|
||||
array $all_releeph_requests,
|
||||
array $all_releeph_requests_without_this_field) {
|
||||
|
||||
$filter_names = array(
|
||||
null => 'All',
|
||||
);
|
||||
|
||||
foreach (self::$filters as $code => $status) {
|
||||
$name = ReleephRequestStatus::getStatusDescriptionFor($status);
|
||||
$filter_names[$code] = $name;
|
||||
}
|
||||
|
||||
$key = 'status';
|
||||
$code = $request->getStr($key);
|
||||
$current_status = idx(self::$filters, $code);
|
||||
|
||||
$codes = array_flip(self::$filters);
|
||||
|
||||
$counters = array(null => count($all_releeph_requests_without_this_field));
|
||||
foreach ($all_releeph_requests_without_this_field as $releeph_request) {
|
||||
$this_status = $releeph_request->getStatus();
|
||||
$this_code = idx($codes, $this_status);
|
||||
if (!isset($counters[$this_code])) {
|
||||
$counters[$this_code] = 0;
|
||||
}
|
||||
$counters[$this_code]++;
|
||||
}
|
||||
|
||||
$control = id(new AphrontFormCountedToggleButtonsControl())
|
||||
->setLabel($this->getName())
|
||||
->setValue($code)
|
||||
->setBaseURI($request->getRequestURI(), $key)
|
||||
->setButtons($filter_names)
|
||||
->setCounters($counters);
|
||||
|
||||
$form
|
||||
->appendChild($control)
|
||||
->addHiddenInput($key, $code);
|
||||
}
|
||||
|
||||
protected function selectReleephRequests(AphrontRequest $request,
|
||||
array &$releeph_requests) {
|
||||
|
||||
$key = 'status';
|
||||
$code = $request->getStr($key);
|
||||
if (!$code) {
|
||||
return;
|
||||
}
|
||||
|
||||
$current_status = idx(self::$filters, $code);
|
||||
|
||||
$filtered = array();
|
||||
foreach ($releeph_requests as $releeph_request) {
|
||||
if ($releeph_request->getStatus() == $current_status) {
|
||||
$filtered[] = $releeph_request;
|
||||
}
|
||||
}
|
||||
$releeph_requests = $filtered;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -7,6 +7,21 @@ final class ReleephRequestQuery
|
|||
private $commitToRevMap;
|
||||
private $ids;
|
||||
private $phids;
|
||||
private $severities;
|
||||
private $requestorPHIDs;
|
||||
private $branchIDs;
|
||||
|
||||
const STATUS_ALL = 'status-all';
|
||||
const STATUS_OPEN = 'status-open';
|
||||
const STATUS_REQUESTED = 'status-requested';
|
||||
const STATUS_NEEDS_PULL = 'status-needs-pull';
|
||||
const STATUS_REJECTED = 'status-rejected';
|
||||
const STATUS_ABANDONED = 'status-abandoned';
|
||||
const STATUS_PULLED = 'status-pulled';
|
||||
const STATUS_NEEDS_REVERT = 'status-needs-revert';
|
||||
const STATUS_REVERTED = 'status-reverted';
|
||||
|
||||
private $status = self::STATUS_ALL;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -18,6 +33,11 @@ final class ReleephRequestQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withBranchIDs(array $branch_ids) {
|
||||
$this->branchIDs = $branch_ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRevisionPHID($commit_phid) {
|
||||
if ($this->commitToRevMap) {
|
||||
return idx($this->commitToRevMap, $commit_phid, null);
|
||||
|
@ -26,11 +46,26 @@ final class ReleephRequestQuery
|
|||
return null;
|
||||
}
|
||||
|
||||
public function withStatus($status) {
|
||||
$this->status = $status;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withRequestedCommitPHIDs(array $requested_commit_phids) {
|
||||
$this->requestedCommitPHIDs = $requested_commit_phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withRequestorPHIDs(array $phids) {
|
||||
$this->requestorPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withSeverities(array $severities) {
|
||||
$this->severities = $severities;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withRevisionPHIDs(array $revision_phids) {
|
||||
$type = PhabricatorEdgeConfig::TYPE_DREV_HAS_COMMIT;
|
||||
|
||||
|
@ -65,6 +100,32 @@ final class ReleephRequestQuery
|
|||
return $table->loadAllFromArray($data);
|
||||
}
|
||||
|
||||
public function willFilterPage(array $requests) {
|
||||
|
||||
// TODO: These should be serviced by the query, but are not currently
|
||||
// denormalized anywhere. For now, filter them here instead.
|
||||
|
||||
$keep_status = array_fuse($this->getKeepStatusConstants());
|
||||
if ($keep_status) {
|
||||
foreach ($requests as $key => $request) {
|
||||
if (empty($keep_status[$request->getStatus()])) {
|
||||
unset($requests[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->severities) {
|
||||
$severities = array_fuse($this->severities);
|
||||
foreach ($requests as $key => $request) {
|
||||
if (empty($severities[$request->getDetail('releeph:severity')])) {
|
||||
unset($requests[$key]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $requests;
|
||||
}
|
||||
|
||||
private function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
$where = array();
|
||||
|
||||
|
@ -82,6 +143,13 @@ final class ReleephRequestQuery
|
|||
$this->phids);
|
||||
}
|
||||
|
||||
if ($this->branchIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'branchID IN (%Ld)',
|
||||
$this->branchIDs);
|
||||
}
|
||||
|
||||
if ($this->requestedCommitPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
|
@ -89,9 +157,59 @@ final class ReleephRequestQuery
|
|||
$this->requestedCommitPHIDs);
|
||||
}
|
||||
|
||||
if ($this->requestorPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'requestUserPHID IN (%Ls)',
|
||||
$this->requestorPHIDs);
|
||||
}
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function getKeepStatusConstants() {
|
||||
switch ($this->status) {
|
||||
case self::STATUS_ALL:
|
||||
return array();
|
||||
case self::STATUS_OPEN:
|
||||
return array(
|
||||
ReleephRequestStatus::STATUS_REQUESTED,
|
||||
ReleephRequestStatus::STATUS_NEEDS_PICK,
|
||||
ReleephRequestStatus::STATUS_NEEDS_REVERT,
|
||||
);
|
||||
case self::STATUS_REQUESTED:
|
||||
return array(
|
||||
ReleephRequestStatus::STATUS_REQUESTED,
|
||||
);
|
||||
case self::STATUS_NEEDS_PULL:
|
||||
return array(
|
||||
ReleephRequestStatus::STATUS_NEEDS_PICK,
|
||||
);
|
||||
case self::STATUS_REJECTED:
|
||||
return array(
|
||||
ReleephRequestStatus::STATUS_REJECTED,
|
||||
);
|
||||
case self::STATUS_ABANDONED:
|
||||
return array(
|
||||
ReleephRequestStatus::STATUS_ABANDONED,
|
||||
);
|
||||
case self::STATUS_PULLED:
|
||||
return array(
|
||||
ReleephRequestStatus::STATUS_PICKED,
|
||||
);
|
||||
case self::STATUS_NEEDS_REVERT:
|
||||
return array(
|
||||
ReleephRequestStatus::NEEDS_REVERT,
|
||||
);
|
||||
case self::STATUS_REVERTED:
|
||||
return array(
|
||||
ReleephRequestStatus::REVERTED,
|
||||
);
|
||||
default:
|
||||
throw new Exception("Unknown status '{$this->status}'!");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
158
src/applications/releeph/query/ReleephRequestSearchEngine.php
Normal file
158
src/applications/releeph/query/ReleephRequestSearchEngine.php
Normal file
|
@ -0,0 +1,158 @@
|
|||
<?php
|
||||
|
||||
final class ReleephRequestSearchEngine
|
||||
extends PhabricatorApplicationSearchEngine {
|
||||
|
||||
private $branch;
|
||||
private $baseURI;
|
||||
|
||||
public function setBranch(ReleephBranch $branch) {
|
||||
$this->branch = $branch;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBranch() {
|
||||
return $this->branch;
|
||||
}
|
||||
|
||||
public function setBaseURI($base_uri) {
|
||||
$this->baseURI = $base_uri;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromRequest(AphrontRequest $request) {
|
||||
$saved = new PhabricatorSavedQuery();
|
||||
|
||||
$saved->setParameter('status', $request->getStr('status'));
|
||||
$saved->setParameter('severity', $request->getStr('severity'));
|
||||
$saved->setParameter('requestorPHIDs', $request->getArr('requestorPHIDs'));
|
||||
|
||||
return $saved;
|
||||
}
|
||||
|
||||
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
|
||||
$query = id(new ReleephRequestQuery())
|
||||
->withBranchIDs(array($this->getBranch()->getID()));
|
||||
|
||||
$status = $saved->getParameter('status');
|
||||
$status = idx($this->getStatusValues(), $status);
|
||||
if ($status) {
|
||||
$query->withStatus($status);
|
||||
}
|
||||
|
||||
$severity = $saved->getParameter('severity');
|
||||
if ($severity) {
|
||||
$query->withSeverities(array($severity));
|
||||
}
|
||||
|
||||
$requestor_phids = $saved->getParameter('requestorPHIDs');
|
||||
if ($requestor_phids) {
|
||||
$query->withRequestorPHIDs($requestor_phids);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function buildSearchForm(
|
||||
AphrontFormView $form,
|
||||
PhabricatorSavedQuery $saved_query) {
|
||||
|
||||
$phids = $saved_query->getParameter('requestorPHIDs', array());
|
||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||
->setViewer($this->requireViewer())
|
||||
->loadHandles();
|
||||
$requestor_tokens = mpull($handles, 'getFullName', 'getPHID');
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setName('status')
|
||||
->setLabel(pht('Status'))
|
||||
->setValue($saved_query->getParameter('status'))
|
||||
->setOptions($this->getStatusOptions()))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setName('severity')
|
||||
->setLabel(pht('Severity'))
|
||||
->setValue($saved_query->getParameter('severity'))
|
||||
->setOptions($this->getSeverityOptions()))
|
||||
->appendChild(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setDatasource('/typeahead/common/users/')
|
||||
->setName('requestorPHIDs')
|
||||
->setLabel(pht('Requestors'))
|
||||
->setValue($requestor_tokens));
|
||||
}
|
||||
|
||||
protected function getURI($path) {
|
||||
return $this->baseURI.$path;
|
||||
}
|
||||
|
||||
public function getBuiltinQueryNames() {
|
||||
$names = array(
|
||||
'open' => pht('Open Requests'),
|
||||
'all' => pht('All Requests'),
|
||||
);
|
||||
|
||||
if ($this->requireViewer()->isLoggedIn()) {
|
||||
$names['requested'] = pht('Requested');
|
||||
}
|
||||
|
||||
return $names;
|
||||
}
|
||||
|
||||
public function buildSavedQueryFromBuiltin($query_key) {
|
||||
|
||||
$query = $this->newSavedQuery();
|
||||
$query->setQueryKey($query_key);
|
||||
|
||||
switch ($query_key) {
|
||||
case 'open':
|
||||
return $query->setParameter('status', 'open');
|
||||
case 'all':
|
||||
return $query;
|
||||
case 'requested':
|
||||
return $query->setParameter(
|
||||
'requestorPHIDs',
|
||||
array($this->requireViewer()->getPHID()));
|
||||
}
|
||||
|
||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||
}
|
||||
|
||||
private function getStatusOptions() {
|
||||
return array(
|
||||
'' => pht('(All Requests)'),
|
||||
'open' => pht('Open Requests'),
|
||||
'requested' => pht('Pull Requested'),
|
||||
'needs-pull' => pht('Needs Pull'),
|
||||
'rejected' => pht('Rejected'),
|
||||
'abandoned' => pht('Abandoned'),
|
||||
'pulled' => pht('Pulled'),
|
||||
'needs-revert' => pht('Needs Revert'),
|
||||
'reverted' => pht('Reverted'),
|
||||
);
|
||||
}
|
||||
|
||||
private function getStatusValues() {
|
||||
return array(
|
||||
'open' => ReleephRequestQuery::STATUS_OPEN,
|
||||
'requested' => ReleephRequestQuery::STATUS_REQUESTED,
|
||||
'needs-pull' => ReleephRequestQuery::STATUS_NEEDS_PULL,
|
||||
'rejected' => ReleephRequestQuery::STATUS_REJECTED,
|
||||
'abandoned' => ReleephRequestQuery::STATUS_ABANDONED,
|
||||
'pulled' => ReleephRequestQuery::STATUS_PULLED,
|
||||
'needs-revert' => ReleephRequestQuery::STATUS_NEEDS_REVERT,
|
||||
'reverted' => ReleephRequestQuery::STATUS_REVERTED,
|
||||
);
|
||||
}
|
||||
|
||||
private function getSeverityOptions() {
|
||||
return array(
|
||||
'' => pht('(All Severities)'),
|
||||
ReleephSeverityFieldSpecification::HOTFIX => pht('Hotfix'),
|
||||
ReleephSeverityFieldSpecification::RELEASE => pht('Release'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCountedToggleButtonsExample
|
||||
extends PhabricatorUIExample {
|
||||
|
||||
public function getName() {
|
||||
return 'Counted Toggle Buttons';
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return 'Like AphrontFormToggleButtonsControl, but with counters.';
|
||||
}
|
||||
|
||||
private static function buildTreesList() {
|
||||
$all_trees = array(
|
||||
array(
|
||||
'name' => 'Oak',
|
||||
'leaves' => 'deciduous',
|
||||
'wood' => 'hard',
|
||||
'branches' => 'climbable',
|
||||
),
|
||||
array(
|
||||
'name' => 'Pine',
|
||||
'leaves' => 'coniferous',
|
||||
'wood' => 'soft',
|
||||
'branches' => 'spindly',
|
||||
),
|
||||
array(
|
||||
'name' => 'Spruce',
|
||||
'leaves' => 'coniferous',
|
||||
'wood' => 'soft',
|
||||
'branches' => 'sticky',
|
||||
),
|
||||
array(
|
||||
'name' => 'Ash',
|
||||
'leaves' => 'deciduous',
|
||||
'wood' => 'hard',
|
||||
'branches' => 'climbable',
|
||||
),
|
||||
array(
|
||||
'name' => 'Holly',
|
||||
'leaves' => 'waxy',
|
||||
'wood' => 'hard',
|
||||
'branches' => 'prickly',
|
||||
),
|
||||
);
|
||||
|
||||
for ($ii = 0; $ii < 345; $ii++) {
|
||||
$name = sprintf("Soylent UltraTree \xE2\x84\xA2 Mutation 0xPD%03x", $ii);
|
||||
$all_trees[] = array(
|
||||
'name' => $name,
|
||||
'leaves' => 'carcinogenic',
|
||||
'wood' => 'metallic',
|
||||
'branches' => 'sentient',
|
||||
);
|
||||
}
|
||||
|
||||
return $all_trees;
|
||||
}
|
||||
|
||||
public function renderExample() {
|
||||
$request = $this->getRequest();
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($request->getUser());
|
||||
|
||||
$attributes = array('leaves', 'wood', 'branches');
|
||||
|
||||
$all_trees = self::buildTreesList();
|
||||
$trees = $all_trees;
|
||||
|
||||
foreach ($attributes as $attribute) {
|
||||
$form_value = $request->getStr($attribute);
|
||||
|
||||
$buttons = array(null => 'all');
|
||||
foreach ($all_trees as $dict) {
|
||||
$value = $dict[$attribute];
|
||||
$buttons[$value] = $value;
|
||||
}
|
||||
|
||||
// The trees filtered by other attributes, before we filter by this
|
||||
// attribute
|
||||
$trees_before = $all_trees;
|
||||
foreach ($attributes as $other_attribute) {
|
||||
if ($other_attribute != $attribute) {
|
||||
$trees_before = $this->filterTrees($trees_before, $other_attribute);
|
||||
}
|
||||
}
|
||||
|
||||
$counters = array(null => count($trees_before));
|
||||
foreach ($trees_before as $dict) {
|
||||
$value = $dict[$attribute];
|
||||
if (!isset($counters[$value])) {
|
||||
$counters[$value] = 0;
|
||||
}
|
||||
$counters[$value]++;
|
||||
}
|
||||
|
||||
$trees = $this->filterTrees($trees, $attribute);
|
||||
|
||||
$control = id(new AphrontFormCountedToggleButtonsControl())
|
||||
->setLabel(ucfirst($attribute))
|
||||
->setName($attribute)
|
||||
->setValue($form_value)
|
||||
->setBaseURI($request->getRequestURI(), $attribute)
|
||||
->setButtons($buttons)
|
||||
->setCounters($counters);
|
||||
|
||||
$form->appendChild($control);
|
||||
}
|
||||
|
||||
$rows = array();
|
||||
foreach ($trees as $dict) {
|
||||
$row = array_select_keys($dict, $attributes);
|
||||
array_unshift($row, $dict['name']);
|
||||
$rows[] = $row;
|
||||
}
|
||||
|
||||
$headers = $attributes;
|
||||
array_unshift($headers, 'name');
|
||||
$table = id(new AphrontTableView($rows))
|
||||
->setHeaders($headers);
|
||||
|
||||
$panel = id(new AphrontPanelView())
|
||||
->setHeader('Counters!')
|
||||
->setWidth(AphrontPanelView::WIDTH_FULL)
|
||||
->appendChild($form)
|
||||
->appendChild($table);
|
||||
|
||||
return $panel;
|
||||
}
|
||||
|
||||
private function filterTrees($trees, $attribute) {
|
||||
$form_value = $this->getRequest()->getStr($attribute);
|
||||
if (!$form_value) {
|
||||
return $trees;
|
||||
}
|
||||
|
||||
$new = array();
|
||||
foreach ($trees as $dict) {
|
||||
if ($dict[$attribute] == $form_value) {
|
||||
$new[] = $dict;
|
||||
}
|
||||
}
|
||||
return $new;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,80 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class AphrontFormCountedToggleButtonsControl extends AphrontFormControl {
|
||||
|
||||
private $baseURI;
|
||||
private $param;
|
||||
|
||||
private $buttons;
|
||||
private $counters = array();
|
||||
|
||||
public function setBaseURI(PhutilURI $uri, $param) {
|
||||
$this->baseURI = $uri;
|
||||
$this->param = $param;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setButtons(array $buttons) {
|
||||
$this->buttons = $buttons;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setCounters(array $counters) {
|
||||
$this->counters = $counters;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getCustomControlClass() {
|
||||
return 'aphront-form-control-counted-togglebuttons';
|
||||
}
|
||||
|
||||
protected function renderInput() {
|
||||
if (!$this->baseURI) {
|
||||
throw new Exception('Call setBaseURI() before render()!');
|
||||
}
|
||||
|
||||
$selected = $this->getValue();
|
||||
|
||||
$out = array();
|
||||
foreach ($this->buttons as $value => $label) {
|
||||
if ($value == $selected) {
|
||||
$more = ' toggle-selected toggle-fixed';
|
||||
} else {
|
||||
$more = null;
|
||||
}
|
||||
|
||||
$counter = idx($this->counters, $value);
|
||||
|
||||
if ($counter > 0) {
|
||||
$href = $this->baseURI->alter($this->param, $value);
|
||||
$counter_markup = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'counter',
|
||||
),
|
||||
$counter);
|
||||
} else {
|
||||
$href = null;
|
||||
$counter_markup = '';
|
||||
$more .= ' disabled';
|
||||
}
|
||||
|
||||
$attributes = array(
|
||||
'class' => 'toggle'.$more,
|
||||
);
|
||||
if ($href) {
|
||||
$attributes['href'] = $href;
|
||||
}
|
||||
|
||||
$out[] = phutil_tag(
|
||||
'a',
|
||||
$attributes,
|
||||
array(
|
||||
$counter_markup,
|
||||
$label));
|
||||
}
|
||||
|
||||
return $out;
|
||||
}
|
||||
|
||||
}
|
|
@ -413,45 +413,6 @@ table.aphront-form-control-checkbox-layout th {
|
|||
float: right;
|
||||
}
|
||||
|
||||
.aphront-form-control-counted-togglebuttons {
|
||||
padding-top: 7px;
|
||||
}
|
||||
|
||||
.aphront-form-control-counted-togglebuttons .toggle {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.aphront-form-control-counted-togglebuttons .toggle-fixed {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.aphront-form-control-counted-togglebuttons .toggle .counter {
|
||||
font-size: smaller;
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: -9px;
|
||||
right: -8px;
|
||||
padding: 0px 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.aphront-form-control-counted-togglebuttons:hover .toggle .counter {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.aphront-form-control-counted-togglebuttons .toggle .counter {
|
||||
background: gray;
|
||||
color: #ddd;
|
||||
}
|
||||
|
||||
.aphront-form-control-counted-togglebuttons .toggle-selected .counter {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.aphront-form-control-counted-togglebuttons .toggle.disabled:hover {
|
||||
background-color: #a7a7a7;
|
||||
}
|
||||
|
||||
.phui-form-divider hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
|
|
Loading…
Reference in a new issue