From 73924dfa1812385deb8af880c0fcb44e1a2bc88a Mon Sep 17 00:00:00 2001 From: epriestley Date: Thu, 30 Jan 2014 11:43:24 -0800 Subject: [PATCH] Add initial skeleton for Dashboard application Summary: Ref T3583. General idea here is: - Users will be able to create `DashboardPanel`s, which are things like the jump nav, or a minifeed, or recent assigned tasks, or recent tokens given, or whatever else. - The `DashboardPanel`s can be combined into `Dashboard`s, which select specific panels and arrange them in some layout (and maybe have a few other options eventually). - Then, you'll be able to set a specific `Dashboard` for your home page, and maybe for project home pages. But you can also use `Dashboard`s on their own if you just like dashboards. My plan is pretty much: - Put in basic infrastructure for dashboards (this diff). - Add basic create/edit (next few diffs). - Once dashboards sort of work, do the homepage integration. This diff does very little: you can't create dashboards or panels yet, and thus there are no dashboards to look at. This is all skeleton code, pretty much. IMPORTANT: We need an icon bwahahahahaha Test Plan: omg si purrfect {F106367} Reviewers: chad, btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T3583 Differential Revision: https://secure.phabricator.com/D8109 --- .../sql/autopatches/20140130.dash.1.board.sql | 10 +++ .../sql/autopatches/20140130.dash.2.panel.sql | 11 +++ src/__phutil_library_map__.php | 38 ++++++++++ .../PhabricatorApplicationDashboard.php | 40 ++++++++++ .../PhabricatorDashboardController.php | 5 ++ .../PhabricatorDashboardListController.php | 43 +++++++++++ ...habricatorDashboardPanelListController.php | 43 +++++++++++ .../PhabricatorDashboardPHIDTypeDashboard.php | 42 +++++++++++ .../PhabricatorDashboardPHIDTypePanel.php | 73 +++++++++++++++++++ .../query/PhabricatorDashboardPanelQuery.php | 60 +++++++++++++++ .../PhabricatorDashboardPanelSearchEngine.php | 49 +++++++++++++ .../query/PhabricatorDashboardQuery.php | 60 +++++++++++++++ .../PhabricatorDashboardSearchEngine.php | 49 +++++++++++++ .../storage/PhabricatorDashboard.php | 52 +++++++++++++ .../storage/PhabricatorDashboardDAO.php | 9 +++ .../storage/PhabricatorDashboardPanel.php | 68 +++++++++++++++++ .../patch/PhabricatorBuiltinPatchList.php | 1 + 17 files changed, 653 insertions(+) create mode 100644 resources/sql/autopatches/20140130.dash.1.board.sql create mode 100644 resources/sql/autopatches/20140130.dash.2.panel.sql create mode 100644 src/applications/dashboard/application/PhabricatorApplicationDashboard.php create mode 100644 src/applications/dashboard/controller/PhabricatorDashboardController.php create mode 100644 src/applications/dashboard/controller/PhabricatorDashboardListController.php create mode 100644 src/applications/dashboard/controller/PhabricatorDashboardPanelListController.php create mode 100644 src/applications/dashboard/phid/PhabricatorDashboardPHIDTypeDashboard.php create mode 100644 src/applications/dashboard/phid/PhabricatorDashboardPHIDTypePanel.php create mode 100644 src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php create mode 100644 src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php create mode 100644 src/applications/dashboard/query/PhabricatorDashboardQuery.php create mode 100644 src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php create mode 100644 src/applications/dashboard/storage/PhabricatorDashboard.php create mode 100644 src/applications/dashboard/storage/PhabricatorDashboardDAO.php create mode 100644 src/applications/dashboard/storage/PhabricatorDashboardPanel.php diff --git a/resources/sql/autopatches/20140130.dash.1.board.sql b/resources/sql/autopatches/20140130.dash.1.board.sql new file mode 100644 index 0000000000..51f82c02a5 --- /dev/null +++ b/resources/sql/autopatches/20140130.dash.1.board.sql @@ -0,0 +1,10 @@ +CREATE TABLE {$NAMESPACE}_dashboard.dashboard ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, + name VARCHAR(255) NOT NULL, + viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, + editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (phid) +) ENGINE=InnoDB, COLLATE=utf8_general_ci; diff --git a/resources/sql/autopatches/20140130.dash.2.panel.sql b/resources/sql/autopatches/20140130.dash.2.panel.sql new file mode 100644 index 0000000000..3176ed3f63 --- /dev/null +++ b/resources/sql/autopatches/20140130.dash.2.panel.sql @@ -0,0 +1,11 @@ +CREATE TABLE {$NAMESPACE}_dashboard.dashboard_panel ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, + name VARCHAR(255) NOT NULL, + viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, + editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, + properties LONGTEXT NOT NULL COLLATE utf8_bin, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (phid) +) ENGINE=InnoDB, COLLATE=utf8_general_ci; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 784fa675c0..b93be87b78 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1075,6 +1075,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationConpherence' => 'applications/conpherence/application/PhabricatorApplicationConpherence.php', 'PhabricatorApplicationCountdown' => 'applications/countdown/application/PhabricatorApplicationCountdown.php', 'PhabricatorApplicationDaemons' => 'applications/daemon/application/PhabricatorApplicationDaemons.php', + 'PhabricatorApplicationDashboard' => 'applications/dashboard/application/PhabricatorApplicationDashboard.php', 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 'PhabricatorApplicationDifferential' => 'applications/differential/application/PhabricatorApplicationDifferential.php', 'PhabricatorApplicationDiffusion' => 'applications/diffusion/application/PhabricatorApplicationDiffusion.php', @@ -1378,6 +1379,18 @@ phutil_register_library_map(array( 'PhabricatorDaemonManagementWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementWorkflow.php', 'PhabricatorDaemonReference' => 'infrastructure/daemon/control/PhabricatorDaemonReference.php', 'PhabricatorDaemonTaskGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php', + 'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php', + 'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php', + 'PhabricatorDashboardDAO' => 'applications/dashboard/storage/PhabricatorDashboardDAO.php', + 'PhabricatorDashboardListController' => 'applications/dashboard/controller/PhabricatorDashboardListController.php', + 'PhabricatorDashboardPHIDTypeDashboard' => 'applications/dashboard/phid/PhabricatorDashboardPHIDTypeDashboard.php', + 'PhabricatorDashboardPHIDTypePanel' => 'applications/dashboard/phid/PhabricatorDashboardPHIDTypePanel.php', + 'PhabricatorDashboardPanel' => 'applications/dashboard/storage/PhabricatorDashboardPanel.php', + 'PhabricatorDashboardPanelListController' => 'applications/dashboard/controller/PhabricatorDashboardPanelListController.php', + 'PhabricatorDashboardPanelQuery' => 'applications/dashboard/query/PhabricatorDashboardPanelQuery.php', + 'PhabricatorDashboardPanelSearchEngine' => 'applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php', + 'PhabricatorDashboardQuery' => 'applications/dashboard/query/PhabricatorDashboardQuery.php', + 'PhabricatorDashboardSearchEngine' => 'applications/dashboard/query/PhabricatorDashboardSearchEngine.php', 'PhabricatorDataNotAttachedException' => 'infrastructure/storage/lisk/PhabricatorDataNotAttachedException.php', 'PhabricatorDebugController' => 'applications/system/PhabricatorDebugController.php', 'PhabricatorDefaultFileStorageEngineSelector' => 'applications/files/engineselector/PhabricatorDefaultFileStorageEngineSelector.php', @@ -3662,6 +3675,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationConpherence' => 'PhabricatorApplication', 'PhabricatorApplicationCountdown' => 'PhabricatorApplication', 'PhabricatorApplicationDaemons' => 'PhabricatorApplication', + 'PhabricatorApplicationDashboard' => 'PhabricatorApplication', 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationDifferential' => 'PhabricatorApplication', 'PhabricatorApplicationDiffusion' => 'PhabricatorApplication', @@ -4007,6 +4021,30 @@ phutil_register_library_map(array( 'PhabricatorDaemonManagementStopWorkflow' => 'PhabricatorDaemonManagementWorkflow', 'PhabricatorDaemonManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorDaemonTaskGarbageCollector' => 'PhabricatorGarbageCollector', + 'PhabricatorDashboard' => + array( + 0 => 'PhabricatorDashboardDAO', + 1 => 'PhabricatorPolicyInterface', + ), + 'PhabricatorDashboardController' => 'PhabricatorController', + 'PhabricatorDashboardDAO' => 'PhabricatorLiskDAO', + 'PhabricatorDashboardListController' => + array( + 0 => 'PhabricatorDashboardController', + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', + ), + 'PhabricatorDashboardPHIDTypeDashboard' => 'PhabricatorPHIDType', + 'PhabricatorDashboardPHIDTypePanel' => 'PhabricatorPHIDType', + 'PhabricatorDashboardPanel' => 'PhabricatorDashboardDAO', + 'PhabricatorDashboardPanelListController' => + array( + 0 => 'PhabricatorDashboardController', + 1 => 'PhabricatorApplicationSearchResultsControllerInterface', + ), + 'PhabricatorDashboardPanelQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorDashboardPanelSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'PhabricatorDashboardQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorDashboardSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorDataNotAttachedException' => 'Exception', 'PhabricatorDebugController' => 'PhabricatorController', 'PhabricatorDefaultFileStorageEngineSelector' => 'PhabricatorFileStorageEngineSelector', diff --git a/src/applications/dashboard/application/PhabricatorApplicationDashboard.php b/src/applications/dashboard/application/PhabricatorApplicationDashboard.php new file mode 100644 index 0000000000..20f49ef58d --- /dev/null +++ b/src/applications/dashboard/application/PhabricatorApplicationDashboard.php @@ -0,0 +1,40 @@ +\d+)' => 'PhabricatorDashboardPanelViewController', + '/dashboard/' => array( + '(?:query/(?P[^/]+)/)?' + => 'PhabricatorDashboardListController', + 'view/(?P\d+)/' => 'PhabricatorDashboardViewController', + 'panel/' => array( + '(?:query/(?P[^/]+)/)?' + => 'PhabricatorDashboardPanelListController', + ), + ), + ); + } + + public function shouldAppearInLaunchView() { + return false; + } + + public function canUninstall() { + return false; + } + +} diff --git a/src/applications/dashboard/controller/PhabricatorDashboardController.php b/src/applications/dashboard/controller/PhabricatorDashboardController.php new file mode 100644 index 0000000000..f7773cd42e --- /dev/null +++ b/src/applications/dashboard/controller/PhabricatorDashboardController.php @@ -0,0 +1,5 @@ +queryKey = idx($data, 'queryKey'); + } + + public function processRequest() { + $request = $this->getRequest(); + $controller = id(new PhabricatorApplicationSearchController($request)) + ->setQueryKey($this->queryKey) + ->setSearchEngine(new PhabricatorDashboardSearchEngine()) + ->setNavigation($this->buildSideNavView()); + return $this->delegateToController($controller); + } + + public function buildSideNavView() { + $user = $this->getRequest()->getUser(); + + $nav = new AphrontSideNavFilterView(); + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); + + id(new PhabricatorDashboardSearchEngine()) + ->setViewer($user) + ->addNavigationItems($nav->getMenu()); + + $nav->selectFilter(null); + + return $nav; + } + + public function renderResultsList( + array $dashboards, + PhabricatorSavedQuery $query) { + + return 'got '.count($dashboards).' ok'; + } + +} diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelListController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelListController.php new file mode 100644 index 0000000000..d4531d8ab8 --- /dev/null +++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelListController.php @@ -0,0 +1,43 @@ +queryKey = idx($data, 'queryKey'); + } + + public function processRequest() { + $request = $this->getRequest(); + $controller = id(new PhabricatorApplicationSearchController($request)) + ->setQueryKey($this->queryKey) + ->setSearchEngine(new PhabricatorDashboardPanelSearchEngine()) + ->setNavigation($this->buildSideNavView()); + return $this->delegateToController($controller); + } + + public function buildSideNavView() { + $user = $this->getRequest()->getUser(); + + $nav = new AphrontSideNavFilterView(); + $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); + + id(new PhabricatorDashboardPanelSearchEngine()) + ->setViewer($user) + ->addNavigationItems($nav->getMenu()); + + $nav->selectFilter(null); + + return $nav; + } + + public function renderResultsList( + array $panels, + PhabricatorSavedQuery $query) { + + return 'got '.count($panels).' ok'; + } + +} diff --git a/src/applications/dashboard/phid/PhabricatorDashboardPHIDTypeDashboard.php b/src/applications/dashboard/phid/PhabricatorDashboardPHIDTypeDashboard.php new file mode 100644 index 0000000000..0af0fd3d9d --- /dev/null +++ b/src/applications/dashboard/phid/PhabricatorDashboardPHIDTypeDashboard.php @@ -0,0 +1,42 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $dashboard = $objects[$phid]; + + $id = $dashboard->getID(); + + $handle->setName($dashboard->getName()); + $handle->setURI("/dashboard/view/{$id}/"); + } + } + +} diff --git a/src/applications/dashboard/phid/PhabricatorDashboardPHIDTypePanel.php b/src/applications/dashboard/phid/PhabricatorDashboardPHIDTypePanel.php new file mode 100644 index 0000000000..8b075e2602 --- /dev/null +++ b/src/applications/dashboard/phid/PhabricatorDashboardPHIDTypePanel.php @@ -0,0 +1,73 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $panel = $objects[$phid]; + + $name = $panel->getName(); + $monogram = $panel->getMonogram(); + + $handle->setName($panel->getMonogram()); + $handle->setFullName("{$monogram} {$name}"); + $handle->setURI("/{$monogram}"); + } + } + + public function canLoadNamedObject($name) { + return preg_match('/^W\d*[1-9]\d*$/i', $name); + } + + public function loadNamedObjects( + PhabricatorObjectQuery $query, + array $names) { + + $id_map = array(); + foreach ($names as $name) { + $id = (int)substr($name, 1); + $id_map[$id][] = $name; + } + + $objects = id(new PhabricatorDashboardPanelQuery()) + ->setViewer($query->getViewer()) + ->withIDs(array_keys($id_map)) + ->execute(); + + $results = array(); + foreach ($objects as $id => $object) { + foreach (idx($id_map, $id, array()) as $name) { + $results[$name] = $object; + } + } + + return $results; + } + +} diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php new file mode 100644 index 0000000000..8e13ee9d5f --- /dev/null +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php @@ -0,0 +1,60 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + 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); + } + + protected function buildWhereClause($conn_r) { + $where = array(); + + if ($this->ids) { + $where[] = qsprintf( + $conn_r, + 'id IN (%Ld)', + $this->ids); + } + + if ($this->phids) { + $where[] = qsprintf( + $conn_r, + 'phid IN (%Ls)', + $this->phids); + } + + $where[] = $this->buildPagingClause($conn_r); + + return $this->formatWhereClause($where); + } + + public function getQueryApplicationClass() { + return 'PhabricatorApplicationDashboard'; + } + +} diff --git a/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php new file mode 100644 index 0000000000..3245dc165e --- /dev/null +++ b/src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php @@ -0,0 +1,49 @@ + pht('All Panels'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + +} diff --git a/src/applications/dashboard/query/PhabricatorDashboardQuery.php b/src/applications/dashboard/query/PhabricatorDashboardQuery.php new file mode 100644 index 0000000000..461ec32a41 --- /dev/null +++ b/src/applications/dashboard/query/PhabricatorDashboardQuery.php @@ -0,0 +1,60 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + protected function loadPage() { + $table = new PhabricatorDashboard(); + $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); + } + + protected function buildWhereClause($conn_r) { + $where = array(); + + if ($this->ids) { + $where[] = qsprintf( + $conn_r, + 'id IN (%Ld)', + $this->ids); + } + + if ($this->phids) { + $where[] = qsprintf( + $conn_r, + 'phid IN (%Ls)', + $this->phids); + } + + $where[] = $this->buildPagingClause($conn_r); + + return $this->formatWhereClause($where); + } + + public function getQueryApplicationClass() { + return 'PhabricatorApplicationDashboard'; + } + +} diff --git a/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php b/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php new file mode 100644 index 0000000000..961a7e5f57 --- /dev/null +++ b/src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php @@ -0,0 +1,49 @@ + pht('All Dashboards'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + +} diff --git a/src/applications/dashboard/storage/PhabricatorDashboard.php b/src/applications/dashboard/storage/PhabricatorDashboard.php new file mode 100644 index 0000000000..96cbc21eec --- /dev/null +++ b/src/applications/dashboard/storage/PhabricatorDashboard.php @@ -0,0 +1,52 @@ + true, + ) + parent::getConfiguration(); + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID( + PhabricatorDashboardPHIDTypeDashboard::TYPECONST); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return $this->getViewPolicy(); + case PhabricatorPolicyCapability::CAN_EDIT: + return $this->getEditPolicy(); + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } + +} diff --git a/src/applications/dashboard/storage/PhabricatorDashboardDAO.php b/src/applications/dashboard/storage/PhabricatorDashboardDAO.php new file mode 100644 index 0000000000..23adff06f2 --- /dev/null +++ b/src/applications/dashboard/storage/PhabricatorDashboardDAO.php @@ -0,0 +1,9 @@ + true, + self::CONFIG_SERIALIZATION => array( + 'properties' => self::SERIALIZATION_JSON, + ), + ) + parent::getConfiguration(); + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID( + PhabricatorDashboardPHIDTypeDashboard::TYPECONST); + } + + public function getProperty($key, $default = null) { + return idx($this->properties, $key, $default); + } + + public function setProperty($key, $value) { + $this->properties[$key] = $value; + return $this; + } + + public function getMonogram() { + return 'W'.$this->getID(); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return $this->getViewPolicy(); + case PhabricatorPolicyCapability::CAN_EDIT: + return $this->getEditPolicy(); + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } + +} diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index 58dacf4ac6..70ee75e1d3 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -117,6 +117,7 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { 'db.nuance' => array(), 'db.passphrase' => array(), 'db.phragment' => array(), + 'db.dashboard' => array(), '0000.legacy.sql' => array( 'legacy' => 0, ),