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

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

+229
+15
resources/sql/autopatches/20140430.dash.2.edge.sql
··· 1 + CREATE TABLE {$NAMESPACE}_dashboard.edge ( 2 + src VARCHAR(64) NOT NULL COLLATE utf8_bin, 3 + type VARCHAR(64) NOT NULL COLLATE utf8_bin, 4 + dst VARCHAR(64) NOT NULL COLLATE utf8_bin, 5 + dateCreated INT UNSIGNED NOT NULL, 6 + seq INT UNSIGNED NOT NULL, 7 + dataID INT UNSIGNED, 8 + PRIMARY KEY (src, type, dst), 9 + KEY (src, type, dateCreated, seq) 10 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 11 + 12 + CREATE TABLE {$NAMESPACE}_dashboard.edgedata ( 13 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 14 + data LONGTEXT NOT NULL COLLATE utf8_bin 15 + ) ENGINE=InnoDB, COLLATE utf8_general_ci;
+2
src/__phutil_library_map__.php
··· 1432 1432 'PhabricatorDaemonReference' => 'infrastructure/daemon/control/PhabricatorDaemonReference.php', 1433 1433 'PhabricatorDaemonTaskGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php', 1434 1434 'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php', 1435 + 'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php', 1435 1436 'PhabricatorDashboardController' => 'applications/dashboard/controller/PhabricatorDashboardController.php', 1436 1437 'PhabricatorDashboardDAO' => 'applications/dashboard/storage/PhabricatorDashboardDAO.php', 1437 1438 'PhabricatorDashboardEditController' => 'applications/dashboard/controller/PhabricatorDashboardEditController.php', ··· 4232 4233 0 => 'PhabricatorDashboardDAO', 4233 4234 1 => 'PhabricatorPolicyInterface', 4234 4235 ), 4236 + 'PhabricatorDashboardAddPanelController' => 'PhabricatorDashboardController', 4235 4237 'PhabricatorDashboardController' => 'PhabricatorController', 4236 4238 'PhabricatorDashboardDAO' => 'PhabricatorLiskDAO', 4237 4239 'PhabricatorDashboardEditController' => 'PhabricatorDashboardController',
+1
src/applications/dashboard/application/PhabricatorApplicationDashboard.php
··· 23 23 'view/(?P<id>\d+)/' => 'PhabricatorDashboardViewController', 24 24 'create/' => 'PhabricatorDashboardEditController', 25 25 'edit/(?:(?P<id>\d+)/)?' => 'PhabricatorDashboardEditController', 26 + 'addpanel/(?P<id>\d+)/' => 'PhabricatorDashboardAddPanelController', 26 27 27 28 'panel/' => array( 28 29 '(?:query/(?P<queryKey>[^/]+)/)?'
+94
src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php
··· 1 + <?php 2 + 3 + final class PhabricatorDashboardAddPanelController 4 + extends PhabricatorDashboardController { 5 + 6 + private $id; 7 + 8 + public function willProcessRequest(array $data) { 9 + $this->id = idx($data, 'id'); 10 + } 11 + 12 + public function processRequest() { 13 + $request = $this->getRequest(); 14 + $viewer = $request->getUser(); 15 + 16 + $dashboard = id(new PhabricatorDashboardQuery()) 17 + ->setViewer($viewer) 18 + ->withIDs(array($this->id)) 19 + ->requireCapabilities( 20 + array( 21 + PhabricatorPolicyCapability::CAN_VIEW, 22 + PhabricatorPolicyCapability::CAN_EDIT, 23 + )) 24 + ->executeOne(); 25 + if (!$dashboard) { 26 + return new Aphront404Response(); 27 + } 28 + 29 + $dashboard_uri = $this->getApplicationURI('view/'.$dashboard->getID().'/'); 30 + 31 + $v_panel = $request->getStr('panel'); 32 + $e_panel = true; 33 + $errors = array(); 34 + if ($request->isFormPost()) { 35 + if (strlen($v_panel)) { 36 + $panel = id(new PhabricatorObjectQuery()) 37 + ->setViewer($viewer) 38 + ->withNames(array($v_panel)) 39 + ->withTypes(array(PhabricatorDashboardPHIDTypePanel::TYPECONST)) 40 + ->executeOne(); 41 + if (!$panel) { 42 + $errors[] = pht('No such panel!'); 43 + $e_panel = pht('Invalid'); 44 + } 45 + } else { 46 + $errors[] = pht('Name a panel to add.'); 47 + $e_panel = pht('Required'); 48 + } 49 + 50 + if (!$errors) { 51 + $xactions = array(); 52 + $xactions[] = id(new PhabricatorDashboardTransaction()) 53 + ->setTransactionType(PhabricatorTransactions::TYPE_EDGE) 54 + ->setMetadataValue( 55 + 'edge:type', 56 + PhabricatorEdgeConfig::TYPE_DASHBOARD_HAS_PANEL) 57 + ->setNewValue( 58 + array( 59 + '+' => array( 60 + $panel->getPHID() => $panel->getPHID(), 61 + ), 62 + )); 63 + 64 + $editor = id(new PhabricatorDashboardTransactionEditor()) 65 + ->setActor($viewer) 66 + ->setContentSourceFromRequest($request) 67 + ->setContinueOnMissingFields(true) 68 + ->setContinueOnNoEffect(true) 69 + ->applyTransactions($dashboard, $xactions); 70 + 71 + return id(new AphrontRedirectResponse())->setURI($dashboard_uri); 72 + } 73 + } 74 + 75 + $form = id(new AphrontFormView()) 76 + ->setUser($viewer) 77 + ->appendRemarkupInstructions( 78 + pht('Enter a panel monogram like `W123`.')) 79 + ->appendChild( 80 + id(new AphrontFormTextControl()) 81 + ->setName('panel') 82 + ->setLabel(pht('Panel')) 83 + ->setValue($v_panel) 84 + ->setError($e_panel)); 85 + 86 + return $this->newDialog() 87 + ->setTitle(pht('Add Panel')) 88 + ->setErrors($errors) 89 + ->appendChild($form->buildLayoutView()) 90 + ->addCancelButton($dashboard_uri) 91 + ->addSubmitButton(pht('Add Panel')); 92 + } 93 + 94 + }
+9
src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php
··· 124 124 pht('Editable By'), 125 125 $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); 126 126 127 + $dashboard_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( 128 + $panel->getPHID(), 129 + PhabricatorEdgeConfig::TYPE_PANEL_HAS_DASHBOARD); 130 + $this->loadHandles($dashboard_phids); 131 + 132 + $properties->addProperty( 133 + pht('Appears On'), 134 + $this->renderHandlesForPHIDs($dashboard_phids)); 135 + 127 136 return $properties; 128 137 } 129 138
+16
src/applications/dashboard/controller/PhabricatorDashboardViewController.php
··· 16 16 $dashboard = id(new PhabricatorDashboardQuery()) 17 17 ->setViewer($viewer) 18 18 ->withIDs(array($this->id)) 19 + ->needPanels(true) 19 20 ->executeOne(); 20 21 if (!$dashboard) { 21 22 return new Aphront404Response(); ··· 77 78 ->setDisabled(!$can_edit) 78 79 ->setWorkflow(!$can_edit)); 79 80 81 + $actions->addAction( 82 + id(new PhabricatorActionView()) 83 + ->setName(pht('Add Panel')) 84 + ->setIcon('new') 85 + ->setHref($this->getApplicationURI("addpanel/{$id}/")) 86 + ->setDisabled(!$can_edit) 87 + ->setWorkflow(true)); 88 + 80 89 return $actions; 81 90 } 82 91 ··· 94 103 $properties->addProperty( 95 104 pht('Editable By'), 96 105 $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); 106 + 107 + $panel_phids = $dashboard->getPanelPHIDs(); 108 + $this->loadHandles($panel_phids); 109 + 110 + $properties->addProperty( 111 + pht('Panels'), 112 + $this->renderHandlesForPHIDs($panel_phids)); 97 113 98 114 return $properties; 99 115 }
+1
src/applications/dashboard/editor/PhabricatorDashboardPanelTransactionEditor.php
··· 8 8 9 9 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 10 10 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 11 + $types[] = PhabricatorTransactions::TYPE_EDGE; 11 12 12 13 $types[] = PhabricatorDashboardPanelTransaction::TYPE_NAME; 13 14
+5
src/applications/dashboard/editor/PhabricatorDashboardTransactionEditor.php
··· 8 8 9 9 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 10 10 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; 11 + $types[] = PhabricatorTransactions::TYPE_EDGE; 11 12 12 13 $types[] = PhabricatorDashboardTransaction::TYPE_NAME; 13 14 ··· 51 52 case PhabricatorTransactions::TYPE_EDIT_POLICY: 52 53 $object->setEditPolicy($xaction->getNewValue()); 53 54 return; 55 + case PhabricatorTransactions::TYPE_EDGE: 56 + return; 54 57 } 55 58 56 59 return parent::applyCustomInternalTransaction($object, $xaction); ··· 62 65 63 66 switch ($xaction->getTransactionType()) { 64 67 case PhabricatorDashboardTransaction::TYPE_NAME: 68 + return; 69 + case PhabricatorTransactions::TYPE_EDGE: 65 70 return; 66 71 } 67 72
+42
src/applications/dashboard/query/PhabricatorDashboardQuery.php
··· 6 6 private $ids; 7 7 private $phids; 8 8 9 + private $needPanels; 10 + 9 11 public function withIDs(array $ids) { 10 12 $this->ids = $ids; 11 13 return $this; ··· 16 18 return $this; 17 19 } 18 20 21 + public function needPanels($need_panels) { 22 + $this->needPanels = $need_panels; 23 + return $this; 24 + } 25 + 19 26 protected function loadPage() { 20 27 $table = new PhabricatorDashboard(); 21 28 $conn_r = $table->establishConnection('r'); ··· 29 36 $this->buildLimitClause($conn_r)); 30 37 31 38 return $table->loadAllFromArray($data); 39 + } 40 + 41 + protected function didFilterPage(array $dashboards) { 42 + if ($this->needPanels) { 43 + $edge_query = id(new PhabricatorEdgeQuery()) 44 + ->withSourcePHIDs(mpull($dashboards, 'getPHID')) 45 + ->withEdgeTypes( 46 + array( 47 + PhabricatorEdgeConfig::TYPE_DASHBOARD_HAS_PANEL, 48 + )); 49 + $edge_query->execute(); 50 + 51 + $panel_phids = $edge_query->getDestinationPHIDs(); 52 + if ($panel_phids) { 53 + $panels = id(new PhabricatorDashboardPanelQuery()) 54 + ->setParentQuery($this) 55 + ->setViewer($this->getViewer()) 56 + ->withPHIDs($panel_phids) 57 + ->execute(); 58 + $panels = mpull($panels, null, 'getPHID'); 59 + } else { 60 + $panels = array(); 61 + } 62 + 63 + foreach ($dashboards as $dashboard) { 64 + $dashboard_phids = $edge_query->getDestinationPHIDs( 65 + array($dashboard->getPHID())); 66 + $dashboard_panels = array_select_keys($panels, $dashboard_phids); 67 + 68 + $dashboard->attachPanelPHIDs($dashboard_phids); 69 + $dashboard->attachPanels($dashboard_panels); 70 + } 71 + } 72 + 73 + return $dashboards; 32 74 } 33 75 34 76 protected function buildWhereClause($conn_r) {
+22
src/applications/dashboard/storage/PhabricatorDashboard.php
··· 10 10 protected $viewPolicy; 11 11 protected $editPolicy; 12 12 13 + private $panelPHIDs = self::ATTACHABLE; 14 + private $panels = self::ATTACHABLE; 15 + 13 16 public static function initializeNewDashboard(PhabricatorUser $actor) { 14 17 return id(new PhabricatorDashboard()) 15 18 ->setName('') ··· 26 29 public function generatePHID() { 27 30 return PhabricatorPHID::generateNewPHID( 28 31 PhabricatorDashboardPHIDTypeDashboard::TYPECONST); 32 + } 33 + 34 + public function attachPanelPHIDs(array $phids) { 35 + $this->panelPHIDs = $phids; 36 + return $this; 37 + } 38 + 39 + public function getPanelPHIDs() { 40 + return $this->assertAttached($this->panelPHIDs); 41 + } 42 + 43 + public function attachPanels(array $panels) { 44 + assert_instances_of($panels, 'PhabricatorDashboardPanel'); 45 + $this->panels = $panels; 46 + return $this; 47 + } 48 + 49 + public function getPanels() { 50 + return $this->assertAttached($this->panels); 29 51 } 30 52 31 53
+22
src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
··· 69 69 const TYPE_OBJECT_HAS_COLUMN = 43; 70 70 const TYPE_COLUMN_HAS_OBJECT = 44; 71 71 72 + const TYPE_DASHBOARD_HAS_PANEL = 45; 73 + const TYPE_PANEL_HAS_DASHBOARD = 46; 74 + 72 75 const TYPE_TEST_NO_CYCLE = 9000; 73 76 74 77 const TYPE_PHOB_HAS_ASANATASK = 80001; ··· 153 156 154 157 self::TYPE_OBJECT_HAS_COLUMN => self::TYPE_COLUMN_HAS_OBJECT, 155 158 self::TYPE_COLUMN_HAS_OBJECT => self::TYPE_OBJECT_HAS_COLUMN, 159 + 160 + self::TYPE_PANEL_HAS_DASHBOARD => self::TYPE_DASHBOARD_HAS_PANEL, 161 + self::TYPE_DASHBOARD_HAS_PANEL => self::TYPE_PANEL_HAS_DASHBOARD, 156 162 ); 157 163 158 164 return idx($map, $edge_type); ··· 259 265 return '%s edited reviewer(s), added %d: %s; removed %d: %s.'; 260 266 case self::TYPE_TASK_HAS_MOCK: 261 267 return '%s edited mock(s), added %d: %s; removed %d: %s.'; 268 + case self::TYPE_DASHBOARD_HAS_PANEL: 269 + return '%s edited panel(s), added %d: %s; removed %d: %s.'; 270 + case self::TYPE_PANEL_HAS_DASHBOARD: 271 + return '%s edited dashboard(s), added %d: %s; removed %d: %s.'; 262 272 case self::TYPE_SUBSCRIBED_TO_OBJECT: 263 273 case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: 264 274 case self::TYPE_FILE_HAS_OBJECT: ··· 329 339 return '%s added %d reviewer(s): %s.'; 330 340 case self::TYPE_TASK_HAS_MOCK: 331 341 return '%s added %d mock(s): %s.'; 342 + case self::TYPE_DASHBOARD_HAS_PANEL: 343 + return '%s added %d panel(s): %s.'; 344 + case self::TYPE_PANEL_HAS_DASHBOARD: 345 + return '%s added %d dashboard(s): %s.'; 332 346 case self::TYPE_SUBSCRIBED_TO_OBJECT: 333 347 case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: 334 348 case self::TYPE_FILE_HAS_OBJECT: ··· 400 414 return '%s removed %d reviewer(s): %s.'; 401 415 case self::TYPE_TASK_HAS_MOCK: 402 416 return '%s removed %d mock(s): %s.'; 417 + case self::TYPE_DASHBOARD_HAS_PANEL: 418 + return '%s removed %d panel(s): %s.'; 419 + case self::TYPE_PANEL_HAS_DASHBOARD: 420 + return '%s removed %d dashboard(s): %s.'; 403 421 case self::TYPE_SUBSCRIBED_TO_OBJECT: 404 422 case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: 405 423 case self::TYPE_FILE_HAS_OBJECT: ··· 469 487 return '%s updated reviewers of %s.'; 470 488 case self::TYPE_TASK_HAS_MOCK: 471 489 return '%s updated mocks of %s.'; 490 + case self::TYPE_PANEL_HAS_DASHBOARD: 491 + return '%s updated panels for %s.'; 492 + case self::TYPE_PANEL_HAS_DASHBOARD: 493 + return '%s updated dashboards for %s.'; 472 494 case self::TYPE_SUBSCRIBED_TO_OBJECT: 473 495 case self::TYPE_UNSUBSCRIBED_FROM_OBJECT: 474 496 case self::TYPE_FILE_HAS_OBJECT: