@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+198 -106
+2
src/__phutil_library_map__.php
··· 5622 5622 'PhabricatorPolicyInterface', 5623 5623 'PhabricatorFlaggableInterface', 5624 5624 'PhabricatorDestructibleInterface', 5625 + 'PhabricatorProjectInterface', 5625 5626 ), 5626 5627 'PhabricatorDashboardAddPanelController' => 'PhabricatorDashboardController', 5627 5628 'PhabricatorDashboardApplication' => 'PhabricatorApplication', ··· 5644 5645 'PhabricatorPolicyInterface', 5645 5646 'PhabricatorCustomFieldInterface', 5646 5647 'PhabricatorFlaggableInterface', 5648 + 'PhabricatorProjectInterface', 5647 5649 'PhabricatorDestructibleInterface', 5648 5650 ), 5649 5651 'PhabricatorDashboardPanelArchiveController' => 'PhabricatorDashboardController',
+22 -4
src/applications/dashboard/controller/PhabricatorDashboardEditController.php
··· 27 27 if (!$dashboard) { 28 28 return new Aphront404Response(); 29 29 } 30 - 30 + $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( 31 + $dashboard->getPHID(), 32 + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); 33 + $v_projects = array_reverse($v_projects); 31 34 $is_new = false; 32 35 } else { 33 36 if (!$request->getStr('edit')) { ··· 44 47 } 45 48 46 49 $dashboard = PhabricatorDashboard::initializeNewDashboard($viewer); 47 - 50 + $v_projects = array(); 48 51 $is_new = true; 49 52 } 50 53 ··· 79 82 $v_layout_mode = $request->getStr('layout_mode'); 80 83 $v_view_policy = $request->getStr('viewPolicy'); 81 84 $v_edit_policy = $request->getStr('editPolicy'); 85 + $v_projects = $request->getArr('projects'); 82 86 83 87 $xactions = array(); 84 88 ··· 100 104 ->setTransactionType($type_edit_policy) 101 105 ->setNewValue($v_edit_policy); 102 106 107 + $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; 108 + $xactions[] = id(new PhabricatorDashboardTransaction()) 109 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 110 + ->setMetadataValue('edge:type', $proj_edge_type) 111 + ->setNewValue(array('=' => array_fuse($v_projects))); 112 + 103 113 try { 104 114 $editor = id(new PhabricatorDashboardTransactionEditor()) 105 115 ->setActor($viewer) ··· 153 163 ->setLabel(pht('Layout Mode')) 154 164 ->setName('layout_mode') 155 165 ->setValue($v_layout_mode) 156 - ->setOptions($layout_mode_options)) 157 - ->appendChild( 166 + ->setOptions($layout_mode_options)); 167 + 168 + $form->appendControl( 169 + id(new AphrontFormTokenizerControl()) 170 + ->setLabel(pht('Projects')) 171 + ->setName('projects') 172 + ->setValue($v_projects) 173 + ->setDatasource(new PhabricatorProjectDatasource())); 174 + 175 + $form->appendChild( 158 176 id(new AphrontFormSubmitControl()) 159 177 ->setValue($button) 160 178 ->addCancelButton($cancel_uri));
+2
src/applications/dashboard/controller/PhabricatorDashboardManageController.php
··· 170 170 pht('Panels'), 171 171 $viewer->renderHandleList($dashboard->getPanelPHIDs())); 172 172 173 + $properties->invokeWillRenderEvent(); 174 + 173 175 return $properties; 174 176 } 175 177
+19
src/applications/dashboard/controller/PhabricatorDashboardPanelEditController.php
··· 57 57 if (!$panel) { 58 58 return new Aphront404Response(); 59 59 } 60 + $v_projects = PhabricatorEdgeQuery::loadDestinationPHIDs( 61 + $panel->getPHID(), 62 + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); 63 + $v_projects = array_reverse($v_projects); 60 64 61 65 if ($dashboard) { 62 66 $can_edit = PhabricatorPolicyFilter::hasCapability( ··· 86 90 if (empty($types[$type])) { 87 91 return $this->processPanelTypeRequest($request); 88 92 } 93 + $v_projects = array(); 89 94 90 95 $panel->setPanelType($type); 91 96 } ··· 136 141 $v_name = $request->getStr('name'); 137 142 $v_view_policy = $request->getStr('viewPolicy'); 138 143 $v_edit_policy = $request->getStr('editPolicy'); 144 + $v_projects = $request->getArr('projects'); 139 145 140 146 $type_name = PhabricatorDashboardPanelTransaction::TYPE_NAME; 141 147 $type_view_policy = PhabricatorTransactions::TYPE_VIEW_POLICY; ··· 155 161 ->setTransactionType($type_edit_policy) 156 162 ->setNewValue($v_edit_policy); 157 163 164 + $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; 165 + $xactions[] = id(new PhabricatorDashboardPanelTransaction()) 166 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 167 + ->setMetadataValue('edge:type', $proj_edge_type) 168 + ->setNewValue(array('=' => array_fuse($v_projects))); 169 + 158 170 $field_xactions = $field_list->buildFieldTransactionsFromRequest( 159 171 new PhabricatorDashboardPanelTransaction(), 160 172 $request); ··· 231 243 ->setPolicyObject($panel) 232 244 ->setCapability(PhabricatorPolicyCapability::CAN_EDIT) 233 245 ->setPolicies($policies)); 246 + 247 + $form->appendControl( 248 + id(new AphrontFormTokenizerControl()) 249 + ->setLabel(pht('Projects')) 250 + ->setName('projects') 251 + ->setValue($v_projects) 252 + ->setDatasource(new PhabricatorProjectDatasource())); 234 253 235 254 $field_list->appendFieldsToForm($form); 236 255
+18 -19
src/applications/dashboard/query/PhabricatorDashboardPanelQuery.php
··· 29 29 } 30 30 31 31 protected function loadPage() { 32 - $table = new PhabricatorDashboardPanel(); 33 - $conn_r = $table->establishConnection('r'); 32 + return $this->loadStandardPage($this->newResultObject()); 33 + } 34 34 35 - $data = queryfx_all( 36 - $conn_r, 37 - 'SELECT * FROM %T %Q %Q %Q', 38 - $table->getTableName(), 39 - $this->buildWhereClause($conn_r), 40 - $this->buildOrderClause($conn_r), 41 - $this->buildLimitClause($conn_r)); 35 + public function newResultObject() { 36 + // TODO: If we don't do this, SearchEngine explodes when trying to 37 + // enumerate custom fields. For now, just give the panel a default panel 38 + // type so custom fields work. In the long run, we may want to find a 39 + // cleaner or more general approach for this. 40 + $text_type = id(new PhabricatorDashboardTextPanelType()) 41 + ->getPanelTypeKey(); 42 42 43 - return $table->loadAllFromArray($data); 43 + return id(new PhabricatorDashboardPanel()) 44 + ->setPanelType($text_type); 44 45 } 45 46 46 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 47 - $where = array(); 47 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 48 + $where = parent::buildWhereClauseParts($conn); 48 49 49 50 if ($this->ids !== null) { 50 51 $where[] = qsprintf( 51 - $conn_r, 52 + $conn, 52 53 'id IN (%Ld)', 53 54 $this->ids); 54 55 } 55 56 56 57 if ($this->phids !== null) { 57 58 $where[] = qsprintf( 58 - $conn_r, 59 + $conn, 59 60 'phid IN (%Ls)', 60 61 $this->phids); 61 62 } 62 63 63 64 if ($this->archived !== null) { 64 65 $where[] = qsprintf( 65 - $conn_r, 66 + $conn, 66 67 'isArchived = %d', 67 68 (int)$this->archived); 68 69 } 69 70 70 71 if ($this->panelTypes !== null) { 71 72 $where[] = qsprintf( 72 - $conn_r, 73 + $conn, 73 74 'panelType IN (%Ls)', 74 75 $this->panelTypes); 75 76 } 76 77 77 - $where[] = $this->buildPagingClause($conn_r); 78 - 79 - return $this->formatWhereClause($where); 78 + return $where; 80 79 } 81 80 82 81 public function getQueryApplicationClass() {
+33 -50
src/applications/dashboard/query/PhabricatorDashboardPanelSearchEngine.php
··· 11 11 return 'PhabricatorDashboardApplication'; 12 12 } 13 13 14 - public function buildSavedQueryFromRequest(AphrontRequest $request) { 15 - $saved = new PhabricatorSavedQuery(); 16 - $saved->setParameter('status', $request->getStr('status')); 17 - $saved->setParameter('paneltype', $request->getStr('paneltype')); 18 - return $saved; 14 + public function newQuery() { 15 + return new PhabricatorDashboardPanelQuery(); 19 16 } 20 17 21 - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 22 - $query = id(new PhabricatorDashboardPanelQuery()); 23 - 24 - $status = $saved->getParameter('status'); 25 - switch ($status) { 26 - case 'active': 27 - $query->withArchived(false); 28 - break; 29 - case 'archived': 30 - $query->withArchived(true); 31 - break; 32 - default: 33 - break; 18 + protected function buildQueryFromParameters(array $map) { 19 + $query = $this->newQuery(); 20 + if ($map['status']) { 21 + switch ($map['status']) { 22 + case 'active': 23 + $query->withArchived(false); 24 + break; 25 + case 'archived': 26 + $query->withArchived(true); 27 + break; 28 + default: 29 + break; 30 + } 34 31 } 35 32 36 - $paneltype = $saved->getParameter('paneltype'); 37 - if ($paneltype) { 38 - $query->withPanelTypes(array($paneltype)); 33 + if ($map['paneltype']) { 34 + $query->withPanelTypes(array($map['paneltype'])); 39 35 } 40 36 41 37 return $query; 42 38 } 43 39 44 - public function buildSearchForm( 45 - AphrontFormView $form, 46 - PhabricatorSavedQuery $saved_query) { 40 + protected function buildCustomSearchFields() { 47 41 48 - $status = $saved_query->getParameter('status', ''); 49 - $paneltype = $saved_query->getParameter('paneltype', ''); 50 - 51 - $panel_types = PhabricatorDashboardPanelType::getAllPanelTypes(); 52 - $panel_types = mpull($panel_types, 'getPanelTypeName', 'getPanelTypeKey'); 53 - asort($panel_types); 54 - $panel_types = (array('' => pht('(All Types)')) + $panel_types); 55 - 56 - $form 57 - ->appendChild( 58 - id(new AphrontFormSelectControl()) 42 + return array( 43 + id(new PhabricatorSearchSelectField()) 44 + ->setKey('status') 59 45 ->setLabel(pht('Status')) 60 - ->setName('status') 61 - ->setValue($status) 62 46 ->setOptions( 63 - array( 64 - '' => pht('(All Panels)'), 65 - 'active' => pht('Active Panels'), 66 - 'archived' => pht('Archived Panels'), 67 - ))) 68 - ->appendChild( 69 - id(new AphrontFormSelectControl()) 47 + id(new PhabricatorDashboardPanel()) 48 + ->getStatuses()), 49 + id(new PhabricatorSearchSelectField()) 50 + ->setKey('paneltype') 70 51 ->setLabel(pht('Panel Type')) 71 - ->setName('paneltype') 72 - ->setValue($paneltype) 73 - ->setOptions($panel_types)); 52 + ->setOptions( 53 + id(new PhabricatorDashboardPanel()) 54 + ->getPanelTypes()), 55 + ); 74 56 } 75 57 76 58 protected function getURI($path) { ··· 117 99 $impl = $panel->getImplementation(); 118 100 if ($impl) { 119 101 $type_text = $impl->getPanelTypeName(); 120 - $type_icon = 'none'; 121 102 } else { 122 103 $type_text = nonempty($panel->getPanelType(), pht('Unknown Type')); 123 - $type_icon = 'fa-question'; 124 104 } 105 + $item->addAttribute($type_text); 125 106 126 - $item->addIcon($type_icon, $type_text); 107 + $properties = $panel->getProperties(); 108 + $class = idx($properties, 'class'); 109 + $item->addAttribute($class); 127 110 128 111 if ($panel->getIsArchived()) { 129 112 $item->setDisabled(true);
+37 -21
src/applications/dashboard/query/PhabricatorDashboardQuery.php
··· 7 7 private $phids; 8 8 9 9 private $needPanels; 10 + private $needProjects; 10 11 11 12 public function withIDs(array $ids) { 12 13 $this->ids = $ids; ··· 23 24 return $this; 24 25 } 25 26 27 + public function needProjects($need_projects) { 28 + $this->needProjects = $need_projects; 29 + return $this; 30 + } 31 + 26 32 protected function loadPage() { 27 - $table = new PhabricatorDashboard(); 28 - $conn_r = $table->establishConnection('r'); 33 + return $this->loadStandardPage($this->newResultObject()); 34 + } 29 35 30 - $data = queryfx_all( 31 - $conn_r, 32 - 'SELECT * FROM %T %Q %Q %Q', 33 - $table->getTableName(), 34 - $this->buildWhereClause($conn_r), 35 - $this->buildOrderClause($conn_r), 36 - $this->buildLimitClause($conn_r)); 37 - 38 - return $table->loadAllFromArray($data); 36 + public function newResultObject() { 37 + return new PhabricatorDashboard(); 39 38 } 40 39 41 40 protected function didFilterPage(array $dashboards) { 41 + 42 + $phids = mpull($dashboards, 'getPHID'); 43 + 42 44 if ($this->needPanels) { 43 45 $edge_query = id(new PhabricatorEdgeQuery()) 44 - ->withSourcePHIDs(mpull($dashboards, 'getPHID')) 46 + ->withSourcePHIDs($phids) 45 47 ->withEdgeTypes( 46 48 array( 47 49 PhabricatorDashboardDashboardHasPanelEdgeType::EDGECONST, ··· 70 72 } 71 73 } 72 74 75 + if ($this->needProjects) { 76 + $edge_query = id(new PhabricatorEdgeQuery()) 77 + ->withSourcePHIDs($phids) 78 + ->withEdgeTypes( 79 + array( 80 + PhabricatorProjectObjectHasProjectEdgeType::EDGECONST, 81 + )); 82 + $edge_query->execute(); 83 + 84 + foreach ($dashboards as $dashboard) { 85 + $project_phids = $edge_query->getDestinationPHIDs( 86 + array($dashboard->getPHID())); 87 + $dashboard->attachProjectPHIDs($project_phids); 88 + } 89 + } 90 + 73 91 return $dashboards; 74 92 } 75 93 76 - protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { 77 - $where = array(); 94 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 95 + $where = parent::buildWhereClauseParts($conn); 78 96 79 - if ($this->ids) { 97 + if ($this->ids !== null) { 80 98 $where[] = qsprintf( 81 - $conn_r, 99 + $conn, 82 100 'id IN (%Ld)', 83 101 $this->ids); 84 102 } 85 103 86 - if ($this->phids) { 104 + if ($this->phids !== null) { 87 105 $where[] = qsprintf( 88 - $conn_r, 106 + $conn, 89 107 'phid IN (%Ls)', 90 108 $this->phids); 91 109 } 92 110 93 - $where[] = $this->buildPagingClause($conn_r); 94 - 95 - return $this->formatWhereClause($where); 111 + return $where; 96 112 } 97 113 98 114 public function getQueryApplicationClass() {
+33 -11
src/applications/dashboard/query/PhabricatorDashboardSearchEngine.php
··· 11 11 return 'PhabricatorDashboardApplication'; 12 12 } 13 13 14 - public function buildSavedQueryFromRequest(AphrontRequest $request) { 15 - return new PhabricatorSavedQuery(); 14 + public function newQuery() { 15 + return id(new PhabricatorDashboardQuery()) 16 + ->needProjects(true); 16 17 } 17 18 18 - public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { 19 - return new PhabricatorDashboardQuery(); 20 - } 21 - 22 - public function buildSearchForm( 23 - AphrontFormView $form, 24 - PhabricatorSavedQuery $saved_query) { 25 - return; 19 + protected function buildCustomSearchFields() { 20 + return array(); 26 21 } 27 22 28 23 protected function getURI($path) { ··· 48 43 return parent::buildSavedQueryFromBuiltin($query_key); 49 44 } 50 45 46 + protected function buildQueryFromParameters(array $map) { 47 + $query = $this->newQuery(); 48 + return $query; 49 + } 50 + 51 51 protected function renderResultList( 52 52 array $dashboards, 53 53 PhabricatorSavedQuery $query, ··· 70 70 $installs = array(); 71 71 } 72 72 73 + $proj_phids = array(); 74 + foreach ($dashboards as $dashboard) { 75 + foreach ($dashboard->getProjectPHIDs() as $project_phid) { 76 + $proj_phids[] = $project_phid; 77 + } 78 + } 79 + 80 + $proj_handles = id(new PhabricatorHandleQuery()) 81 + ->setViewer($viewer) 82 + ->withPHIDs($proj_phids) 83 + ->execute(); 84 + 73 85 $list = new PHUIObjectItemListView(); 74 86 $list->setUser($viewer); 75 87 $list->initBehavior('phabricator-tooltips', array()); ··· 101 113 } 102 114 } 103 115 116 + $project_handles = array_select_keys( 117 + $proj_handles, 118 + $dashboard->getProjectPHIDs()); 119 + 120 + $item->addAttribute( 121 + id(new PHUIHandleTagListView()) 122 + ->setLimit(4) 123 + ->setNoDataString(pht('No Projects')) 124 + ->setSlim(true) 125 + ->setHandles($project_handles)); 126 + 104 127 $list->addItem($item); 105 128 } 106 129 ··· 109 132 $result->setNoDataString(pht('No dashboards found.')); 110 133 111 134 return $result; 112 - 113 135 } 114 136 115 137 }
+13 -1
src/applications/dashboard/storage/PhabricatorDashboard.php
··· 8 8 PhabricatorApplicationTransactionInterface, 9 9 PhabricatorPolicyInterface, 10 10 PhabricatorFlaggableInterface, 11 - PhabricatorDestructibleInterface { 11 + PhabricatorDestructibleInterface, 12 + PhabricatorProjectInterface { 12 13 13 14 protected $name; 14 15 protected $viewPolicy; ··· 17 18 18 19 private $panelPHIDs = self::ATTACHABLE; 19 20 private $panels = self::ATTACHABLE; 21 + private $edgeProjectPHIDs = self::ATTACHABLE; 22 + 20 23 21 24 public static function initializeNewDashboard(PhabricatorUser $actor) { 22 25 return id(new PhabricatorDashboard()) ··· 62 65 public function setLayoutConfigFromObject( 63 66 PhabricatorDashboardLayoutConfig $object) { 64 67 $this->setLayoutConfig($object->toDictionary()); 68 + return $this; 69 + } 70 + 71 + public function getProjectPHIDs() { 72 + return $this->assertAttached($this->edgeProjectPHIDs); 73 + } 74 + 75 + public function attachProjectPHIDs(array $phids) { 76 + $this->edgeProjectPHIDs = $phids; 65 77 return $this; 66 78 } 67 79
+19
src/applications/dashboard/storage/PhabricatorDashboardPanel.php
··· 10 10 PhabricatorPolicyInterface, 11 11 PhabricatorCustomFieldInterface, 12 12 PhabricatorFlaggableInterface, 13 + PhabricatorProjectInterface, 13 14 PhabricatorDestructibleInterface { 14 15 15 16 protected $name; ··· 69 70 70 71 public function getMonogram() { 71 72 return 'W'.$this->getID(); 73 + } 74 + 75 + public function getPanelTypes() { 76 + $panel_types = PhabricatorDashboardPanelType::getAllPanelTypes(); 77 + $panel_types = mpull($panel_types, 'getPanelTypeName', 'getPanelTypeKey'); 78 + asort($panel_types); 79 + $panel_types = (array('' => pht('(All Types)')) + $panel_types); 80 + return $panel_types; 81 + } 82 + 83 + public function getStatuses() { 84 + $statuses = 85 + array( 86 + '' => pht('(All Panels)'), 87 + 'active' => pht('Active Panels'), 88 + 'archived' => pht('Archived Panels'), 89 + ); 90 + return $statuses; 72 91 } 73 92 74 93 public function getImplementation() {