Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14829357
D9211.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
16 KB
Referenced Files
None
Subscribers
None
D9211.diff
View Options
diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -407,6 +407,7 @@
'rsrc/js/application/policy/behavior-policy-control.js' => '71b4cbcc',
'rsrc/js/application/policy/behavior-policy-rule-editor.js' => '263aeb8c',
'rsrc/js/application/ponder/behavior-votebox.js' => '327dbe61',
+ 'rsrc/js/application/projects/behavior-boards-filter.js' => '22f113af',
'rsrc/js/application/projects/behavior-project-boards.js' => 'd8e135db',
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
'rsrc/js/application/releeph/releeph-preview-branch.js' => '9eb2cedb',
@@ -544,6 +545,7 @@
'javelin-behavior-audio-source' => '59b251eb',
'javelin-behavior-audit-preview' => 'be81801d',
'javelin-behavior-balanced-payment-form' => '3b3e1664',
+ 'javelin-behavior-boards-filter' => '22f113af',
'javelin-behavior-config-reorder-fields' => '938aed89',
'javelin-behavior-conpherence-menu' => '7ee23816',
'javelin-behavior-conpherence-pontificate' => '53f6f2dd',
@@ -1004,6 +1006,13 @@
5 => 'javelin-json',
6 => 'phabricator-prefab',
),
+ '22f113af' =>
+ array(
+ 0 => 'javelin-behavior',
+ 1 => 'javelin-dom',
+ 2 => 'javelin-stratcom',
+ 3 => 'phuix-dropdown-menu',
+ ),
'263aeb8c' =>
array(
0 => 'javelin-behavior',
diff --git a/src/applications/maniphest/query/ManiphestTaskQuery.php b/src/applications/maniphest/query/ManiphestTaskQuery.php
--- a/src/applications/maniphest/query/ManiphestTaskQuery.php
+++ b/src/applications/maniphest/query/ManiphestTaskQuery.php
@@ -95,6 +95,22 @@
return $this;
}
+ /**
+ * Add an additional "all projects" constraint to existing filters.
+ *
+ * This is used by boards to supplement queries.
+ *
+ * @param list<phid> List of project PHIDs to add to any existing constriant.
+ * @return this
+ */
+ public function addWithAllProjects(array $projects) {
+ if ($this->projectPHIDs === null) {
+ $this->projectPHIDs = array();
+ }
+
+ return $this->withAllProjects(array_merge($this->projectPHIDs, $projects));
+ }
+
public function withoutProjects(array $projects) {
$this->xprojectPHIDs = $projects;
return $this;
diff --git a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
--- a/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
+++ b/src/applications/maniphest/query/ManiphestTaskSearchEngine.php
@@ -4,6 +4,26 @@
extends PhabricatorApplicationSearchEngine {
private $showBatchControls;
+ private $baseURI;
+ private $isBoardView;
+
+ public function setIsBoardView($is_board_view) {
+ $this->isBoardView = $is_board_view;
+ return $this;
+ }
+
+ public function getIsBoardView() {
+ return $this->isBoardView;
+ }
+
+ public function setBaseURI($base_uri) {
+ $this->baseURI = $base_uri;
+ return $this;
+ }
+
+ public function getBaseURI() {
+ return $this->baseURI;
+ }
public function setShowBatchControls($show_batch_controls) {
$this->showBatchControls = $show_batch_controls;
@@ -301,14 +321,20 @@
->setDatasource('/typeahead/common/projects/')
->setName('allProjects')
->setLabel(pht('In All Projects'))
- ->setValue($all_project_handles))
- ->appendChild(
- id(new AphrontFormCheckboxControl())
- ->addCheckbox(
- 'withNoProject',
- 1,
- pht('Show only tasks with no projects.'),
- $with_no_projects))
+ ->setValue($all_project_handles));
+
+ if (!$this->getIsBoardView()) {
+ $form
+ ->appendChild(
+ id(new AphrontFormCheckboxControl())
+ ->addCheckbox(
+ 'withNoProject',
+ 1,
+ pht('Show only tasks with no projects.'),
+ $with_no_projects));
+ }
+
+ $form
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource('/typeahead/common/projects/')
@@ -340,19 +366,25 @@
->setLabel(pht('Subscribers'))
->setValue($subscriber_handles))
->appendChild($status_control)
- ->appendChild($priority_control)
- ->appendChild(
- id(new AphrontFormSelectControl())
- ->setName('group')
- ->setLabel(pht('Group By'))
- ->setValue($saved->getParameter('group'))
- ->setOptions($this->getGroupOptions()))
- ->appendChild(
- id(new AphrontFormSelectControl())
- ->setName('order')
- ->setLabel(pht('Order By'))
- ->setValue($saved->getParameter('order'))
- ->setOptions($this->getOrderOptions()))
+ ->appendChild($priority_control);
+
+ if (!$this->getIsBoardView()) {
+ $form
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setName('group')
+ ->setLabel(pht('Group By'))
+ ->setValue($saved->getParameter('group'))
+ ->setOptions($this->getGroupOptions()))
+ ->appendChild(
+ id(new AphrontFormSelectControl())
+ ->setName('order')
+ ->setLabel(pht('Order By'))
+ ->setValue($saved->getParameter('order'))
+ ->setOptions($this->getOrderOptions()));
+ }
+
+ $form
->appendChild(
id(new AphrontFormTextControl())
->setName('fulltext')
@@ -382,15 +414,20 @@
'modifiedEnd',
pht('Updated Before'));
- $form
- ->appendChild(
- id(new AphrontFormTextControl())
- ->setName('limit')
- ->setLabel(pht('Page Size'))
- ->setValue($saved->getParameter('limit', 100)));
+ if (!$this->getIsBoardView()) {
+ $form
+ ->appendChild(
+ id(new AphrontFormTextControl())
+ ->setName('limit')
+ ->setLabel(pht('Page Size'))
+ ->setValue($saved->getParameter('limit', 100)));
+ }
}
protected function getURI($path) {
+ if ($this->baseURI) {
+ return $this->baseURI.$path;
+ }
return '/maniphest/'.$path;
}
diff --git a/src/applications/project/application/PhabricatorApplicationProject.php b/src/applications/project/application/PhabricatorApplicationProject.php
--- a/src/applications/project/application/PhabricatorApplicationProject.php
+++ b/src/applications/project/application/PhabricatorApplicationProject.php
@@ -51,7 +51,10 @@
'picture/(?P<id>[1-9]\d*)/' =>
'PhabricatorProjectEditPictureController',
'create/' => 'PhabricatorProjectCreateController',
- 'board/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectBoardViewController',
+ 'board/(?P<id>[1-9]\d*)/'.
+ '(?P<filter>filter/)?'.
+ '(?:query/(?P<queryKey>[^/]+)/)?' =>
+ 'PhabricatorProjectBoardViewController',
'move/(?P<id>[1-9]\d*)/' => 'PhabricatorProjectMoveController',
'board/(?P<projectID>[1-9]\d*)/edit/(?:(?P<id>\d+)/)?'
=> 'PhabricatorProjectBoardEditController',
diff --git a/src/applications/project/controller/PhabricatorProjectBoardViewController.php b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
--- a/src/applications/project/controller/PhabricatorProjectBoardViewController.php
+++ b/src/applications/project/controller/PhabricatorProjectBoardViewController.php
@@ -5,6 +5,8 @@
private $id;
private $handles;
+ private $queryKey;
+ private $filter;
public function shouldAllowPublic() {
return true;
@@ -12,6 +14,8 @@
public function willProcessRequest(array $data) {
$this->id = $data['id'];
+ $this->queryKey = idx($data, 'queryKey');
+ $this->filter = (bool)idx($data, 'filter');
}
public function processRequest() {
@@ -50,12 +54,63 @@
ksort($columns);
- $tasks = id(new ManiphestTaskQuery())
+ $board_uri = $this->getApplicationURI('board/'.$project->getID().'/');
+
+ $engine = id(new ManiphestTaskSearchEngine())
->setViewer($viewer)
- ->withAllProjects(array($project->getPHID()))
- ->withStatuses(ManiphestTaskStatus::getOpenStatusConstants())
+ ->setBaseURI($board_uri)
+ ->setIsBoardView(true);
+
+ if ($request->isFormPost()) {
+ $saved = $engine->buildSavedQueryFromRequest($request);
+ $engine->saveQuery($saved);
+ return id(new AphrontRedirectResponse())->setURI(
+ $engine->getQueryResultsPageURI($saved->getQueryKey()));
+ }
+
+ $query_key = $this->queryKey;
+ if (!$query_key) {
+ $query_key = 'open';
+ }
+
+ $custom_query = null;
+ if ($engine->isBuiltinQuery($query_key)) {
+ $saved = $engine->buildSavedQueryFromBuiltin($query_key);
+ } else {
+ $saved = id(new PhabricatorSavedQueryQuery())
+ ->setViewer($viewer)
+ ->withQueryKeys(array($query_key))
+ ->executeOne();
+
+ if (!$saved) {
+ return new Aphront404Response();
+ }
+
+ $custom_query = $saved;
+ }
+
+ if ($this->filter) {
+ $filter_form = id(new AphrontFormView())
+ ->setUser($viewer);
+ $engine->buildSearchForm($filter_form, $saved);
+
+ return $this->newDialog()
+ ->setWidth(AphrontDialogView::WIDTH_FULL)
+ ->setTitle(pht('Advanced Filter'))
+ ->appendChild($filter_form->buildLayoutView())
+ ->setSubmitURI($board_uri)
+ ->addSubmitButton(pht('Apply Filter'))
+ ->addCancelButton($board_uri);
+ }
+
+ $task_query = $engine->buildQueryFromSavedQuery($saved);
+
+ $tasks = $task_query
+ ->addWithAllProjects(array($project->getPHID()))
->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY)
+ ->setViewer($viewer)
->execute();
+
$tasks = mpull($tasks, null, 'getPHID');
$task_phids = array_keys($tasks);
@@ -166,6 +221,87 @@
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit);
+ Javelin::initBehavior(
+ 'boards-filter',
+ array(
+ ));
+
+ $filter_icon = id(new PHUIIconView())
+ ->setIconFont('fa-search-plus bluegrey');
+
+ $named = array(
+ 'open' => pht('Open Tasks'),
+ 'all' => pht('All Tasks'),
+ );
+
+ if ($viewer->isLoggedIn()) {
+ $named['assigned'] = pht('Assigned to Me');
+ }
+
+ if ($custom_query) {
+ $named[$custom_query->getQueryKey()] = pht('Custom Filter');
+ }
+
+ $items = array();
+ foreach ($named as $key => $name) {
+ $is_selected = ($key == $query_key);
+ if ($is_selected) {
+ $active_filter = $name;
+ }
+
+ $is_custom = false;
+ if ($custom_query) {
+ $is_custom = ($key == $custom_query->getQueryKey());
+ }
+
+ $item = id(new PhabricatorActionView())
+ ->setIcon('fa-search')
+ ->setSelected($is_selected)
+ ->setName($name);
+
+ if ($is_custom) {
+ $item->setHref(
+ $this->getApplicationURI(
+ 'board/'.$this->id.'/filter/query/'.$key.'/'));
+ $item->setWorkflow(true);
+ } else {
+ $item->setHref($engine->getQueryResultsPageURI($key));
+ }
+
+ $items[] = $item;
+ }
+
+ $items[] = id(new PhabricatorActionView())
+ ->setIcon('fa-cog')
+ ->setHref($this->getApplicationURI('board/'.$this->id.'/filter/'))
+ ->setWorkflow(true)
+ ->setName(pht('Advanced Filter...'));
+
+
+
+ $filter_menu = id(new PhabricatorActionListView())
+ ->setUser($viewer);
+ foreach ($items as $item) {
+ $filter_menu->addAction($item);
+ }
+
+ $filter_button = id(new PHUIButtonView())
+ ->setText(pht('Filter: %s', $active_filter))
+ ->setIcon($filter_icon)
+ ->setTag('a')
+ ->setHref('#')
+ ->addSigil('boards-filter-menu')
+
+/*
+ TODO: @chad, this looks really gnarly right now, at least in Safari.
+ ->setDropdown(true)
+*/
+
+ ->setMetadata(
+ array(
+ 'items' => hsprintf('%s', $filter_menu),
+ ));
+
$header_link = phutil_tag(
'a',
array(
@@ -179,6 +315,7 @@
->setNoBackground(true)
->setImage($project->getProfileImageURI())
->setImageURL($this->getApplicationURI('view/'.$project->getID().'/'))
+ ->addActionLink($filter_button)
->addActionLink($add_button)
->setPolicyObject($project);
diff --git a/src/applications/project/view/ProjectBoardTaskCard.php b/src/applications/project/view/ProjectBoardTaskCard.php
--- a/src/applications/project/view/ProjectBoardTaskCard.php
+++ b/src/applications/project/view/ProjectBoardTaskCard.php
@@ -53,6 +53,7 @@
->setGrippable($can_edit)
->setHref('/T'.$task->getID())
->addSigil('project-card')
+ ->setDisabled($task->isClosed())
->setMetadata(
array(
'objectPHID' => $task->getPHID(),
diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php
--- a/src/applications/search/controller/PhabricatorApplicationSearchController.php
+++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php
@@ -90,7 +90,7 @@
if ($request->isFormPost()) {
$saved_query = $engine->buildSavedQueryFromRequest($request);
- $this->saveQuery($saved_query);
+ $engine->saveQuery($saved_query);
return id(new AphrontRedirectResponse())->setURI(
$engine->getQueryResultsPageURI($saved_query->getQueryKey()).'#R');
}
@@ -145,7 +145,7 @@
// Save the query to generate a query key, so "Save Custom Query..." and
// other features like Maniphest's "Export..." work correctly.
- $this->saveQuery($saved_query);
+ $engine->saveQuery($saved_query);
}
$nav->selectFilter(
@@ -353,18 +353,6 @@
));
}
- private function saveQuery(PhabricatorSavedQuery $query) {
- $query->setEngineClassName(get_class($this->getSearchEngine()));
-
- $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
- try {
- $query->save();
- } catch (AphrontQueryDuplicateKeyException $ex) {
- // Ignore, this is just a repeated search.
- }
- unset($unguarded);
- }
-
protected function buildApplicationMenu() {
return $this->getDelegatingController()->buildApplicationMenu();
}
diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
--- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
+++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php
@@ -35,6 +35,18 @@
return $this->viewer;
}
+ public function saveQuery(PhabricatorSavedQuery $query) {
+ $query->setEngineClassName(get_class($this));
+
+ $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
+ try {
+ $query->save();
+ } catch (AphrontQueryDuplicateKeyException $ex) {
+ // Ignore, this is just a repeated search.
+ }
+ unset($unguarded);
+ }
+
/**
* Create a saved query object from the request.
*
diff --git a/src/view/layout/PhabricatorActionView.php b/src/view/layout/PhabricatorActionView.php
--- a/src/view/layout/PhabricatorActionView.php
+++ b/src/view/layout/PhabricatorActionView.php
@@ -12,6 +12,16 @@
private $objectURI;
private $sigils = array();
private $metadata;
+ private $selected;
+
+ public function setSelected($selected) {
+ $this->selected = $selected;
+ return $this;
+ }
+
+ public function getSelected() {
+ return $this->selected;
+ }
public function setMetadata($metadata) {
$this->metadata = $metadata;
@@ -167,6 +177,10 @@
$classes[] = 'phabricator-action-view-disabled';
}
+ if ($this->selected) {
+ $classes[] = 'phabricator-action-view-selected';
+ }
+
return phutil_tag(
'li',
array(
diff --git a/webroot/rsrc/js/application/projects/behavior-boards-filter.js b/webroot/rsrc/js/application/projects/behavior-boards-filter.js
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/js/application/projects/behavior-boards-filter.js
@@ -0,0 +1,35 @@
+/**
+ * @provides javelin-behavior-boards-filter
+ * @requires javelin-behavior
+ * javelin-dom
+ * javelin-stratcom
+ * phuix-dropdown-menu
+ */
+
+JX.behavior('boards-filter', function(config) {
+
+ JX.Stratcom.listen('click', 'boards-filter-menu', function(e) {
+ var data = e.getNodeData('boards-filter-menu');
+ if (data.menu) {
+ return;
+ }
+
+ e.kill();
+
+ var list = JX.$H(data.items).getFragment().firstChild;
+
+ var button = e.getNode('boards-filter-menu');
+ data.menu = new JX.PHUIXDropdownMenu(button);
+ data.menu.setContent(list);
+ data.menu.open();
+
+ JX.DOM.listen(list, 'click', 'tag:a', function(e) {
+ if (!e.isNormalClick()) {
+ return;
+ }
+ data.menu.close();
+ });
+ });
+
+
+});
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Jan 30, 4:06 PM (2 h, 58 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7074218
Default Alt Text
D9211.diff (16 KB)
Attached To
Mode
D9211: Allow workboards to be filtered with ApplicationSearch
Attached
Detach File
Event Timeline
Log In to Comment