From 6be53bd9160807fb7c829b72b23025d233f7072c Mon Sep 17 00:00:00 2001 From: Chad Little Date: Tue, 21 Jul 2015 12:01:19 -0700 Subject: [PATCH] Add Projects to Dashboards and Panels Summary: Making an attempt here. This adds the ability to set Projects on Dashboards and Dashboard Panels. Most of this went smooth, but I can't figure out why the queries don't automatically show searching by Projects. I'm stumped. Rest seems fine. Test Plan: Assign a Project to a Dashboard and a Panel, see Project show up, edit it, see transactions. Reviewers: btrahan, epriestley Reviewed By: epriestley Subscribers: epriestley, Korvin Differential Revision: https://secure.phabricator.com/D13656 --- src/__phutil_library_map__.php | 2 + .../PhabricatorDashboardEditController.php | 26 +++++- .../PhabricatorDashboardManageController.php | 2 + ...habricatorDashboardPanelEditController.php | 19 +++++ .../query/PhabricatorDashboardPanelQuery.php | 41 +++++---- .../PhabricatorDashboardPanelSearchEngine.php | 83 ++++++++----------- .../query/PhabricatorDashboardQuery.php | 58 ++++++++----- .../PhabricatorDashboardSearchEngine.php | 44 +++++++--- .../storage/PhabricatorDashboard.php | 14 +++- .../storage/PhabricatorDashboardPanel.php | 19 +++++ 10 files changed, 200 insertions(+), 108 deletions(-) diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 0459033891..d9531b1ce2 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -5622,6 +5622,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorFlaggableInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorProjectInterface', ), 'PhabricatorDashboardAddPanelController' => 'PhabricatorDashboardController', 'PhabricatorDashboardApplication' => 'PhabricatorApplication', @@ -5644,6 +5645,7 @@ phutil_register_library_map(array( 'PhabricatorPolicyInterface', 'PhabricatorCustomFieldInterface', 'PhabricatorFlaggableInterface', + 'PhabricatorProjectInterface', 'PhabricatorDestructibleInterface', ), 'PhabricatorDashboardPanelArchiveController' => 'PhabricatorDashboardController', diff --git a/src/applications/dashboard/controller/PhabricatorDashboardEditController.php b/src/applications/dashboard/controller/PhabricatorDashboardEditController.php index 26ddd2870a..9838728bf1 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardEditController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardEditController.php @@ -27,7 +27,10 @@ final class PhabricatorDashboardEditController if (!$dashboard) { return new Aphront404Response(); } - + $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( + $dashboard->getPHID(), + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); + $v_projects = array_reverse($v_projects); $is_new = false; } else { if (!$request->getStr('edit')) { @@ -44,7 +47,7 @@ final class PhabricatorDashboardEditController } $dashboard = PhabricatorDashboard::initializeNewDashboard($viewer); - + $v_projects = array(); $is_new = true; } @@ -79,6 +82,7 @@ final class PhabricatorDashboardEditController $v_layout_mode = $request->getStr('layout_mode'); $v_view_policy = $request->getStr('viewPolicy'); $v_edit_policy = $request->getStr('editPolicy'); + $v_projects = $request->getArr('projects'); $xactions = array(); @@ -100,6 +104,12 @@ final class PhabricatorDashboardEditController ->setTransactionType($type_edit_policy) ->setNewValue($v_edit_policy); + $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; + $xactions[] = id(new PhabricatorDashboardTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue('edge:type', $proj_edge_type) + ->setNewValue(array('=' => array_fuse($v_projects))); + try { $editor = id(new PhabricatorDashboardTransactionEditor()) ->setActor($viewer) @@ -153,8 +163,16 @@ final class PhabricatorDashboardEditController ->setLabel(pht('Layout Mode')) ->setName('layout_mode') ->setValue($v_layout_mode) - ->setOptions($layout_mode_options)) - ->appendChild( + ->setOptions($layout_mode_options)); + + $form->appendControl( + id(new AphrontFormTokenizerControl()) + ->setLabel(pht('Projects')) + ->setName('projects') + ->setValue($v_projects) + ->setDatasource(new PhabricatorProjectDatasource())); + + $form->appendChild( id(new AphrontFormSubmitControl()) ->setValue($button) ->addCancelButton($cancel_uri)); diff --git a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php index 00c913ae60..58e9c1c20a 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php @@ -170,6 +170,8 @@ final class PhabricatorDashboardManageController pht('Panels'), $viewer->renderHandleList($dashboard->getPanelPHIDs())); + $properties->invokeWillRenderEvent(); + return $properties; } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php index 81c675537f..8ec942a34a 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php @@ -57,6 +57,10 @@ final class PhabricatorDashboardPanelEditController if (!$panel) { return new Aphront404Response(); } + $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( + $panel->getPHID(), + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); + $v_projects = array_reverse($v_projects); if ($dashboard) { $can_edit = PhabricatorPolicyFilter::hasCapability( @@ -86,6 +90,7 @@ final class PhabricatorDashboardPanelEditController if (empty($types[$type])) { return $this->processPanelTypeRequest($request); } + $v_projects = array(); $panel->setPanelType($type); } @@ -136,6 +141,7 @@ final class PhabricatorDashboardPanelEditController $v_name = $request->getStr('name'); $v_view_policy = $request->getStr('viewPolicy'); $v_edit_policy = $request->getStr('editPolicy'); + $v_projects = $request->getArr('projects'); $type_name = PhabricatorDashboardPanelTransaction::TYPE_NAME; $type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY; @@ -155,6 +161,12 @@ final class PhabricatorDashboardPanelEditController ->setTransactionType($type_edit_policy) ->setNewValue($v_edit_policy); + $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; + $xactions[] = id(new PhabricatorDashboardPanelTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue('edge:type', $proj_edge_type) + ->setNewValue(array('=' => array_fuse($v_projects))); + $field_xactions = $field_list->buildFieldTransactionsFromRequest( new PhabricatorDashboardPanelTransaction(), $request); @@ -232,6 +244,13 @@ final class PhabricatorDashboardPanelEditController ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) ->setPolicies($policies)); + $form->appendControl( + id(new AphrontFormTokenizerControl()) + ->setLabel(pht('Projects')) + ->setName('projects') + ->setValue($v_projects) + ->setDatasource(new PhabricatorProjectDatasource())); + $field_list->appendFieldsToForm($form); $crumbs = $this->buildApplicationCrumbs(); diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php index a9a062ffb2..3a6589a32c 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -29,54 +29,53 @@ final class PhabricatorDashboardPanelQuery } protected function loadPage() { - $table = new PhabricatorDashboardPanel(); - $conn_r = $table->establishConnection('r'); - - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + return $this->loadStandardPage($this->newResultObject()); } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + public function newResultObject() { + // TODO: If we don't do this, SearchEngine explodes when trying to + // enumerate custom fields. For now, just give the panel a default panel + // type so custom fields work. In the long run, we may want to find a + // cleaner or more general approach for this. + $text_type = id(new PhabricatorDashboardTextPanelType()) + ->getPanelTypeKey(); + + return id(new PhabricatorDashboardPanel()) + ->setPanelType($text_type); + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } if ($this->archived !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'isArchived = %d', (int)$this->archived); } if ($this->panelTypes !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'panelType IN (%Ls)', $this->panelTypes); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } public function getQueryApplicationClass() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php index 6dd2a5cab3..113ed8ff94 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php @@ -11,66 +11,48 @@ final class PhabricatorDashboardPanelSearchEngine return 'PhabricatorDashboardApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - $saved = new PhabricatorSavedQuery(); - $saved->setParameter('status', $request->getStr('status')); - $saved->setParameter('paneltype', $request->getStr('paneltype')); - return $saved; + public function newQuery() { + return new PhabricatorDashboardPanelQuery(); } - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - $query = id(new PhabricatorDashboardPanelQuery()); - - $status = $saved->getParameter('status'); - switch ($status) { - case 'active': - $query->withArchived(false); - break; - case 'archived': - $query->withArchived(true); - break; - default: - break; + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + if ($map['status']) { + switch ($map['status']) { + case 'active': + $query->withArchived(false); + break; + case 'archived': + $query->withArchived(true); + break; + default: + break; + } } - $paneltype = $saved->getParameter('paneltype'); - if ($paneltype) { - $query->withPanelTypes(array($paneltype)); + if ($map['paneltype']) { + $query->withPanelTypes(array($map['paneltype'])); } return $query; } - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { + protected function buildCustomSearchFields() { - $status = $saved_query->getParameter('status', ''); - $paneltype = $saved_query->getParameter('paneltype', ''); - - $panel_types = PhabricatorDashboardPanelType::getAllPanelTypes(); - $panel_types = mpull($panel_types, 'getPanelTypeName', 'getPanelTypeKey'); - asort($panel_types); - $panel_types = (array('' => pht('(All Types)')) + $panel_types); - - $form - ->appendChild( - id(new AphrontFormSelectControl()) + return array( + id(new PhabricatorSearchSelectField()) + ->setKey('status') ->setLabel(pht('Status')) - ->setName('status') - ->setValue($status) ->setOptions( - array( - '' => pht('(All Panels)'), - 'active' => pht('Active Panels'), - 'archived' => pht('Archived Panels'), - ))) - ->appendChild( - id(new AphrontFormSelectControl()) + id(new PhabricatorDashboardPanel()) + ->getStatuses()), + id(new PhabricatorSearchSelectField()) + ->setKey('paneltype') ->setLabel(pht('Panel Type')) - ->setName('paneltype') - ->setValue($paneltype) - ->setOptions($panel_types)); + ->setOptions( + id(new PhabricatorDashboardPanel()) + ->getPanelTypes()), + ); } protected function getURI($path) { @@ -117,13 +99,14 @@ final class PhabricatorDashboardPanelSearchEngine $impl = $panel->getImplementation(); if ($impl) { $type_text = $impl->getPanelTypeName(); - $type_icon = 'none'; } else { $type_text = nonempty($panel->getPanelType(), pht('Unknown Type')); - $type_icon = 'fa-question'; } + $item->addAttribute($type_text); - $item->addIcon($type_icon, $type_text); + $properties = $panel->getProperties(); + $class = idx($properties, 'class'); + $item->addAttribute($class); if ($panel->getIsArchived()) { $item->setDisabled(true); diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php index 324151e722..f1fe0047db 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php @@ -7,6 +7,7 @@ final class PhabricatorDashboardQuery private $phids; private $needPanels; + private $needProjects; public function withIDs(array $ids) { $this->ids = $ids; @@ -23,25 +24,26 @@ final class PhabricatorDashboardQuery return $this; } + public function needProjects($need_projects) { + $this->needProjects = $need_projects; + return $this; + } + protected function loadPage() { - $table = new PhabricatorDashboard(); - $conn_r = $table->establishConnection('r'); + return $this->loadStandardPage($this->newResultObject()); + } - $data = queryfx_all( - $conn_r, - 'SELECT * FROM %T %Q %Q %Q', - $table->getTableName(), - $this->buildWhereClause($conn_r), - $this->buildOrderClause($conn_r), - $this->buildLimitClause($conn_r)); - - return $table->loadAllFromArray($data); + public function newResultObject() { + return new PhabricatorDashboard(); } protected function didFilterPage(array $dashboards) { + + $phids = mpull($dashboards, 'getPHID'); + if ($this->needPanels) { $edge_query = id(new PhabricatorEdgeQuery()) - ->withSourcePHIDs(mpull($dashboards, 'getPHID')) + ->withSourcePHIDs($phids) ->withEdgeTypes( array( PhabricatorDashboardDashboardHasPanelEdgeType::EDGECONST, @@ -70,29 +72,43 @@ final class PhabricatorDashboardQuery } } + if ($this->needProjects) { + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs($phids) + ->withEdgeTypes( + array( + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, + )); + $edge_query->execute(); + + foreach ($dashboards as $dashboard) { + $project_phids = $edge_query->getDestinationPHIDs( + array($dashboard->getPHID())); + $dashboard->attachProjectPHIDs($project_phids); + } + } + return $dashboards; } - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { - $where = array(); + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); - if ($this->ids) { + if ($this->ids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'id IN (%Ld)', $this->ids); } - if ($this->phids) { + if ($this->phids !== null) { $where[] = qsprintf( - $conn_r, + $conn, 'phid IN (%Ls)', $this->phids); } - $where[] = $this->buildPagingClause($conn_r); - - return $this->formatWhereClause($where); + return $where; } public function getQueryApplicationClass() { diff --git a/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php index c09661423e..6aa0b43c8d 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php +++ b/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php @@ -11,18 +11,13 @@ final class PhabricatorDashboardSearchEngine return 'PhabricatorDashboardApplication'; } - public function buildSavedQueryFromRequest(AphrontRequest $request) { - return new PhabricatorSavedQuery(); + public function newQuery() { + return id(new PhabricatorDashboardQuery()) + ->needProjects(true); } - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { - return new PhabricatorDashboardQuery(); - } - - public function buildSearchForm( - AphrontFormView $form, - PhabricatorSavedQuery $saved_query) { - return; + protected function buildCustomSearchFields() { + return array(); } protected function getURI($path) { @@ -48,6 +43,11 @@ final class PhabricatorDashboardSearchEngine return parent::buildSavedQueryFromBuiltin($query_key); } + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + return $query; + } + protected function renderResultList( array $dashboards, PhabricatorSavedQuery $query, @@ -70,6 +70,18 @@ final class PhabricatorDashboardSearchEngine $installs = array(); } + $proj_phids = array(); + foreach ($dashboards as $dashboard) { + foreach ($dashboard->getProjectPHIDs() as $project_phid) { + $proj_phids[] = $project_phid; + } + } + + $proj_handles = id(new PhabricatorHandleQuery()) + ->setViewer($viewer) + ->withPHIDs($proj_phids) + ->execute(); + $list = new PHUIObjectItemListView(); $list->setUser($viewer); $list->initBehavior('phabricator-tooltips', array()); @@ -101,6 +113,17 @@ final class PhabricatorDashboardSearchEngine } } + $project_handles = array_select_keys( + $proj_handles, + $dashboard->getProjectPHIDs()); + + $item->addAttribute( + id(new PHUIHandleTagListView()) + ->setLimit(4) + ->setNoDataString(pht('No Projects')) + ->setSlim(true) + ->setHandles($project_handles)); + $list->addItem($item); } @@ -109,7 +132,6 @@ final class PhabricatorDashboardSearchEngine $result->setNoDataString(pht('No dashboards found.')); return $result; - } } diff --git a/src/applications/dashboard/storage/PhabricatorDashboard.php b/src/applications/dashboard/storage/PhabricatorDashboard.php index 343da48351..df921b4414 100644 --- a/src/applications/dashboard/storage/PhabricatorDashboard.php +++ b/src/applications/dashboard/storage/PhabricatorDashboard.php @@ -8,7 +8,8 @@ final class PhabricatorDashboard extends PhabricatorDashboardDAO PhabricatorApplicationTransactionInterface, PhabricatorPolicyInterface, PhabricatorFlaggableInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorProjectInterface { protected $name; protected $viewPolicy; @@ -17,6 +18,8 @@ final class PhabricatorDashboard extends PhabricatorDashboardDAO private $panelPHIDs = self::ATTACHABLE; private $panels = self::ATTACHABLE; + private $edgeProjectPHIDs = self::ATTACHABLE; + public static function initializeNewDashboard(PhabricatorUser $actor) { return id(new PhabricatorDashboard()) @@ -65,6 +68,15 @@ final class PhabricatorDashboard extends PhabricatorDashboardDAO return $this; } + public function getProjectPHIDs() { + return $this->assertAttached($this->edgeProjectPHIDs); + } + + public function attachProjectPHIDs(array $phids) { + $this->edgeProjectPHIDs = $phids; + return $this; + } + public function attachPanelPHIDs(array $phids) { $this->panelPHIDs = $phids; return $this; diff --git a/src/applications/dashboard/storage/PhabricatorDashboardPanel.php b/src/applications/dashboard/storage/PhabricatorDashboardPanel.php index 7c1b652c15..4e69fba636 100644 --- a/src/applications/dashboard/storage/PhabricatorDashboardPanel.php +++ b/src/applications/dashboard/storage/PhabricatorDashboardPanel.php @@ -10,6 +10,7 @@ final class PhabricatorDashboardPanel PhabricatorPolicyInterface, PhabricatorCustomFieldInterface, PhabricatorFlaggableInterface, + PhabricatorProjectInterface, PhabricatorDestructibleInterface { protected $name; @@ -71,6 +72,24 @@ final class PhabricatorDashboardPanel return 'W'.$this->getID(); } + public function getPanelTypes() { + $panel_types = PhabricatorDashboardPanelType::getAllPanelTypes(); + $panel_types = mpull($panel_types, 'getPanelTypeName', 'getPanelTypeKey'); + asort($panel_types); + $panel_types = (array('' => pht('(All Types)')) + $panel_types); + return $panel_types; + } + + public function getStatuses() { + $statuses = + array( + '' => pht('(All Panels)'), + 'active' => pht('Active Panels'), + 'archived' => pht('Archived Panels'), + ); + return $statuses; + } + public function getImplementation() { return idx( PhabricatorDashboardPanelType::getAllPanelTypes(),