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

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
This commit is contained in:
Chad Little 2015-07-21 12:01:19 -07:00
parent f1222f956a
commit 6be53bd916
10 changed files with 200 additions and 108 deletions

View file

@ -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',

View file

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

View file

@ -170,6 +170,8 @@ final class PhabricatorDashboardManageController
pht('Panels'),
$viewer->renderHandleList($dashboard->getPanelPHIDs()));
$properties->invokeWillRenderEvent();
return $properties;
}

View file

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

View file

@ -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() {

View file

@ -11,18 +11,14 @@ 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) {
protected function buildQueryFromParameters(array $map) {
$query = $this->newQuery();
if ($map['status']) {
switch ($map['status']) {
case 'active':
$query->withArchived(false);
break;
@ -32,45 +28,31 @@ final class PhabricatorDashboardPanelSearchEngine
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);

View file

@ -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() {

View file

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

View file

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

View file

@ -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(),