Changeset View
Changeset View
Standalone View
Standalone View
src/applications/search/engine/PhabricatorProfileMenuEngine.php
<?php | <?php | ||||
abstract class PhabricatorProfileMenuEngine extends Phobject { | abstract class PhabricatorProfileMenuEngine extends Phobject { | ||||
private $viewer; | private $viewer; | ||||
private $profileObject; | private $profileObject; | ||||
private $customPHID; | private $customPHID; | ||||
private $items; | private $items; | ||||
private $defaultItem; | |||||
private $controller; | private $controller; | ||||
private $navigation; | private $navigation; | ||||
private $showNavigation = true; | private $showNavigation = true; | ||||
private $editMode; | private $editMode; | ||||
private $pageClasses = array(); | private $pageClasses = array(); | ||||
private $showContentCrumbs = true; | private $showContentCrumbs = true; | ||||
const ITEM_CUSTOM_DIVIDER = 'engine.divider'; | const ITEM_CUSTOM_DIVIDER = 'engine.divider'; | ||||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Lines | abstract class PhabricatorProfileMenuEngine extends Phobject { | ||||
private function setDefaultItem( | private function setDefaultItem( | ||||
PhabricatorProfileMenuItemConfiguration $default_item) { | PhabricatorProfileMenuItemConfiguration $default_item) { | ||||
$this->defaultItem = $default_item; | $this->defaultItem = $default_item; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getDefaultItem() { | public function getDefaultItem() { | ||||
$this->getItems(); | return $this->pickDefaultItem($this->getItems()); | ||||
return $this->defaultItem; | |||||
} | } | ||||
public function setShowNavigation($show) { | public function setShowNavigation($show) { | ||||
$this->showNavigation = $show; | $this->showNavigation = $show; | ||||
return $this; | return $this; | ||||
} | } | ||||
public function getShowNavigation() { | public function getShowNavigation() { | ||||
▲ Show 20 Lines • Show All 57 Lines • ▼ Show 20 Lines | public function buildResponse() { | ||||
// If we miss on the MenuEngine route, try the EditEngine route. This will | // If we miss on the MenuEngine route, try the EditEngine route. This will | ||||
// be populated while editing items. | // be populated while editing items. | ||||
if (!$item_id) { | if (!$item_id) { | ||||
$item_id = $request->getURIData('id'); | $item_id = $request->getURIData('id'); | ||||
} | } | ||||
$item_list = $this->getItems(); | $item_list = $this->getItems(); | ||||
$selected_item = null; | $selected_item = $this->pickSelectedItem( | ||||
if (strlen($item_id)) { | $item_list, | ||||
$item_id_int = (int)$item_id; | $item_id, | ||||
foreach ($item_list as $item) { | $is_view); | ||||
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; | |||||
} | |||||
} | |||||
} | |||||
if (!$selected_item) { | |||||
if ($is_view) { | |||||
$selected_item = $this->getDefaultItem(); | |||||
} | |||||
} | |||||
switch ($item_action) { | switch ($item_action) { | ||||
case 'view': | case 'view': | ||||
// If we were not able to select an item, we're still going to render | // If we were not able to select an item, we're still going to render | ||||
// a page state. For example, this happens when you create a new | // a page state. For example, this happens when you create a new | ||||
// portal for the first time. | // portal for the first time. | ||||
break; | break; | ||||
case 'info': | case 'info': | ||||
▲ Show 20 Lines • Show All 291 Lines • ▼ Show 20 Lines | foreach ($stored_items as $stored_item) { | ||||
} else { | } else { | ||||
continue; | continue; | ||||
} | } | ||||
} else { | } else { | ||||
$items[] = $stored_item; | $items[] = $stored_item; | ||||
} | } | ||||
} | } | ||||
$items = $this->arrangeItems($items, $mode); | return $this->arrangeItems($items, $mode); | ||||
// Make sure exactly one valid item is marked as default. | |||||
$default = null; | |||||
$first = null; | |||||
foreach ($items as $item) { | |||||
if (!$item->canMakeDefault() || $item->isDisabled()) { | |||||
continue; | |||||
} | |||||
// If this engine doesn't support pinning items, don't respect any | |||||
// setting which might be present in the database. | |||||
if ($this->isMenuEnginePinnable()) { | |||||
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($mode) { | private function loadBuiltinProfileItems($mode) { | ||||
$object = $this->getProfileObject(); | $object = $this->getProfileObject(); | ||||
switch ($mode) { | switch ($mode) { | ||||
case self::MODE_GLOBAL: | case self::MODE_GLOBAL: | ||||
$builtins = $this->getBuiltinProfileItems($object); | $builtins = $this->getBuiltinProfileItems($object); | ||||
▲ Show 20 Lines • Show All 827 Lines • ▼ Show 20 Lines | |||||
} | } | ||||
protected function newNoMenuItemsView() { | protected function newNoMenuItemsView() { | ||||
return $this->newEmptyView( | return $this->newEmptyView( | ||||
pht('No Menu Items'), | pht('No Menu Items'), | ||||
pht('There are no menu items.')); | pht('There are no menu items.')); | ||||
} | } | ||||
private function pickDefaultItem(array $items) { | |||||
// Remove all the items which can not be the default item. | |||||
foreach ($items as $key => $item) { | |||||
if (!$item->canMakeDefault()) { | |||||
unset($items[$key]); | |||||
continue; | |||||
} | |||||
if ($item->isDisabled()) { | |||||
unset($items[$key]); | |||||
continue; | |||||
} | |||||
} | |||||
// If this engine supports pinning items and a valid item is pinned, | |||||
// pick that item as the default. | |||||
if ($this->isMenuEnginePinnable()) { | |||||
foreach ($items as $key => $item) { | |||||
if ($item->isDefault()) { | |||||
return $item; | |||||
} | |||||
} | |||||
} | |||||
// If we have some other valid items, pick the first one as the default. | |||||
if ($items) { | |||||
return head($items); | |||||
amckinley: Is this going to result in a stable choice of default? Presumably `items` is an ordered list. | |||||
Done Inline ActionsYeah, the stored items have an explicit order (like <someOrderColumn, id>). epriestley: Yeah, the stored items have an explicit order (like `<someOrderColumn, id>`). | |||||
} | |||||
return null; | |||||
} | |||||
private function pickSelectedItem(array $items, $item_id, $is_view) { | |||||
if (strlen($item_id)) { | |||||
$item_id_int = (int)$item_id; | |||||
foreach ($items as $item) { | |||||
if ($item_id_int) { | |||||
if ((int)$item->getID() === $item_id_int) { | |||||
Not Done Inline ActionsSo I understand the cast to int is because everything comes out of the MySQL layer as a string, but why cast the target ID initially instead of just using the == operator? amckinley: So I understand the cast to `int` is because everything comes out of the MySQL layer as a… | |||||
Done Inline ActionsIf the ID is some kind of junk value like "quack", it will match 0 with ==. There's probably no way we can reach this code and actually get a bad comparison, this is more just a general PHP hygiene thing where code that compares if ("quack" == 0) { ... } and enters the condition is never desirable even in cases where it doesn't cause any actual problems. epriestley: If the ID is some kind of junk value like "quack", it will match `0` with `==`.
There's… | |||||
return $item; | |||||
} | |||||
} | |||||
$builtin_key = $item->getBuiltinKey(); | |||||
if ($builtin_key === (string)$item_id) { | |||||
return $item; | |||||
} | |||||
} | |||||
// Nothing matches the selected item ID, so we don't have a valid | |||||
// selection. | |||||
return null; | |||||
} | |||||
if ($is_view) { | |||||
return $this->pickDefaultItem($items); | |||||
} | |||||
return null; | |||||
} | |||||
} | } |
Is this going to result in a stable choice of default? Presumably items is an ordered list.