Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/favorites/application/PhabricatorFavoritesApplication.php b/src/applications/favorites/application/PhabricatorFavoritesApplication.php
index 6ae45319a0..842d2e33b0 100644
--- a/src/applications/favorites/application/PhabricatorFavoritesApplication.php
+++ b/src/applications/favorites/application/PhabricatorFavoritesApplication.php
@@ -1,95 +1,104 @@
<?php
final class PhabricatorFavoritesApplication extends PhabricatorApplication {
public function getBaseURI() {
return '/favorites/';
}
public function getName() {
return pht('Favorites');
}
public function getShortDescription() {
return pht('Favorite Items');
}
public function getIcon() {
return 'fa-star';
}
public function getRoutes() {
return array(
'/favorites/' => array(
'' => 'PhabricatorFavoritesMainController',
'(?P<type>global|personal)/item/' => $this->getProfileMenuRouting(
'PhabricatorFavoritesMenuItemController'),
),
);
}
public function isLaunchable() {
return false;
}
public function buildMainMenuExtraNodes(
PhabricatorUser $viewer,
PhabricatorController $controller = null) {
return id(new PHUIButtonView())
->setTag('a')
->setHref('#')
->setIcon('fa-star')
->addClass('phabricator-core-user-menu')
->setNoCSS(true)
->setDropdown(true)
->setDropdownMenu($this->renderFavoritesDropdown($viewer));
}
private function renderFavoritesDropdown(PhabricatorUser $viewer) {
$application = __CLASS__;
$favorites = id(new PhabricatorApplicationQuery())
->setViewer($viewer)
->withClasses(array($application))
->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();
$view = id(new PhabricatorActionListView())
->setViewer($viewer);
foreach ($item_views as $item) {
$type = null;
if (!strlen($item->getName())) {
$type = PhabricatorActionView::TYPE_DIVIDER;
}
$action = id(new PhabricatorActionView())
->setName($item->getName())
->setHref($item->getHref())
->setType($type);
$view->addAction($action);
}
// Build out edit interface
if ($viewer->isLoggedIn()) {
$view->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Favorites'))
->setHref('/favorites/'));
}
return $view;
}
}
diff --git a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php
index ff994e4793..c8f1acb31c 100644
--- a/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php
+++ b/src/applications/favorites/engine/PhabricatorFavoritesProfileMenuEngine.php
@@ -1,65 +1,61 @@
<?php
final class PhabricatorFavoritesProfileMenuEngine
extends PhabricatorProfileMenuEngine {
protected function isMenuEngineConfigurable() {
return true;
}
protected function getItemURI($path) {
$object = $this->getProfileObject();
$custom = $this->getCustomPHID();
if ($custom) {
return "/favorites/personal/item/{$path}";
} else {
return "/favorites/global/item/{$path}";
}
}
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
index b3c341178b..7014e719b9 100644
--- a/src/applications/search/engine/PhabricatorProfileMenuEngine.php
+++ b/src/applications/search/engine/PhabricatorProfileMenuEngine.php
@@ -1,993 +1,1055 @@
<?php
abstract class PhabricatorProfileMenuEngine extends Phobject {
private $viewer;
private $profileObject;
private $customPHID;
private $items;
- private $menuType;
+ private $menuType = self::MENU_GLOBAL;
private $defaultItem;
private $controller;
private $navigation;
private $showNavigation = true;
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;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setProfileObject($profile_object) {
$this->profileObject = $profile_object;
return $this;
}
public function getProfileObject() {
return $this->profileObject;
}
public function setCustomPHID($custom_phid) {
$this->customPHID = $custom_phid;
return $this;
}
public function getCustomPHID() {
return $this->customPHID;
}
public function setController(PhabricatorController $controller) {
$this->controller = $controller;
return $this;
}
public function getController() {
return $this->controller;
}
private function setDefaultItem(
PhabricatorProfileMenuItemConfiguration $default_item) {
$this->defaultItem = $default_item;
return $this;
}
public function getDefaultItem() {
$this->loadItems();
return $this->defaultItem;
}
public function setMenuType($type) {
$this->menuType = $type;
return $this;
}
private function getMenuType() {
return $this->menuType;
}
public function setShowNavigation($show) {
$this->showNavigation = $show;
return $this;
}
public function getShowNavigation() {
return $this->showNavigation;
}
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();
$viewer = $controller->getViewer();
$this->setViewer($viewer);
$request = $controller->getRequest();
$item_action = $request->getURIData('itemAction');
// If the engine is not configurable, don't respond to any of the editing
// or configuration routes.
if (!$this->isMenuEngineConfigurable()) {
switch ($item_action) {
case 'view':
break;
default:
return new Aphront404Response();
}
}
$item_id = $request->getURIData('itemID');
$item_list = $this->getItems();
$selected_item = null;
if (strlen($item_id)) {
$item_id_int = (int)$item_id;
foreach ($item_list as $item) {
if ($item_id_int) {
if ((int)$item->getID() === $item_id_int) {
$selected_item = $item;
break;
}
}
$builtin_key = $item->getBuiltinKey();
if ($builtin_key === (string)$item_id) {
$selected_item = $item;
break;
}
}
}
switch ($item_action) {
case 'view':
case 'info':
case 'hide':
case 'default':
case 'builtin':
if (!$selected_item) {
return new Aphront404Response();
}
break;
case 'edit':
if (!$request->getURIData('id')) {
// If we continue along the "edit" pathway without an ID, we hit an
// unrelated exception because we can not build a new menu item out
// of thin air. For menus, new items are created via the "new"
// action. Just catch this case and 404 early since there's currently
// no clean way to make EditEngine aware of this.
return new Aphront404Response();
}
break;
}
$navigation = $this->buildNavigation();
$navigation->selectFilter('item.configure');
$crumbs = $controller->buildApplicationCrumbsForEditEngine();
switch ($this->getMenuType()) {
case 'personal':
$crumbs->addTextCrumb(pht('Personal'));
break;
case 'global':
$crumbs->addTextCrumb(pht('Global'));
break;
}
switch ($item_action) {
case 'view':
$content = $this->buildItemViewContent($selected_item);
break;
case 'configure':
$content = $this->buildItemConfigureContent($item_list);
$crumbs->addTextCrumb(pht('Configure Menu'));
break;
case 'reorder':
$content = $this->buildItemReorderContent($item_list);
break;
case 'new':
$item_key = $request->getURIData('itemKey');
$content = $this->buildItemNewContent($item_key);
break;
case 'builtin':
$content = $this->buildItemBuiltinContent($selected_item);
break;
case 'hide':
$content = $this->buildItemHideContent($selected_item);
break;
case 'default':
$content = $this->buildItemDefaultContent(
$selected_item,
$item_list);
break;
case 'edit':
$content = $this->buildItemEditContent();
break;
default:
throw new Exception(
pht(
'Unsupported item action "%s".',
$item_action));
}
if ($content instanceof AphrontResponse) {
return $content;
}
if ($content instanceof AphrontResponseProducerInterface) {
return $content;
}
$crumbs->setBorder(true);
$page = $controller->newPage()
->setTitle(pht('Configure Menu'))
->setCrumbs($crumbs)
->appendChild($content);
if ($this->getShowNavigation()) {
$page->setNavigation($navigation);
}
return $page;
}
public function buildNavigation() {
if ($this->navigation) {
return $this->navigation;
}
$nav = id(new AphrontSideNavFilterView())
->setIsProfileMenu(true)
->setBaseURI(new PhutilURI($this->getItemURI('')));
$menu_items = $this->getItems();
+
$filtered_items = array();
foreach ($menu_items as $menu_item) {
if ($menu_item->isDisabled()) {
continue;
}
$filtered_items[] = $menu_item;
}
$filtered_groups = mgroup($filtered_items, 'getMenuItemKey');
foreach ($filtered_groups as $group) {
$first_item = head($group);
$first_item->willBuildNavigationItems($group);
}
foreach ($menu_items as $menu_item) {
if ($menu_item->isDisabled()) {
continue;
}
$items = $menu_item->buildNavigationMenuItems();
foreach ($items as $item) {
$this->validateNavigationMenuItem($item);
}
// If the item produced only a single item which does not otherwise
// have a key, try to automatically assign it a reasonable key. This
// makes selecting the correct item simpler.
if (count($items) == 1) {
$item = head($items);
if ($item->getKey() === null) {
$builtin_key = $menu_item->getBuiltinKey();
$item_phid = $menu_item->getPHID();
if ($builtin_key !== null) {
$item->setKey($builtin_key);
} else if ($item_phid !== null) {
$item->setKey($item_phid);
}
}
}
foreach ($items as $item) {
$nav->addMenuItem($item);
}
}
$nav->selectFilter(null);
$this->navigation = $nav;
return $this->navigation;
}
private function getItems() {
if ($this->items === null) {
$this->items = $this->loadItems();
}
return $this->items;
}
private function loadItems() {
$viewer = $this->getViewer();
$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);
}
// Merge the stored items into the builtin items. If a builtin item has
// a stored version, replace the defaults with the stored changes.
foreach ($stored_items as $stored_item) {
if (!$stored_item->shouldEnableForObject($object)) {
continue;
}
$builtin_key = $stored_item->getBuiltinKey();
if ($builtin_key !== null) {
// If this builtin actually exists, replace the builtin with the
// stored configuration. Otherwise, we're just going to drop the
// stored config: it corresponds to an out-of-date or uninstalled
// item.
if (isset($items[$builtin_key])) {
$items[$builtin_key] = $stored_item;
} else {
continue;
}
} else {
$items[] = $stored_item;
}
}
- $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;
$first = null;
foreach ($items as $item) {
if (!$item->canMakeDefault()) {
continue;
}
if ($item->isDefault()) {
$default = $item;
break;
}
if ($first === null) {
$first = $item;
}
}
if (!$default) {
$default = $first;
}
if ($default) {
$this->setDefaultItem($default);
}
return $items;
}
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();
$order = 1;
$map = array();
foreach ($builtins as $builtin) {
$builtin_key = $builtin->getBuiltinKey();
if (!$builtin_key) {
throw new Exception(
pht(
'Object produced a builtin item with no builtin item key! '.
'Builtin items must have a unique key.'));
}
if (isset($map[$builtin_key])) {
throw new Exception(
pht(
'Object produced two items with the same builtin key ("%s"). '.
'Each item must have a unique builtin key.',
$builtin_key));
}
$item_key = $builtin->getMenuItemKey();
$item = idx($items, $item_key);
if (!$item) {
throw new Exception(
pht(
'Builtin item ("%s") specifies a bad item key ("%s"); there '.
'is no corresponding item implementation available.',
$builtin_key,
$item_key));
}
$item = clone $item;
$item->setViewer($viewer);
$builtin
->setProfilePHID($object->getPHID())
->attachMenuItem($item)
->attachProfileObject($object)
->setMenuItemOrder($order);
if (!$builtin->shouldEnableForObject($object)) {
continue;
}
$map[$builtin_key] = $builtin;
$order++;
}
return $map;
}
private function validateNavigationMenuItem($item) {
if (!($item instanceof PHUIListItemView)) {
throw new Exception(
pht(
'Expected buildNavigationMenuItems() to return a list of '.
'PHUIListItemView objects, but got a surprise.'));
}
}
public function getConfigureURI() {
return $this->getItemURI('configure/');
}
private function buildItemReorderContent(array $items) {
$viewer = $this->getViewer();
$object = $this->getProfileObject();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$object,
PhabricatorPolicyCapability::CAN_EDIT);
$controller = $this->getController();
$request = $controller->getRequest();
$request->validateCSRF();
$order = $request->getStrList('order');
$by_builtin = array();
$by_id = array();
foreach ($items as $key => $item) {
$id = $item->getID();
if ($id) {
$by_id[$id] = $key;
continue;
}
$builtin_key = $item->getBuiltinKey();
if ($builtin_key) {
$by_builtin[$builtin_key] = $key;
continue;
}
}
$key_order = array();
foreach ($order as $order_item) {
if (isset($by_id[$order_item])) {
$key_order[] = $by_id[$order_item];
continue;
}
if (isset($by_builtin[$order_item])) {
$key_order[] = $by_builtin[$order_item];
continue;
}
}
$items = array_select_keys($items, $key_order) + $items;
$type_order =
PhabricatorProfileMenuItemConfigurationTransaction::TYPE_ORDER;
$order = 1;
foreach ($items as $item) {
$xactions = array();
$xactions[] = id(new PhabricatorProfileMenuItemConfigurationTransaction())
->setTransactionType($type_order)
->setNewValue($order);
$editor = id(new PhabricatorProfileMenuEditor())
->setContentSourceFromRequest($request)
->setActor($viewer)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($item, $xactions);
$order++;
}
return id(new AphrontRedirectResponse())
->setURI($this->getConfigureURI());
}
private function buildItemConfigureContent(array $items) {
$viewer = $this->getViewer();
$object = $this->getProfileObject();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$object,
PhabricatorPolicyCapability::CAN_EDIT);
$list_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'reorder-profile-menu-items',
array(
'listID' => $list_id,
'orderURI' => $this->getItemURI('reorder/'),
));
$list = id(new PHUIObjectItemListView())
->setID($list_id)
->setNoDataString(pht('This menu currently has no items.'));
foreach ($items as $item) {
$id = $item->getID();
$builtin_key = $item->getBuiltinKey();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$item,
PhabricatorPolicyCapability::CAN_EDIT);
$view = id(new PHUIObjectItemView());
$name = $item->getDisplayName();
$type = $item->getMenuItemTypeName();
if (!strlen(trim($name))) {
$name = pht('Untitled "%s" Item', $type);
}
$view->setHeader($name);
$view->addAttribute($type);
if ($can_edit) {
$view
->setGrippable(true)
->addSigil('profile-menu-item')
->setMetadata(
array(
'key' => nonempty($id, $builtin_key),
));
if ($id) {
$default_uri = $this->getItemURI("default/{$id}/");
} else {
$default_uri = $this->getItemURI("default/{$builtin_key}/");
}
if ($item->isDefault()) {
$default_icon = 'fa-thumb-tack green';
$default_text = pht('Current Default');
} else if ($item->canMakeDefault()) {
$default_icon = 'fa-thumb-tack';
$default_text = pht('Make Default');
} else {
$default_text = null;
}
if ($default_text !== null) {
$view->addAction(
id(new PHUIListItemView())
->setHref($default_uri)
->setWorkflow(true)
->setName($default_text)
->setIcon($default_icon));
}
if ($id) {
$view->setHref($this->getItemURI("edit/{$id}/"));
$hide_uri = $this->getItemURI("hide/{$id}/");
} else {
$view->setHref($this->getItemURI("builtin/{$builtin_key}/"));
$hide_uri = $this->getItemURI("hide/{$builtin_key}/");
}
if ($item->isDisabled()) {
$hide_icon = 'fa-plus';
$hide_text = pht('Enable');
} else if ($item->getBuiltinKey() !== null) {
$hide_icon = 'fa-times';
$hide_text = pht('Disable');
} else {
$hide_icon = 'fa-times';
$hide_text = pht('Delete');
}
$can_disable = $item->canHideMenuItem();
$view->addAction(
id(new PHUIListItemView())
->setHref($hide_uri)
->setWorkflow(true)
->setDisabled(!$can_disable)
->setName($hide_text)
->setIcon($hide_icon));
}
if ($item->isDisabled()) {
$view->setDisabled(true);
}
$list->addItem($view);
}
$action_view = id(new PhabricatorActionListView())
->setUser($viewer);
$item_types = PhabricatorProfileMenuItem::getAllMenuItems();
$object = $this->getProfileObject();
$action_list = id(new PhabricatorActionListView())
->setViewer($viewer);
$action_list->addAction(
id(new PhabricatorActionView())
->setLabel(true)
->setName(pht('Add New Menu Item...')));
foreach ($item_types as $item_type) {
if (!$item_type->canAddToObject($object)) {
continue;
}
$item_key = $item_type->getMenuItemKey();
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon($item_type->getMenuItemTypeIcon())
->setName($item_type->getMenuItemTypeName())
->setHref($this->getItemURI("new/{$item_key}/"))
->setWorkflow(true));
}
$action_list->addAction(
id(new PhabricatorActionView())
->setLabel(true)
->setName(pht('Documentation')));
$doc_link = PhabricatorEnv::getDoclink('Profile Menu User Guide');
$doc_name = pht('Profile Menu User Guide');
$action_list->addAction(
id(new PhabricatorActionView())
->setIcon('fa-book')
->setHref($doc_link)
->setName($doc_name));
$header = id(new PHUIHeaderView())
->setHeader(pht('Menu Items'))
->setHeaderIcon('fa-list');
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Current Menu Items'))
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
->setObjectList($list);
$panel = id(new PHUICurtainPanelView())
->appendChild($action_view);
$curtain = id(new PHUICurtainView())
->setViewer($viewer)
->setActionList($action_list);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setCurtain($curtain)
->setMainColumn(
array(
$box,
));
return $view;
}
private function buildItemNewContent($item_key) {
$item_types = PhabricatorProfileMenuItem::getAllMenuItems();
$item_type = idx($item_types, $item_key);
if (!$item_type) {
return new Aphront404Response();
}
$object = $this->getProfileObject();
if (!$item_type->canAddToObject($object)) {
return new Aphront404Response();
}
$custom_phid = $this->getCustomPHID();
$configuration = PhabricatorProfileMenuItemConfiguration::initializeNewItem(
$object,
$item_type,
$custom_phid);
$viewer = $this->getViewer();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$configuration,
PhabricatorPolicyCapability::CAN_EDIT);
$controller = $this->getController();
return id(new PhabricatorProfileMenuEditEngine())
->setMenuEngine($this)
->setProfileObject($object)
->setNewMenuItemConfiguration($configuration)
->setCustomPHID($custom_phid)
->setController($controller)
->buildResponse();
}
private function buildItemEditContent() {
$viewer = $this->getViewer();
$object = $this->getProfileObject();
$controller = $this->getController();
return id(new PhabricatorProfileMenuEditEngine())
->setMenuEngine($this)
->setProfileObject($object)
->setController($controller)
->setCustomPHID($this->getCustomPHID())
->buildResponse();
}
private function buildItemBuiltinContent(
PhabricatorProfileMenuItemConfiguration $configuration) {
// If this builtin item has already been persisted, redirect to the
// edit page.
$id = $configuration->getID();
if ($id) {
return id(new AphrontRedirectResponse())
->setURI($this->getItemURI("edit/{$id}/"));
}
// Otherwise, act like we're creating a new item, we're just starting
// with the builtin template.
$viewer = $this->getViewer();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$configuration,
PhabricatorPolicyCapability::CAN_EDIT);
$object = $this->getProfileObject();
$controller = $this->getController();
return id(new PhabricatorProfileMenuEditEngine())
->setIsBuiltin(true)
->setMenuEngine($this)
->setProfileObject($object)
->setNewMenuItemConfiguration($configuration)
->setController($controller)
->setCustomPHID($this->getCustomPHID())
->buildResponse();
}
private function buildItemHideContent(
PhabricatorProfileMenuItemConfiguration $configuration) {
$controller = $this->getController();
$request = $controller->getRequest();
$viewer = $this->getViewer();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$configuration,
PhabricatorPolicyCapability::CAN_EDIT);
if (!$configuration->canHideMenuItem()) {
return $controller->newDialog()
->setTitle(pht('Mandatory Item'))
->appendParagraph(
pht('This menu item is very important, and can not be disabled.'))
->addCancelButton($this->getConfigureURI());
}
if ($configuration->getBuiltinKey() === null) {
$new_value = null;
$title = pht('Delete Menu Item');
$body = pht('Delete this menu item?');
$button = pht('Delete Menu Item');
} else if ($configuration->isDisabled()) {
$new_value = PhabricatorProfileMenuItemConfiguration::VISIBILITY_VISIBLE;
$title = pht('Enable Menu Item');
$body = pht(
'Enable this menu item? It will appear in the menu again.');
$button = pht('Enable Menu Item');
} else {
$new_value = PhabricatorProfileMenuItemConfiguration::VISIBILITY_DISABLED;
$title = pht('Disable Menu Item');
$body = pht(
'Disable this menu item? It will no longer appear in the menu, but '.
'you can re-enable it later.');
$button = pht('Disable Menu Item');
}
$v_visibility = $configuration->getVisibility();
if ($request->isFormPost()) {
if ($new_value === null) {
$configuration->delete();
} else {
$type_visibility =
PhabricatorProfileMenuItemConfigurationTransaction::TYPE_VISIBILITY;
$xactions = array();
$xactions[] =
id(new PhabricatorProfileMenuItemConfigurationTransaction())
->setTransactionType($type_visibility)
->setNewValue($new_value);
$editor = id(new PhabricatorProfileMenuEditor())
->setContentSourceFromRequest($request)
->setActor($viewer)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($configuration, $xactions);
}
return id(new AphrontRedirectResponse())
->setURI($this->getConfigureURI());
}
return $controller->newDialog()
->setTitle($title)
->appendParagraph($body)
->addCancelButton($this->getConfigureURI())
->addSubmitButton($button);
}
private function buildItemDefaultContent(
PhabricatorProfileMenuItemConfiguration $configuration,
array $items) {
$controller = $this->getController();
$request = $controller->getRequest();
$viewer = $this->getViewer();
PhabricatorPolicyFilter::requireCapability(
$viewer,
$configuration,
PhabricatorPolicyCapability::CAN_EDIT);
$done_uri = $this->getConfigureURI();
if (!$configuration->canMakeDefault()) {
return $controller->newDialog()
->setTitle(pht('Not Defaultable'))
->appendParagraph(
pht(
'This item can not be set as the default item. This is usually '.
'because the item has no page of its own, or links to an '.
'external page.'))
->addCancelButton($done_uri);
}
if ($configuration->isDefault()) {
return $controller->newDialog()
->setTitle(pht('Already Default'))
->appendParagraph(
pht(
'This item is already set as the default item for this menu.'))
->addCancelButton($done_uri);
}
if ($request->isFormPost()) {
$key = $configuration->getID();
if (!$key) {
$key = $configuration->getBuiltinKey();
}
$this->adjustDefault($key);
return id(new AphrontRedirectResponse())
->setURI($done_uri);
}
return $controller->newDialog()
->setTitle(pht('Make Default'))
->appendParagraph(
pht(
'Set this item as the default for this menu? Users arriving on '.
'this page will be shown the content of this item by default.'))
->addCancelButton($done_uri)
->addSubmitButton(pht('Make Default'));
}
protected function newItem() {
return PhabricatorProfileMenuItemConfiguration::initializeNewBuiltin();
}
public function adjustDefault($key) {
$controller = $this->getController();
$request = $controller->getRequest();
$viewer = $request->getViewer();
$items = $this->loadItems();
// To adjust the default item, we first change any existing items that
// are marked as defaults to "visible", then make the new default item
// the default.
$default = array();
$visible = array();
foreach ($items as $item) {
$builtin_key = $item->getBuiltinKey();
$id = $item->getID();
$is_target =
(($builtin_key !== null) && ($builtin_key === $key)) ||
(($id !== null) && ((int)$id === (int)$key));
if ($is_target) {
if (!$item->isDefault()) {
$default[] = $item;
}
} else {
if ($item->isDefault()) {
$visible[] = $item;
}
}
}
$type_visibility =
PhabricatorProfileMenuItemConfigurationTransaction::TYPE_VISIBILITY;
$v_visible = PhabricatorProfileMenuItemConfiguration::VISIBILITY_VISIBLE;
$v_default = PhabricatorProfileMenuItemConfiguration::VISIBILITY_DEFAULT;
$apply = array(
array($v_visible, $visible),
array($v_default, $default),
);
foreach ($apply as $group) {
list($value, $items) = $group;
foreach ($items as $item) {
$xactions = array();
$xactions[] =
id(new PhabricatorProfileMenuItemConfigurationTransaction())
->setTransactionType($type_visibility)
->setNewValue($value);
$editor = id(new PhabricatorProfileMenuEditor())
->setContentSourceFromRequest($request)
->setActor($viewer)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($item, $xactions);
}
}
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
index 2256be446f..83a84ac306 100644
--- a/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php
+++ b/src/applications/search/query/PhabricatorProfileMenuItemConfigurationQuery.php
@@ -1,123 +1,130 @@
<?php
final class PhabricatorProfileMenuItemConfigurationQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $profilePHIDs;
private $customPHIDs;
- private $menuType;
+ private $includeGlobal;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withProfilePHIDs(array $phids) {
$this->profilePHIDs = $phids;
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;
}
public function newResultObject() {
return new PhabricatorProfileMenuItemConfiguration();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'phid IN (%Ls)',
$this->phids);
}
if ($this->profilePHIDs !== null) {
$where[] = qsprintf(
$conn,
'profilePHID IN (%Ls)',
$this->profilePHIDs);
}
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;
}
protected function willFilterPage(array $page) {
$items = PhabricatorProfileMenuItem::getAllMenuItems();
foreach ($page as $key => $item) {
$item_type = idx($items, $item->getMenuItemKey());
if (!$item_type) {
$this->didRejectResult($item);
unset($page[$key]);
continue;
}
$item_type = clone $item_type;
$item_type->setViewer($this->getViewer());
$item->attachMenuItem($item_type);
}
if (!$page) {
return array();
}
$profile_phids = mpull($page, 'getProfilePHID');
$profiles = id(new PhabricatorObjectQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withPHIDs($profile_phids)
->execute();
$profiles = mpull($profiles, null, 'getPHID');
foreach ($page as $key => $item) {
$profile = idx($profiles, $item->getProfilePHID());
if (!$profile) {
$this->didRejectResult($item);
unset($page[$key]);
continue;
}
$item->attachProfileObject($profile);
}
return $page;
}
public function getQueryApplicationClass() {
return 'PhabricatorSearchApplication';
}
}
diff --git a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
index 78fc380604..943474669d 100644
--- a/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
+++ b/src/applications/search/storage/PhabricatorProfileMenuItemConfiguration.php
@@ -1,213 +1,223 @@
<?php
final class PhabricatorProfileMenuItemConfiguration
extends PhabricatorSearchDAO
implements
PhabricatorPolicyInterface,
PhabricatorExtendedPolicyInterface,
PhabricatorApplicationTransactionInterface {
protected $profilePHID;
protected $menuItemKey;
protected $builtinKey;
protected $menuItemOrder;
protected $visibility;
protected $customPHID;
protected $menuItemProperties = array();
private $profileObject = self::ATTACHABLE;
private $menuItem = self::ATTACHABLE;
const VISIBILITY_DEFAULT = 'default';
const VISIBILITY_VISIBLE = 'visible';
const VISIBILITY_DISABLED = 'disabled';
public function getTableName() {
// For now, this class uses an older table name.
return 'search_profilepanelconfiguration';
}
public static function initializeNewBuiltin() {
return id(new self())
->setVisibility(self::VISIBILITY_VISIBLE);
}
public static function initializeNewItem(
$profile_object,
PhabricatorProfileMenuItem $item,
$custom_phid) {
return self::initializeNewBuiltin()
->setProfilePHID($profile_object->getPHID())
->setMenuItemKey($item->getMenuItemKey())
->attachMenuItem($item)
->attachProfileObject($profile_object)
->setCustomPHID($custom_phid);
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_SERIALIZATION => array(
'menuItemProperties' => self::SERIALIZATION_JSON,
),
self::CONFIG_COLUMN_SCHEMA => array(
'menuItemKey' => 'text64',
'builtinKey' => 'text64?',
'menuItemOrder' => 'uint32?',
'customPHID' => 'phid?',
'visibility' => 'text32',
),
self::CONFIG_KEY_SCHEMA => array(
'key_profile' => array(
'columns' => array('profilePHID', 'menuItemOrder'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorProfileMenuItemPHIDType::TYPECONST);
}
public function attachMenuItem(PhabricatorProfileMenuItem $item) {
$this->menuItem = $item;
return $this;
}
public function getMenuItem() {
return $this->assertAttached($this->menuItem);
}
public function attachProfileObject($profile_object) {
$this->profileObject = $profile_object;
return $this;
}
public function getProfileObject() {
return $this->assertAttached($this->profileObject);
}
public function setMenuItemProperty($key, $value) {
$this->menuItemProperties[$key] = $value;
return $this;
}
public function getMenuItemProperty($key, $default = null) {
return idx($this->menuItemProperties, $key, $default);
}
public function buildNavigationMenuItems() {
return $this->getMenuItem()->buildNavigationMenuItems($this);
}
public function getMenuItemTypeName() {
return $this->getMenuItem()->getMenuItemTypeName();
}
public function getDisplayName() {
return $this->getMenuItem()->getDisplayName($this);
}
public function canMakeDefault() {
return $this->getMenuItem()->canMakeDefault($this);
}
public function canHideMenuItem() {
return $this->getMenuItem()->canHideMenuItem($this);
}
public function shouldEnableForObject($object) {
return $this->getMenuItem()->shouldEnableForObject($object);
}
public function willBuildNavigationItems(array $items) {
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() {
if (!$this->canHideMenuItem()) {
return false;
}
return ($this->getVisibility() === self::VISIBILITY_DISABLED);
}
public function isDefault() {
return ($this->getVisibility() === self::VISIBILITY_DEFAULT);
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
return PhabricatorPolicies::getMostOpenPolicy();
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getProfileObject()->hasAutomaticCapability(
$capability,
$viewer);
}
/* -( PhabricatorExtendedPolicyInterface )--------------------------------- */
public function getExtendedPolicy($capability, PhabricatorUser $viewer) {
return array(
array(
$this->getProfileObject(),
$capability,
),
);
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorProfileMenuEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorProfileMenuItemConfigurationTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jul 29, 7:12 AM (2 w, 6 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
188266
Default Alt Text
(47 KB)

Event Timeline