diff --git a/resources/celerity/map.php b/resources/celerity/map.php --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -9,7 +9,7 @@ 'names' => array( 'conpherence.pkg.css' => '3c8a0668', 'conpherence.pkg.js' => '020aebcf', - 'core.pkg.css' => '9d654dff', + 'core.pkg.css' => '1db0892b', 'core.pkg.js' => '794952ae', 'differential.pkg.css' => '8d8360fb', 'differential.pkg.js' => '67e02996', @@ -164,7 +164,7 @@ 'rsrc/css/phui/phui-invisible-character-view.css' => 'c694c4a4', 'rsrc/css/phui/phui-left-right.css' => '68513c34', 'rsrc/css/phui/phui-lightbox.css' => '4ebf22da', - 'rsrc/css/phui/phui-list.css' => '734a1039', + 'rsrc/css/phui/phui-list.css' => 'b05144dd', 'rsrc/css/phui/phui-object-box.css' => 'f434b6be', 'rsrc/css/phui/phui-pager.css' => 'd022c7ad', 'rsrc/css/phui/phui-pinboard-view.css' => '1f08f5d8', @@ -847,7 +847,7 @@ 'phui-invisible-character-view-css' => 'c694c4a4', 'phui-left-right-css' => '68513c34', 'phui-lightbox-css' => '4ebf22da', - 'phui-list-view-css' => '734a1039', + 'phui-list-view-css' => 'b05144dd', 'phui-object-box-css' => 'f434b6be', 'phui-oi-big-ui-css' => 'fa74cc35', 'phui-oi-color-css' => 'b517bfa0', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -3064,6 +3064,7 @@ 'PhabricatorEdgeEditType' => 'applications/transactions/edittype/PhabricatorEdgeEditType.php', 'PhabricatorEdgeEditor' => 'infrastructure/edges/editor/PhabricatorEdgeEditor.php', 'PhabricatorEdgeGraph' => 'infrastructure/edges/util/PhabricatorEdgeGraph.php', + 'PhabricatorEdgeIndexEngineExtension' => 'applications/search/engineextension/PhabricatorEdgeIndexEngineExtension.php', 'PhabricatorEdgeObject' => 'infrastructure/edges/conduit/PhabricatorEdgeObject.php', 'PhabricatorEdgeObjectQuery' => 'infrastructure/edges/query/PhabricatorEdgeObjectQuery.php', 'PhabricatorEdgeQuery' => 'infrastructure/edges/query/PhabricatorEdgeQuery.php', @@ -4067,11 +4068,13 @@ 'PhabricatorProfileMenuEditor' => 'applications/search/editor/PhabricatorProfileMenuEditor.php', 'PhabricatorProfileMenuEngine' => 'applications/search/engine/PhabricatorProfileMenuEngine.php', 'PhabricatorProfileMenuItem' => 'applications/search/menuitem/PhabricatorProfileMenuItem.php', + 'PhabricatorProfileMenuItemAffectsObjectEdgeType' => 'applications/search/edge/PhabricatorProfileMenuItemAffectsObjectEdgeType.php', 'PhabricatorProfileMenuItemConfiguration' => 'applications/search/storage/PhabricatorProfileMenuItemConfiguration.php', 'PhabricatorProfileMenuItemConfigurationQuery' => 'applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php', 'PhabricatorProfileMenuItemConfigurationTransaction' => 'applications/search/storage/PhabricatorProfileMenuItemConfigurationTransaction.php', 'PhabricatorProfileMenuItemConfigurationTransactionQuery' => 'applications/search/query/PhabricatorProfileMenuItemConfigurationTransactionQuery.php', 'PhabricatorProfileMenuItemIconSet' => 'applications/search/menuitem/PhabricatorProfileMenuItemIconSet.php', + 'PhabricatorProfileMenuItemIndexEngineExtension' => 'applications/search/engineextension/PhabricatorProfileMenuItemIndexEngineExtension.php', 'PhabricatorProfileMenuItemPHIDType' => 'applications/search/phidtype/PhabricatorProfileMenuItemPHIDType.php', 'PhabricatorProfileMenuItemView' => 'applications/search/engine/PhabricatorProfileMenuItemView.php', 'PhabricatorProfileMenuItemViewList' => 'applications/search/engine/PhabricatorProfileMenuItemViewList.php', @@ -7292,7 +7295,7 @@ 'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor', 'HeraldRuleField' => 'HeraldField', 'HeraldRuleFieldGroup' => 'HeraldFieldGroup', - 'HeraldRuleIndexEngineExtension' => 'PhabricatorIndexEngineExtension', + 'HeraldRuleIndexEngineExtension' => 'PhabricatorEdgeIndexEngineExtension', 'HeraldRuleListController' => 'HeraldController', 'HeraldRuleListView' => 'AphrontView', 'HeraldRuleNameTransaction' => 'HeraldRuleTransactionType', @@ -9056,6 +9059,7 @@ 'PhabricatorEdgeEditType' => 'PhabricatorPHIDListEditType', 'PhabricatorEdgeEditor' => 'Phobject', 'PhabricatorEdgeGraph' => 'AbstractDirectedGraph', + 'PhabricatorEdgeIndexEngineExtension' => 'PhabricatorIndexEngineExtension', 'PhabricatorEdgeObject' => array( 'Phobject', 'PhabricatorPolicyInterface', @@ -10211,16 +10215,19 @@ 'PhabricatorProfileMenuEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorProfileMenuEngine' => 'Phobject', 'PhabricatorProfileMenuItem' => 'Phobject', + 'PhabricatorProfileMenuItemAffectsObjectEdgeType' => 'PhabricatorEdgeType', 'PhabricatorProfileMenuItemConfiguration' => array( 'PhabricatorSearchDAO', 'PhabricatorPolicyInterface', 'PhabricatorExtendedPolicyInterface', 'PhabricatorApplicationTransactionInterface', + 'PhabricatorIndexableInterface', ), 'PhabricatorProfileMenuItemConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorProfileMenuItemConfigurationTransaction' => 'PhabricatorApplicationTransaction', 'PhabricatorProfileMenuItemConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorProfileMenuItemIconSet' => 'PhabricatorIconSet', + 'PhabricatorProfileMenuItemIndexEngineExtension' => 'PhabricatorEdgeIndexEngineExtension', 'PhabricatorProfileMenuItemPHIDType' => 'PhabricatorPHIDType', 'PhabricatorProfileMenuItemView' => 'Phobject', 'PhabricatorProfileMenuItemViewList' => 'Phobject', diff --git a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardViewController.php b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardViewController.php --- a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardViewController.php +++ b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardViewController.php @@ -32,6 +32,8 @@ $curtain = $this->buildCurtainView($dashboard); + $usage_box = $this->newUsageView($dashboard); + $timeline = $this->buildTransactionTimeline( $dashboard, new PhabricatorDashboardTransactionQuery()); @@ -53,6 +55,7 @@ ->setMainColumn( array( $dashboard_box, + $usage_box, $timeline, )); @@ -110,5 +113,89 @@ return $curtain; } + private function newUsageView(PhabricatorDashboard $dashboard) { + $viewer = $this->getViewer(); + + $custom_phids = array(); + if ($viewer->getPHID()) { + $custom_phids[] = $viewer->getPHID(); + } + + $items = id(new PhabricatorProfileMenuItemConfigurationQuery()) + ->setViewer($viewer) + ->withAffectedObjectPHIDs( + array( + $dashboard->getPHID(), + )) + ->withCustomPHIDs($custom_phids, $include_global = true) + ->execute(); + + $handle_phids = array(); + foreach ($items as $item) { + $handle_phids[] = $item->getProfilePHID(); + $custom_phid = $item->getCustomPHID(); + if ($custom_phid) { + $handle_phids[] = $custom_phid; + } + } + + if ($handle_phids) { + $handles = $viewer->loadHandles($handle_phids); + } else { + $handles = array(); + } + + $items = msortv($items, 'newUsageSortVector'); + + $rows = array(); + foreach ($items as $item) { + $profile_phid = $item->getProfilePHID(); + $custom_phid = $item->getCustomPHID(); + + $profile = $handles[$profile_phid]->renderLink(); + $profile_icon = $handles[$profile_phid]->getIcon(); + + if ($custom_phid) { + $custom = $handles[$custom_phid]->renderLink(); + } else { + $custom = pht('Global'); + } + + $type = $item->getProfileMenuTypeDescription(); + + $rows[] = array( + id(new PHUIIconView())->setIcon($profile_icon), + $type, + $profile, + $custom, + ); + } + + $usage_table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + null, + pht('Type'), + pht('Menu'), + pht('Global/Personal'), + )) + ->setColumnClasses( + array( + 'center', + null, + 'pri', + 'wide', + )); + + $header_view = id(new PHUIHeaderView()) + ->setHeader(pht('Dashboard Used By')); + + $usage_box = id(new PHUIObjectBoxView()) + ->setTable($usage_table) + ->setHeader($header_view); + + return $usage_box; + } + } diff --git a/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php b/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php --- a/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php +++ b/src/applications/dashboard/phid/PhabricatorDashboardPortalPHIDType.php @@ -34,6 +34,7 @@ $portal = $objects[$phid]; $handle + ->setIcon('fa-compass') ->setName($portal->getName()) ->setURI($portal->getURI()); } diff --git a/src/applications/herald/engineextension/HeraldRuleIndexEngineExtension.php b/src/applications/herald/engineextension/HeraldRuleIndexEngineExtension.php --- a/src/applications/herald/engineextension/HeraldRuleIndexEngineExtension.php +++ b/src/applications/herald/engineextension/HeraldRuleIndexEngineExtension.php @@ -1,7 +1,7 @@ getPHID(), - $edge_type); - $old_edges = array_fuse($old_edges); - - $new_edges = $this->getPHIDsAffectedByActions($object); - $new_edges = array_fuse($new_edges); - - $add_edges = array_diff_key($new_edges, $old_edges); - $rem_edges = array_diff_key($old_edges, $new_edges); - - if (!$add_edges && !$rem_edges) { - return; - } - - $editor = new PhabricatorEdgeEditor(); - - foreach ($add_edges as $phid) { - $editor->addEdge($object->getPHID(), $edge_type, $phid); - } - - foreach ($rem_edges as $phid) { - $editor->removeEdge($object->getPHID(), $edge_type, $phid); - } - - $editor->save(); + protected function getIndexEdgeType() { + return HeraldRuleActionAffectsObjectEdgeType::EDGECONST; } - public function getIndexVersion($object) { - $phids = $this->getPHIDsAffectedByActions($object); - sort($phids); - $phids = implode(':', $phids); - return PhabricatorHash::digestForIndex($phids); - } + protected function getIndexDestinationPHIDs($object) { + $rule = $object; - private function getPHIDsAffectedByActions(HeraldRule $rule) { $viewer = $this->getViewer(); $rule = id(new HeraldRuleQuery()) diff --git a/src/applications/search/edge/PhabricatorProfileMenuItemAffectsObjectEdgeType.php b/src/applications/search/edge/PhabricatorProfileMenuItemAffectsObjectEdgeType.php new file mode 100644 --- /dev/null +++ b/src/applications/search/edge/PhabricatorProfileMenuItemAffectsObjectEdgeType.php @@ -0,0 +1,8 @@ +getIndexEdgeType(); + + $old_edges = PhabricatorEdgeQuery::loadDestinationPHIDs( + $object->getPHID(), + $edge_type); + $old_edges = array_fuse($old_edges); + + $new_edges = $this->getIndexDestinationPHIDs($object); + $new_edges = array_fuse($new_edges); + + $add_edges = array_diff_key($new_edges, $old_edges); + $rem_edges = array_diff_key($old_edges, $new_edges); + + if (!$add_edges && !$rem_edges) { + return; + } + + $editor = new PhabricatorEdgeEditor(); + + foreach ($add_edges as $phid) { + $editor->addEdge($object->getPHID(), $edge_type, $phid); + } + + foreach ($rem_edges as $phid) { + $editor->removeEdge($object->getPHID(), $edge_type, $phid); + } + + $editor->save(); + } + + final public function getIndexVersion($object) { + $phids = $this->getIndexDestinationPHIDs($object); + sort($phids); + $phids = implode(':', $phids); + return PhabricatorHash::digestForIndex($phids); + } + +} diff --git a/src/applications/search/engineextension/PhabricatorProfileMenuItemIndexEngineExtension.php b/src/applications/search/engineextension/PhabricatorProfileMenuItemIndexEngineExtension.php new file mode 100644 --- /dev/null +++ b/src/applications/search/engineextension/PhabricatorProfileMenuItemIndexEngineExtension.php @@ -0,0 +1,28 @@ +getAffectedObjectPHIDs(); + } + +} diff --git a/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php --- a/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorDashboardProfileMenuItem.php @@ -36,11 +36,19 @@ return $this->dashboard; } + public function getAffectedObjectPHIDs( + PhabricatorProfileMenuItemConfiguration $config) { + return array( + $this->getDashboardPHID($config), + ); + } + + public function newPageContent( PhabricatorProfileMenuItemConfiguration $config) { $viewer = $this->getViewer(); - $dashboard_phid = $config->getMenuItemProperty('dashboardPHID'); + $dashboard_phid = $this->getDashboardPHID($config); // Reload the dashboard to attach panels, which we need for rendering. $dashboard = id(new PhabricatorDashboardQuery()) @@ -71,7 +79,7 @@ $viewer = $this->getViewer(); $dashboard_phids = array(); foreach ($items as $item) { - $dashboard_phids[] = $item->getMenuItemProperty('dashboardPHID'); + $dashboard_phids[] = $this->getDashboardPHID($item); } $dashboards = id(new PhabricatorDashboardQuery()) @@ -83,7 +91,7 @@ $dashboards = mpull($dashboards, null, 'getPHID'); foreach ($items as $item) { - $dashboard_phid = $item->getMenuItemProperty('dashboardPHID'); + $dashboard_phid = $this->getDashboardPHID($item); $dashboard = idx($dashboards, $dashboard_phid, null); $menu_item = $item->getMenuItem(); @@ -125,7 +133,7 @@ ->setLabel(pht('Dashboard')) ->setIsRequired(true) ->setDatasource(new PhabricatorDashboardDatasource()) - ->setSingleValue($config->getMenuItemProperty('dashboardPHID')), + ->setSingleValue($this->getDashboardPHID($config)), id(new PhabricatorTextEditField()) ->setKey('name') ->setLabel(pht('Name')) @@ -226,6 +234,11 @@ return $errors; } + private function getDashboardPHID( + PhabricatorProfileMenuItemConfiguration $config) { + return $config->getMenuItemProperty('dashboardPHID'); + } + private function getDashboardHandle() { return $this->dashboardHandle; } diff --git a/src/applications/search/menuitem/PhabricatorProfileMenuItem.php b/src/applications/search/menuitem/PhabricatorProfileMenuItem.php --- a/src/applications/search/menuitem/PhabricatorProfileMenuItem.php +++ b/src/applications/search/menuitem/PhabricatorProfileMenuItem.php @@ -159,4 +159,9 @@ )); } + public function getAffectedObjectPHIDs( + PhabricatorProfileMenuItemConfiguration $config) { + return array(); + } + } diff --git a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php --- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php +++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php @@ -8,6 +8,7 @@ private $profilePHIDs; private $customPHIDs; private $includeGlobal; + private $affectedObjectPHIDs; public function withIDs(array $ids) { $this->ids = $ids; @@ -30,6 +31,11 @@ return $this; } + public function withAffectedObjectPHIDs(array $phids) { + $this->affectedObjectPHIDs = $phids; + return $this; + } + public function newResultObject() { return new PhabricatorProfileMenuItemConfiguration(); } @@ -44,21 +50,21 @@ if ($this->ids !== null) { $where[] = qsprintf( $conn, - 'id IN (%Ld)', + 'config.id IN (%Ld)', $this->ids); } if ($this->phids !== null) { $where[] = qsprintf( $conn, - 'phid IN (%Ls)', + 'config.phid IN (%Ls)', $this->phids); } if ($this->profilePHIDs !== null) { $where[] = qsprintf( $conn, - 'profilePHID IN (%Ls)', + 'config.profilePHID IN (%Ls)', $this->profilePHIDs); } @@ -66,23 +72,45 @@ if ($this->customPHIDs && $this->includeGlobal) { $where[] = qsprintf( $conn, - 'customPHID IN (%Ls) OR customPHID IS NULL', + 'config.customPHID IN (%Ls) OR config.customPHID IS NULL', $this->customPHIDs); } else if ($this->customPHIDs) { $where[] = qsprintf( $conn, - 'customPHID IN (%Ls)', + 'config.customPHID IN (%Ls)', $this->customPHIDs); } else { $where[] = qsprintf( $conn, - 'customPHID IS NULL'); + 'config.customPHID IS NULL'); } } + if ($this->affectedObjectPHIDs !== null) { + $where[] = qsprintf( + $conn, + 'affected.dst IN (%Ls)', + $this->affectedObjectPHIDs); + } + return $where; } + protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { + $joins = parent::buildJoinClauseParts($conn); + + if ($this->affectedObjectPHIDs !== null) { + $joins[] = qsprintf( + $conn, + 'JOIN %T affected ON affected.src = config.phid + AND affected.type = %d', + PhabricatorEdgeConfig::TABLE_NAME_EDGE, + PhabricatorProfileMenuItemAffectsObjectEdgeType::EDGECONST); + } + + return $joins; + } + protected function willFilterPage(array $page) { $items = PhabricatorProfileMenuItem::getAllMenuItems(); foreach ($page as $key => $item) { @@ -128,4 +156,8 @@ return 'PhabricatorSearchApplication'; } + protected function getPrimaryTableAlias() { + return 'config'; + } + } diff --git a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php --- a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php +++ b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php @@ -5,7 +5,8 @@ implements PhabricatorPolicyInterface, PhabricatorExtendedPolicyInterface, - PhabricatorApplicationTransactionInterface { + PhabricatorApplicationTransactionInterface, + PhabricatorIndexableInterface { protected $profilePHID; protected $menuItemKey; @@ -255,6 +256,49 @@ return false; } + public function getAffectedObjectPHIDs() { + return $this->getMenuItem()->getAffectedObjectPHIDs($this); + } + + public function getProfileMenuTypeDescription() { + $profile_phid = $this->getProfilePHID(); + + $home_phid = id(new PhabricatorHomeApplication())->getPHID(); + if ($profile_phid === $home_phid) { + return pht('Home Menu'); + } + + $favorites_phid = id(new PhabricatorFavoritesApplication())->getPHID(); + if ($profile_phid === $favorites_phid) { + return pht('Favorites Menu'); + } + + switch (phid_get_type($profile_phid)) { + case PhabricatorProjectProjectPHIDType::TYPECONST: + return pht('Project Menu'); + case PhabricatorDashboardPortalPHIDType::TYPECONST: + return pht('Portal Menu'); + } + + return pht('Profile Menu'); + } + + public function newUsageSortVector() { + // Used to sort items in contexts where we're showing the usage of an + // object in menus, like "Dashboard Used By" on Dashboard pages. + + // Sort usage as a custom item after usage as a global item. + if ($this->getCustomPHID()) { + $is_personal = 1; + } else { + $is_personal = 0; + } + + return id(new PhutilSortVector()) + ->addInt($is_personal) + ->addInt($this->getID()); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/webroot/rsrc/css/phui/phui-list.css b/webroot/rsrc/css/phui/phui-list.css --- a/webroot/rsrc/css/phui/phui-list.css +++ b/webroot/rsrc/css/phui/phui-list.css @@ -261,7 +261,8 @@ /* - Action Icon ----------------------------------------------------------- */ -.phui-list-sidenav .phui-list-item-has-action-icon .phui-list-item-action-href { +.phabricator-nav-local .phui-list-item-has-action-icon + .phui-list-item-action-href { position: absolute; width: 28px; top: 0; @@ -273,26 +274,27 @@ display: none; } -.phui-list-sidenav .phui-list-item-has-action-icon.phui-list-item-selected +.phabricator-nav-local .phui-list-item-has-action-icon.phui-list-item-selected .phui-list-item-href { padding-right: 32px; } -.phui-list-sidenav .phui-list-item-has-action-icon.phui-list-item-selected +.phabricator-nav-local .phui-list-item-has-action-icon.phui-list-item-selected .phui-list-item-action-href { display: block; } -.phui-list-sidenav .phui-list-item-has-action-icon +.phabricator-nav-local .phui-list-item-has-action-icon .phui-list-item-action-href:hover { background-color: rgba({$alphablack},.05); } -.phui-list-sidenav .phui-list-item-has-action-icon .phui-list-item-action-icon { +.phabricator-nav-local .phui-list-item-has-action-icon + .phui-list-item-action-icon { opacity: 0.5; } -.phui-list-sidenav .phui-list-item-has-action-icon +.phabricator-nav-local .phui-list-item-has-action-icon .phui-list-item-action-href:hover .phui-list-item-action-icon { opacity: 1;