1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-10 00:42:41 +01:00

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
This commit is contained in:
epriestley 2014-01-30 11:43:24 -08:00
parent eb397a48b4
commit 73924dfa18
17 changed files with 653 additions and 0 deletions

View file

@ -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;

View file

@ -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;

View file

@ -1075,6 +1075,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationConpherence' => 'applications/conpherence/application/PhabricatorApplicationConpherence.php', 'PhabricatorApplicationConpherence' => 'applications/conpherence/application/PhabricatorApplicationConpherence.php',
'PhabricatorApplicationCountdown' => 'applications/countdown/application/PhabricatorApplicationCountdown.php', 'PhabricatorApplicationCountdown' => 'applications/countdown/application/PhabricatorApplicationCountdown.php',
'PhabricatorApplicationDaemons' => 'applications/daemon/application/PhabricatorApplicationDaemons.php', 'PhabricatorApplicationDaemons' => 'applications/daemon/application/PhabricatorApplicationDaemons.php',
'PhabricatorApplicationDashboard' => 'applications/dashboard/application/PhabricatorApplicationDashboard.php',
'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php',
'PhabricatorApplicationDifferential' => 'applications/differential/application/PhabricatorApplicationDifferential.php', 'PhabricatorApplicationDifferential' => 'applications/differential/application/PhabricatorApplicationDifferential.php',
'PhabricatorApplicationDiffusion' => 'applications/diffusion/application/PhabricatorApplicationDiffusion.php', 'PhabricatorApplicationDiffusion' => 'applications/diffusion/application/PhabricatorApplicationDiffusion.php',
@ -1378,6 +1379,18 @@ phutil_register_library_map(array(
'PhabricatorDaemonManagementWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementWorkflow.php', 'PhabricatorDaemonManagementWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementWorkflow.php',
'PhabricatorDaemonReference' => 'infrastructure/daemon/control/PhabricatorDaemonReference.php', 'PhabricatorDaemonReference' => 'infrastructure/daemon/control/PhabricatorDaemonReference.php',
'PhabricatorDaemonTaskGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.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', 'PhabricatorDataNotAttachedException' => 'infrastructure/storage/lisk/PhabricatorDataNotAttachedException.php',
'PhabricatorDebugController' => 'applications/system/PhabricatorDebugController.php', 'PhabricatorDebugController' => 'applications/system/PhabricatorDebugController.php',
'PhabricatorDefaultFileStorageEngineSelector' => 'applications/files/engineselector/PhabricatorDefaultFileStorageEngineSelector.php', 'PhabricatorDefaultFileStorageEngineSelector' => 'applications/files/engineselector/PhabricatorDefaultFileStorageEngineSelector.php',
@ -3662,6 +3675,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationConpherence' => 'PhabricatorApplication', 'PhabricatorApplicationConpherence' => 'PhabricatorApplication',
'PhabricatorApplicationCountdown' => 'PhabricatorApplication', 'PhabricatorApplicationCountdown' => 'PhabricatorApplication',
'PhabricatorApplicationDaemons' => 'PhabricatorApplication', 'PhabricatorApplicationDaemons' => 'PhabricatorApplication',
'PhabricatorApplicationDashboard' => 'PhabricatorApplication',
'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController',
'PhabricatorApplicationDifferential' => 'PhabricatorApplication', 'PhabricatorApplicationDifferential' => 'PhabricatorApplication',
'PhabricatorApplicationDiffusion' => 'PhabricatorApplication', 'PhabricatorApplicationDiffusion' => 'PhabricatorApplication',
@ -4007,6 +4021,30 @@ phutil_register_library_map(array(
'PhabricatorDaemonManagementStopWorkflow' => 'PhabricatorDaemonManagementWorkflow', 'PhabricatorDaemonManagementStopWorkflow' => 'PhabricatorDaemonManagementWorkflow',
'PhabricatorDaemonManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorDaemonManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorDaemonTaskGarbageCollector' => 'PhabricatorGarbageCollector', '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', 'PhabricatorDataNotAttachedException' => 'Exception',
'PhabricatorDebugController' => 'PhabricatorController', 'PhabricatorDebugController' => 'PhabricatorController',
'PhabricatorDefaultFileStorageEngineSelector' => 'PhabricatorFileStorageEngineSelector', 'PhabricatorDefaultFileStorageEngineSelector' => 'PhabricatorFileStorageEngineSelector',

View file

@ -0,0 +1,40 @@
<?php
final class PhabricatorApplicationDashboard extends PhabricatorApplication {
public function getBaseURI() {
return '/dashboard/';
}
public function getShortDescription() {
return pht('Such Data');
}
public function getIconName() {
return 'dashboard';
}
public function getRoutes() {
return array(
'/W(?P<id>\d+)' => 'PhabricatorDashboardPanelViewController',
'/dashboard/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhabricatorDashboardListController',
'view/(?P<id>\d+)/' => 'PhabricatorDashboardViewController',
'panel/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?'
=> 'PhabricatorDashboardPanelListController',
),
),
);
}
public function shouldAppearInLaunchView() {
return false;
}
public function canUninstall() {
return false;
}
}

View file

@ -0,0 +1,5 @@
<?php
abstract class PhabricatorDashboardController extends PhabricatorController {
}

View file

@ -0,0 +1,43 @@
<?php
final class PhabricatorDashboardListController
extends PhabricatorDashboardController
implements PhabricatorApplicationSearchResultsControllerInterface {
private $queryKey;
public function willProcessRequest(array $data) {
$this->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';
}
}

View file

@ -0,0 +1,43 @@
<?php
final class PhabricatorDashboardPanelListController
extends PhabricatorDashboardController
implements PhabricatorApplicationSearchResultsControllerInterface {
private $queryKey;
public function willProcessRequest(array $data) {
$this->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';
}
}

View file

@ -0,0 +1,42 @@
<?php
final class PhabricatorDashboardPHIDTypeDashboard extends PhabricatorPHIDType {
const TYPECONST = 'DSHB';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Dashboard');
}
public function newObject() {
return new PhabricatorDashboard();
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorDashboardQuery())
->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}/");
}
}
}

View file

@ -0,0 +1,73 @@
<?php
final class PhabricatorDashboardPHIDTypePanel extends PhabricatorPHIDType {
const TYPECONST = 'DSHP';
public function getTypeConstant() {
return self::TYPECONST;
}
public function getTypeName() {
return pht('Panel');
}
public function newObject() {
return new PhabricatorDashboardPanel();
}
protected function buildQueryForObjects(
PhabricatorObjectQuery $query,
array $phids) {
return id(new PhabricatorDashboardPanelQuery())
->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;
}
}

View file

@ -0,0 +1,60 @@
<?php
final class PhabricatorDashboardPanelQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
public function withIDs(array $ids) {
$this->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';
}
}

View file

@ -0,0 +1,49 @@
<?php
final class PhabricatorDashboardPanelSearchEngine
extends PhabricatorApplicationSearchEngine {
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorDashboardPanelQuery());
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
return;
}
protected function getURI($path) {
return '/dashboard/panel/'.$path;
}
public function getBuiltinQueryNames() {
$names = array(
'all' => 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);
}
}

View file

@ -0,0 +1,60 @@
<?php
final class PhabricatorDashboardQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
public function withIDs(array $ids) {
$this->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';
}
}

View file

@ -0,0 +1,49 @@
<?php
final class PhabricatorDashboardSearchEngine
extends PhabricatorApplicationSearchEngine {
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new PhabricatorDashboardQuery());
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved_query) {
return;
}
protected function getURI($path) {
return '/dashboard/'.$path;
}
public function getBuiltinQueryNames() {
$names = array(
'all' => 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);
}
}

View file

@ -0,0 +1,52 @@
<?php
/**
* A collection of dashboard panels with a specific layout.
*/
final class PhabricatorDashboard extends PhabricatorDashboardDAO
implements PhabricatorPolicyInterface {
private $name;
private $viewPolicy;
private $editPolicy;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => 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;
}
}

View file

@ -0,0 +1,9 @@
<?php
abstract class PhabricatorDashboardDAO extends PhabricatorLiskDAO {
public function getApplicationName() {
return 'dashboard';
}
}

View file

@ -0,0 +1,68 @@
<?php
/**
* An individual dashboard panel.
*/
final class PhabricatorDashboardPanel extends PhabricatorDashboardDAO {
private $name;
private $viewPolicy;
private $editPolicy;
private $properties = array();
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => 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;
}
}

View file

@ -117,6 +117,7 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'db.nuance' => array(), 'db.nuance' => array(),
'db.passphrase' => array(), 'db.passphrase' => array(),
'db.phragment' => array(), 'db.phragment' => array(),
'db.dashboard' => array(),
'0000.legacy.sql' => array( '0000.legacy.sql' => array(
'legacy' => 0, 'legacy' => 0,
), ),