From 941f0ba7ae206618c1375832f17a76f03b72096f Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 30 Apr 2014 14:28:55 -0700 Subject: [PATCH] Allow panels to appear on dashboards Summary: Ref T3583. Adds edges, query relationships, etc. Lots of debugging/temporary UI. My general intent here is to use edges to track where panels appear, and then put additional data on the dashboard itself to control layout, positioning, etc. Dashboards don't actually render yet so this is still pretty boring. Test Plan: {F149175} {F149176} {F149177} Reviewers: chad, btrahan Reviewed By: btrahan Subscribers: epriestley Maniphest Tasks: T3583 Differential Revision: https://secure.phabricator.com/D8916 --- .../sql/autopatches/20140430.dash.2.edge.sql | 15 +++ src/__phutil_library_map__.php | 2 + .../PhabricatorApplicationDashboard.php | 1 + ...PhabricatorDashboardAddPanelController.php | 94 +++++++++++++++++++ ...habricatorDashboardPanelViewController.php | 9 ++ .../PhabricatorDashboardViewController.php | 16 ++++ ...ricatorDashboardPanelTransactionEditor.php | 1 + .../PhabricatorDashboardTransactionEditor.php | 5 + .../query/PhabricatorDashboardQuery.php | 42 +++++++++ .../storage/PhabricatorDashboard.php | 22 +++++ .../edges/constants/PhabricatorEdgeConfig.php | 22 +++++ 11 files changed, 229 insertions(+) create mode 100644 resources/sql/autopatches/20140430.dash.2.edge.sql create mode 100644 src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php diff --git a/resources/sql/autopatches/20140430.dash.2.edge.sql b/resources/sql/autopatches/20140430.dash.2.edge.sql new file mode 100644 index 0000000000..0e3ef01f60 --- /dev/null +++ b/resources/sql/autopatches/20140430.dash.2.edge.sql @@ -0,0 +1,15 @@ +CREATE TABLE {$NAMESPACE}_dashboard.edge ( + src VARCHAR(64) NOT NULL COLLATE utf8_bin, + type VARCHAR(64) NOT NULL COLLATE utf8_bin, + dst VARCHAR(64) NOT NULL COLLATE utf8_bin, + dateCreated INT UNSIGNED NOT NULL, + seq INT UNSIGNED NOT NULL, + dataID INT UNSIGNED, + PRIMARY KEY (src, type, dst), + KEY (src, type, dateCreated, seq) +) ENGINE=InnoDB, COLLATE utf8_general_ci; + +CREATE TABLE {$NAMESPACE}_dashboard.edgedata ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + data LONGTEXT NOT NULL COLLATE utf8_bin +) ENGINE=InnoDB, COLLATE utf8_general_ci; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index fb486a2134..085c9d628e 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1432,6 +1432,7 @@ phutil_register_library_map(array( 'PhabricatorDaemonReference' => 'infrastructure/daemon/control/PhabricatorDaemonReference.php', 'PhabricatorDaemonTaskGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php', 'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php', + 'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php', 'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php', 'PhabricatorDashboardDAO' => 'applications/dashboard/storage/PhabricatorDashboardDAO.php', 'PhabricatorDashboardEditController' => 'applications/dashboard/controller/PhabricatorDashboardEditController.php', @@ -4232,6 +4233,7 @@ phutil_register_library_map(array( 0 => 'PhabricatorDashboardDAO', 1 => 'PhabricatorPolicyInterface', ), + 'PhabricatorDashboardAddPanelController' => 'PhabricatorDashboardController', 'PhabricatorDashboardController' => 'PhabricatorController', 'PhabricatorDashboardDAO' => 'PhabricatorLiskDAO', 'PhabricatorDashboardEditController' => 'PhabricatorDashboardController', diff --git a/src/applications/dashboard/application/PhabricatorApplicationDashboard.php b/src/applications/dashboard/application/PhabricatorApplicationDashboard.php index c4f5d05fbc..71f6a289b7 100644 --- a/src/applications/dashboard/application/PhabricatorApplicationDashboard.php +++ b/src/applications/dashboard/application/PhabricatorApplicationDashboard.php @@ -23,6 +23,7 @@ final class PhabricatorApplicationDashboard extends PhabricatorApplication { 'view/(?P\d+)/' => 'PhabricatorDashboardViewController', 'create/' => 'PhabricatorDashboardEditController', 'edit/(?:(?P\d+)/)?' => 'PhabricatorDashboardEditController', + 'addpanel/(?P\d+)/' => 'PhabricatorDashboardAddPanelController', 'panel/' => array( '(?:query/(?P[^/]+)/)?' diff --git a/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php b/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php new file mode 100644 index 0000000000..9fc8d9720c --- /dev/null +++ b/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php @@ -0,0 +1,94 @@ +id = idx($data, 'id'); + } + + public function processRequest() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + + $dashboard = id(new PhabricatorDashboardQuery()) + ->setViewer($viewer) + ->withIDs(array($this->id)) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->executeOne(); + if (!$dashboard) { + return new Aphront404Response(); + } + + $dashboard_uri = $this->getApplicationURI('view/'.$dashboard->getID().'/'); + + $v_panel = $request->getStr('panel'); + $e_panel = true; + $errors = array(); + if ($request->isFormPost()) { + if (strlen($v_panel)) { + $panel = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withNames(array($v_panel)) + ->withTypes(array(PhabricatorDashboardPHIDTypePanel::TYPECONST)) + ->executeOne(); + if (!$panel) { + $errors[] = pht('No such panel!'); + $e_panel = pht('Invalid'); + } + } else { + $errors[] = pht('Name a panel to add.'); + $e_panel = pht('Required'); + } + + if (!$errors) { + $xactions = array(); + $xactions[] = id(new PhabricatorDashboardTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) + ->setMetadataValue( + 'edge:type', + PhabricatorEdgeConfig::TYPE_DASHBOARD_HAS_PANEL) + ->setNewValue( + array( + '+' => array( + $panel->getPHID() => $panel->getPHID(), + ), + )); + + $editor = id(new PhabricatorDashboardTransactionEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnMissingFields(true) + ->setContinueOnNoEffect(true) + ->applyTransactions($dashboard, $xactions); + + return id(new AphrontRedirectResponse())->setURI($dashboard_uri); + } + } + + $form = id(new AphrontFormView()) + ->setUser($viewer) + ->appendRemarkupInstructions( + pht('Enter a panel monogram like `W123`.')) + ->appendChild( + id(new AphrontFormTextControl()) + ->setName('panel') + ->setLabel(pht('Panel')) + ->setValue($v_panel) + ->setError($e_panel)); + + return $this->newDialog() + ->setTitle(pht('Add Panel')) + ->setErrors($errors) + ->appendChild($form->buildLayoutView()) + ->addCancelButton($dashboard_uri) + ->addSubmitButton(pht('Add Panel')); + } + +} diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php index a3a63a0965..9cc06a8595 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php @@ -124,6 +124,15 @@ final class PhabricatorDashboardPanelViewController pht('Editable By'), $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); + $dashboard_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( + $panel->getPHID(), + PhabricatorEdgeConfig::TYPE_PANEL_HAS_DASHBOARD); + $this->loadHandles($dashboard_phids); + + $properties->addProperty( + pht('Appears On'), + $this->renderHandlesForPHIDs($dashboard_phids)); + return $properties; } diff --git a/src/applications/dashboard/controller/PhabricatorDashboardViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardViewController.php index acd3c03768..94c0416792 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardViewController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardViewController.php @@ -16,6 +16,7 @@ final class PhabricatorDashboardViewController $dashboard = id(new PhabricatorDashboardQuery()) ->setViewer($viewer) ->withIDs(array($this->id)) + ->needPanels(true) ->executeOne(); if (!$dashboard) { return new Aphront404Response(); @@ -77,6 +78,14 @@ final class PhabricatorDashboardViewController ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); + $actions->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Add Panel')) + ->setIcon('new') + ->setHref($this->getApplicationURI("addpanel/{$id}/")) + ->setDisabled(!$can_edit) + ->setWorkflow(true)); + return $actions; } @@ -95,6 +104,13 @@ final class PhabricatorDashboardViewController pht('Editable By'), $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); + $panel_phids = $dashboard->getPanelPHIDs(); + $this->loadHandles($panel_phids); + + $properties->addProperty( + pht('Panels'), + $this->renderHandlesForPHIDs($panel_phids)); + return $properties; } diff --git a/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php index 19dc8da69e..15b32cedb0 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php @@ -8,6 +8,7 @@ final class PhabricatorDashboardPanelTransactionEditor $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; + $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorDashboardPanelTransaction::TYPE_NAME; diff --git a/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php b/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php index 023dd704f2..968c79468a 100644 --- a/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php +++ b/src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php @@ -8,6 +8,7 @@ final class PhabricatorDashboardTransactionEditor $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; + $types[] = PhabricatorTransactions::TYPE_EDGE; $types[] = PhabricatorDashboardTransaction::TYPE_NAME; @@ -51,6 +52,8 @@ final class PhabricatorDashboardTransactionEditor case PhabricatorTransactions::TYPE_EDIT_POLICY: $object->setEditPolicy($xaction->getNewValue()); return; + case PhabricatorTransactions::TYPE_EDGE: + return; } return parent::applyCustomInternalTransaction($object, $xaction); @@ -63,6 +66,8 @@ final class PhabricatorDashboardTransactionEditor switch ($xaction->getTransactionType()) { case PhabricatorDashboardTransaction::TYPE_NAME: return; + case PhabricatorTransactions::TYPE_EDGE: + return; } return parent::applyCustomExternalTransaction($object, $xaction); diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php index 461ec32a41..f236fb0066 100644 --- a/src/applications/dashboard/query/PhabricatorDashboardQuery.php +++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php @@ -6,6 +6,8 @@ final class PhabricatorDashboardQuery private $ids; private $phids; + private $needPanels; + public function withIDs(array $ids) { $this->ids = $ids; return $this; @@ -16,6 +18,11 @@ final class PhabricatorDashboardQuery return $this; } + public function needPanels($need_panels) { + $this->needPanels = $need_panels; + return $this; + } + protected function loadPage() { $table = new PhabricatorDashboard(); $conn_r = $table->establishConnection('r'); @@ -31,6 +38,41 @@ final class PhabricatorDashboardQuery return $table->loadAllFromArray($data); } + protected function didFilterPage(array $dashboards) { + if ($this->needPanels) { + $edge_query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs(mpull($dashboards, 'getPHID')) + ->withEdgeTypes( + array( + PhabricatorEdgeConfig::TYPE_DASHBOARD_HAS_PANEL, + )); + $edge_query->execute(); + + $panel_phids = $edge_query->getDestinationPHIDs(); + if ($panel_phids) { + $panels = id(new PhabricatorDashboardPanelQuery()) + ->setParentQuery($this) + ->setViewer($this->getViewer()) + ->withPHIDs($panel_phids) + ->execute(); + $panels = mpull($panels, null, 'getPHID'); + } else { + $panels = array(); + } + + foreach ($dashboards as $dashboard) { + $dashboard_phids = $edge_query->getDestinationPHIDs( + array($dashboard->getPHID())); + $dashboard_panels = array_select_keys($panels, $dashboard_phids); + + $dashboard->attachPanelPHIDs($dashboard_phids); + $dashboard->attachPanels($dashboard_panels); + } + } + + return $dashboards; + } + protected function buildWhereClause($conn_r) { $where = array(); diff --git a/src/applications/dashboard/storage/PhabricatorDashboard.php b/src/applications/dashboard/storage/PhabricatorDashboard.php index 0cefda2cd7..8753e0d484 100644 --- a/src/applications/dashboard/storage/PhabricatorDashboard.php +++ b/src/applications/dashboard/storage/PhabricatorDashboard.php @@ -10,6 +10,9 @@ final class PhabricatorDashboard extends PhabricatorDashboardDAO protected $viewPolicy; protected $editPolicy; + private $panelPHIDs = self::ATTACHABLE; + private $panels = self::ATTACHABLE; + public static function initializeNewDashboard(PhabricatorUser $actor) { return id(new PhabricatorDashboard()) ->setName('') @@ -28,6 +31,25 @@ final class PhabricatorDashboard extends PhabricatorDashboardDAO PhabricatorDashboardPHIDTypeDashboard::TYPECONST); } + public function attachPanelPHIDs(array $phids) { + $this->panelPHIDs = $phids; + return $this; + } + + public function getPanelPHIDs() { + return $this->assertAttached($this->panelPHIDs); + } + + public function attachPanels(array $panels) { + assert_instances_of($panels, 'PhabricatorDashboardPanel'); + $this->panels = $panels; + return $this; + } + + public function getPanels() { + return $this->assertAttached($this->panels); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php b/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php index 8614a38372..26969ebb16 100644 --- a/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php +++ b/src/infrastructure/edges/constants/PhabricatorEdgeConfig.php @@ -69,6 +69,9 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { const TYPE_OBJECT_HAS_COLUMN = 43; const TYPE_COLUMN_HAS_OBJECT = 44; + const TYPE_DASHBOARD_HAS_PANEL = 45; + const TYPE_PANEL_HAS_DASHBOARD = 46; + const TYPE_TEST_NO_CYCLE = 9000; const TYPE_PHOB_HAS_ASANATASK = 80001; @@ -153,6 +156,9 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { self::TYPE_OBJECT_HAS_COLUMN => self::TYPE_COLUMN_HAS_OBJECT, self::TYPE_COLUMN_HAS_OBJECT => self::TYPE_OBJECT_HAS_COLUMN, + + self::TYPE_PANEL_HAS_DASHBOARD => self::TYPE_DASHBOARD_HAS_PANEL, + self::TYPE_DASHBOARD_HAS_PANEL => self::TYPE_PANEL_HAS_DASHBOARD, ); return idx($map, $edge_type); @@ -259,6 +265,10 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { return '%s edited reviewer(s), added %d: %s; removed %d: %s.'; case self::TYPE_TASK_HAS_MOCK: return '%s edited mock(s), added %d: %s; removed %d: %s.'; + case self::TYPE_DASHBOARD_HAS_PANEL: + return '%s edited panel(s), added %d: %s; removed %d: %s.'; + case self::TYPE_PANEL_HAS_DASHBOARD: + return '%s edited dashboard(s), added %d: %s; removed %d: %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: @@ -329,6 +339,10 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { return '%s added %d reviewer(s): %s.'; case self::TYPE_TASK_HAS_MOCK: return '%s added %d mock(s): %s.'; + case self::TYPE_DASHBOARD_HAS_PANEL: + return '%s added %d panel(s): %s.'; + case self::TYPE_PANEL_HAS_DASHBOARD: + return '%s added %d dashboard(s): %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: @@ -400,6 +414,10 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { return '%s removed %d reviewer(s): %s.'; case self::TYPE_TASK_HAS_MOCK: return '%s removed %d mock(s): %s.'; + case self::TYPE_DASHBOARD_HAS_PANEL: + return '%s removed %d panel(s): %s.'; + case self::TYPE_PANEL_HAS_DASHBOARD: + return '%s removed %d dashboard(s): %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: @@ -469,6 +487,10 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants { return '%s updated reviewers of %s.'; case self::TYPE_TASK_HAS_MOCK: return '%s updated mocks of %s.'; + case self::TYPE_PANEL_HAS_DASHBOARD: + return '%s updated panels for %s.'; + case self::TYPE_PANEL_HAS_DASHBOARD: + return '%s updated dashboards for %s.'; case self::TYPE_SUBSCRIBED_TO_OBJECT: case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: case self::TYPE_FILE_HAS_OBJECT: