diff --git a/src/applications/favorites/application/PhabricatorFavoritesApplication.php b/src/applications/favorites/application/PhabricatorFavoritesApplication.php --- a/src/applications/favorites/application/PhabricatorFavoritesApplication.php +++ b/src/applications/favorites/application/PhabricatorFavoritesApplication.php @@ -55,11 +55,20 @@ ->withInstalled(true) ->executeOne(); - $filter_view = id(new PhabricatorFavoritesProfileMenuEngine()) + $menu_engine = id(new PhabricatorFavoritesProfileMenuEngine()) ->setViewer($viewer) - ->setProfileObject($favorites) - ->setMenuType(PhabricatorProfileMenuEngine::MENU_COMBINED) - ->buildNavigation(); + ->setProfileObject($favorites); + + if ($viewer->getPHID()) { + $menu_engine + ->setCustomPHID($viewer->getPHID()) + ->setMenuType(PhabricatorProfileMenuEngine::MENU_COMBINED); + } else { + $menu_engine + ->setMenuType(PhabricatorProfileMenuEngine::MENU_GLOBAL); + } + + $filter_view = $menu_engine->buildNavigation(); $menu_view = $filter_view->getMenu(); $item_views = $menu_view->getItems(); diff --git a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php --- a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php +++ b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php @@ -20,44 +20,40 @@ protected function getBuiltinProfileItems($object) { $items = array(); - $custom_phid = $this->getCustomPHID(); $viewer = $this->getViewer(); - // Built-in Global Defaults - if (!$custom_phid) { - $create_task = array( - 'name' => null, - 'formKey' => - id(new ManiphestEditEngine())->getProfileMenuItemDefault(), - ); - - $create_project = array( - 'name' => null, - 'formKey' => - id(new PhabricatorProjectEditEngine())->getProfileMenuItemDefault(), - ); - - $create_repository = array( - 'name' => null, - 'formKey' => - id(new DiffusionRepositoryEditEngine())->getProfileMenuItemDefault(), - ); - - $items[] = $this->newItem() - ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_TASK) - ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) - ->setMenuItemProperties($create_task); - - $items[] = $this->newItem() - ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_PROJECT) - ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) - ->setMenuItemProperties($create_project); - - $items[] = $this->newItem() - ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_REPOSITORY) - ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) - ->setMenuItemProperties($create_repository); - } + $create_task = array( + 'name' => null, + 'formKey' => + id(new ManiphestEditEngine())->getProfileMenuItemDefault(), + ); + + $create_project = array( + 'name' => null, + 'formKey' => + id(new PhabricatorProjectEditEngine())->getProfileMenuItemDefault(), + ); + + $create_repository = array( + 'name' => null, + 'formKey' => + id(new DiffusionRepositoryEditEngine())->getProfileMenuItemDefault(), + ); + + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_TASK) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_task); + + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_PROJECT) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_project); + + $items[] = $this->newItem() + ->setBuiltinKey(PhabricatorFavoritesConstants::ITEM_REPOSITORY) + ->setMenuItemKey(PhabricatorEditEngineProfileMenuItem::MENUITEMKEY) + ->setMenuItemProperties($create_repository); return $items; } diff --git a/src/applications/search/engine/PhabricatorProfileMenuEngine.php b/src/applications/search/engine/PhabricatorProfileMenuEngine.php --- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php +++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php @@ -6,7 +6,7 @@ private $profileObject; private $customPHID; private $items; - private $menuType; + private $menuType = self::MENU_GLOBAL; private $defaultItem; private $controller; private $navigation; @@ -15,6 +15,7 @@ const MENU_GLOBAL = 'global'; const MENU_PERSONAL = 'personal'; const MENU_COMBINED = 'menu'; + const ITEM_CUSTOM_DIVIDER = 'engine.divider'; public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; @@ -82,9 +83,16 @@ } abstract protected function getItemURI($path); - abstract protected function isMenuEngineConfigurable(); + abstract protected function getBuiltinProfileItems($object); + + protected function getBuiltinCustomProfileItems( + $object, + $custom_phid) { + return array(); + } + public function buildResponse() { $controller = $this->getController(); @@ -230,6 +238,7 @@ ->setBaseURI(new PhutilURI($this->getItemURI(''))); $menu_items = $this->getItems(); + $filtered_items = array(); foreach ($menu_items as $menu_item) { if ($menu_item->isDisabled()) { @@ -294,23 +303,26 @@ $object = $this->getProfileObject(); $items = $this->loadBuiltinProfileItems(); - $menu = $this->getMenuType(); - - if ($this->getCustomPHID()) { - $stored_items = id(new PhabricatorProfileMenuItemConfigurationQuery()) - ->setViewer($viewer) - ->withProfilePHIDs(array($object->getPHID())) - ->withCustomPHIDs(array($this->getCustomPHID())) - ->setMenuType($menu) - ->execute(); - } else { - $stored_items = id(new PhabricatorProfileMenuItemConfigurationQuery()) - ->setViewer($viewer) - ->withProfilePHIDs(array($object->getPHID())) - ->setMenuType($menu) - ->execute(); + + $query = id(new PhabricatorProfileMenuItemConfigurationQuery()) + ->setViewer($viewer) + ->withProfilePHIDs(array($object->getPHID())); + + $menu_type = $this->getMenuType(); + switch ($menu_type) { + case self::MENU_GLOBAL: + $query->withCustomPHIDs(array(), true); + break; + case self::MENU_PERSONAL: + $query->withCustomPHIDs(array($this->getCustomPHID()), false); + break; + case self::MENU_COMBINED: + $query->withCustomPHIDs(array($this->getCustomPHID()), true); + break; } + $stored_items = $query->execute(); + foreach ($stored_items as $stored_item) { $impl = $stored_item->getMenuItem(); $impl->setViewer($viewer); @@ -339,12 +351,7 @@ } } - $items = msort($items, 'getSortKey'); - - // Normalize keys since callers shouldn't rely on this array being - // partially keyed. - $items = array_values($items); - + $items = $this->arrangeItems($items); // Make sure exactly one valid item is marked as default. $default = null; @@ -377,7 +384,26 @@ private function loadBuiltinProfileItems() { $object = $this->getProfileObject(); - $builtins = $this->getBuiltinProfileItems($object); + + $menu_type = $this->getMenuType(); + switch ($menu_type) { + case self::MENU_GLOBAL: + $builtins = $this->getBuiltinProfileItems($object); + break; + case self::MENU_PERSONAL: + $builtins = $this->getBuiltinCustomProfileItems( + $object, + $this->getCustomPHID()); + break; + case self::MENU_COMBINED: + $builtins = array(); + $builtins[] = $this->getBuiltinCustomProfileItems( + $object, + $this->getCustomPHID()); + $builtins[] = $this->getBuiltinProfileItems($object); + $builtins = array_mergev($builtins); + break; + } $items = PhabricatorProfileMenuItem::getAllMenuItems(); $viewer = $this->getViewer(); @@ -990,4 +1016,40 @@ return $this; } + private function arrangeItems(array $items) { + // Sort the items. + $items = msortv($items, 'getSortVector'); + + // If we have some global items and some custom items and are in "combined" + // mode, put a hard-coded divider item between them. + if ($this->getMenuType() == self::MENU_COMBINED) { + $list = array(); + $seen_custom = false; + $seen_global = false; + foreach ($items as $item) { + if ($item->getCustomPHID()) { + $seen_custom = true; + } else { + if ($seen_custom && !$seen_global) { + $list[] = $this->newItem() + ->setBuiltinKey(self::ITEM_CUSTOM_DIVIDER) + ->setMenuItemKey(PhabricatorDividerProfileMenuItem::MENUITEMKEY) + ->attachMenuItem( + new PhabricatorDividerProfileMenuItem()); + } + $seen_global = true; + } + $list[] = $item; + } + $items = $list; + } + + // Normalize keys since callers shouldn't rely on this array being + // partially keyed. + $items = array_values($items); + + return $items; + } + + } 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 @@ -7,7 +7,7 @@ private $phids; private $profilePHIDs; private $customPHIDs; - private $menuType; + private $includeGlobal; public function withIDs(array $ids) { $this->ids = $ids; @@ -24,13 +24,9 @@ return $this; } - public function withCustomPHIDs(array $phids) { + public function withCustomPHIDs(array $phids, $include_global = false) { $this->customPHIDs = $phids; - return $this; - } - - public function setMenuType($type) { - $this->menuType = $type; + $this->includeGlobal = $include_global; return $this; } @@ -67,10 +63,21 @@ } if ($this->customPHIDs !== null) { - $where[] = qsprintf( - $conn, - 'customPHID IN (%Ls)', - $this->customPHIDs); + if ($this->customPHIDs && $this->includeGlobal) { + $where[] = qsprintf( + $conn, + 'customPHID IN (%Ls) OR customPHID IS NULL', + $this->customPHIDs); + } else if ($this->customPHIDs) { + $where[] = qsprintf( + $conn, + 'customPHID IN (%Ls)', + $this->customPHIDs); + } else { + $where[] = qsprintf( + $conn, + 'customPHID IS NULL'); + } } return $where; 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 @@ -126,18 +126,28 @@ return $this->getMenuItem()->willBuildNavigationItems($items); } - public function getSortKey() { + public function getSortVector() { + // Sort custom items above global items. + if ($this->getCustomPHID()) { + $is_global = 0; + } else { + $is_global = 1; + } + + // Sort items with an explicit order above items without an explicit order, + // so any newly created builtins go to the bottom. $order = $this->getMenuItemOrder(); - if ($order === null) { - $order = 'Z'; + if ($order !== null) { + $has_order = 0; } else { - $order = sprintf('%020d', $order); + $has_order = 1; } - return sprintf( - '~%s%020d', - $order, - $this->getID()); + return id(new PhutilSortVector()) + ->addInt($is_global) + ->addInt($has_order) + ->addInt((int)$order) + ->addInt((int)$this->getID()); } public function isDisabled() {