2016-01-12 10:27:39 -08:00
|
|
|
<?php
|
|
|
|
|
2016-12-11 09:38:06 -08:00
|
|
|
final class PhabricatorProfileMenuItemConfiguration
|
2016-01-12 10:27:39 -08:00
|
|
|
extends PhabricatorSearchDAO
|
|
|
|
implements
|
|
|
|
PhabricatorPolicyInterface,
|
2016-01-12 15:06:43 -08:00
|
|
|
PhabricatorExtendedPolicyInterface,
|
|
|
|
PhabricatorApplicationTransactionInterface {
|
2016-01-12 10:27:39 -08:00
|
|
|
|
|
|
|
protected $profilePHID;
|
2016-12-11 09:38:06 -08:00
|
|
|
protected $menuItemKey;
|
2016-01-12 10:27:39 -08:00
|
|
|
protected $builtinKey;
|
2016-12-11 09:38:06 -08:00
|
|
|
protected $menuItemOrder;
|
2016-01-12 15:06:43 -08:00
|
|
|
protected $visibility;
|
2017-01-06 20:12:57 -08:00
|
|
|
protected $customPHID;
|
2016-12-11 09:38:06 -08:00
|
|
|
protected $menuItemProperties = array();
|
2016-01-12 10:27:39 -08:00
|
|
|
|
|
|
|
private $profileObject = self::ATTACHABLE;
|
2016-12-11 10:08:26 -08:00
|
|
|
private $menuItem = self::ATTACHABLE;
|
2019-03-30 14:22:08 -07:00
|
|
|
private $isHeadItem = false;
|
|
|
|
private $isTailItem = false;
|
2016-01-12 10:27:39 -08:00
|
|
|
|
2016-01-22 05:33:21 -08:00
|
|
|
const VISIBILITY_DEFAULT = 'default';
|
2016-01-12 15:06:43 -08:00
|
|
|
const VISIBILITY_VISIBLE = 'visible';
|
|
|
|
const VISIBILITY_DISABLED = 'disabled';
|
|
|
|
|
2016-12-11 09:38:06 -08:00
|
|
|
public function getTableName() {
|
|
|
|
// For now, this class uses an older table name.
|
|
|
|
return 'search_profilepanelconfiguration';
|
|
|
|
}
|
|
|
|
|
2016-01-12 15:06:43 -08:00
|
|
|
public static function initializeNewBuiltin() {
|
|
|
|
return id(new self())
|
|
|
|
->setVisibility(self::VISIBILITY_VISIBLE);
|
|
|
|
}
|
|
|
|
|
2016-12-11 10:08:26 -08:00
|
|
|
public static function initializeNewItem(
|
2016-01-14 05:59:25 -08:00
|
|
|
$profile_object,
|
2017-01-10 11:20:26 -08:00
|
|
|
PhabricatorProfileMenuItem $item,
|
|
|
|
$custom_phid) {
|
2016-01-12 10:27:39 -08:00
|
|
|
|
2016-01-12 15:06:43 -08:00
|
|
|
return self::initializeNewBuiltin()
|
2016-01-12 10:27:39 -08:00
|
|
|
->setProfilePHID($profile_object->getPHID())
|
2016-12-11 10:08:26 -08:00
|
|
|
->setMenuItemKey($item->getMenuItemKey())
|
|
|
|
->attachMenuItem($item)
|
2017-01-10 11:20:26 -08:00
|
|
|
->attachProfileObject($profile_object)
|
|
|
|
->setCustomPHID($custom_phid);
|
2016-01-12 10:27:39 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
protected function getConfiguration() {
|
|
|
|
return array(
|
|
|
|
self::CONFIG_AUX_PHID => true,
|
|
|
|
self::CONFIG_SERIALIZATION => array(
|
2016-12-11 09:38:06 -08:00
|
|
|
'menuItemProperties' => self::SERIALIZATION_JSON,
|
2016-01-12 10:27:39 -08:00
|
|
|
),
|
|
|
|
self::CONFIG_COLUMN_SCHEMA => array(
|
2016-12-11 09:38:06 -08:00
|
|
|
'menuItemKey' => 'text64',
|
2016-01-12 15:06:43 -08:00
|
|
|
'builtinKey' => 'text64?',
|
2016-12-11 09:38:06 -08:00
|
|
|
'menuItemOrder' => 'uint32?',
|
2017-01-06 20:12:57 -08:00
|
|
|
'customPHID' => 'phid?',
|
2016-01-12 15:06:43 -08:00
|
|
|
'visibility' => 'text32',
|
2016-01-12 10:27:39 -08:00
|
|
|
),
|
|
|
|
self::CONFIG_KEY_SCHEMA => array(
|
|
|
|
'key_profile' => array(
|
2016-12-11 09:38:06 -08:00
|
|
|
'columns' => array('profilePHID', 'menuItemOrder'),
|
2016-01-12 10:27:39 -08:00
|
|
|
),
|
|
|
|
),
|
|
|
|
) + parent::getConfiguration();
|
|
|
|
}
|
|
|
|
|
2016-01-12 15:06:43 -08:00
|
|
|
public function generatePHID() {
|
|
|
|
return PhabricatorPHID::generateNewPHID(
|
2016-12-11 09:38:06 -08:00
|
|
|
PhabricatorProfileMenuItemPHIDType::TYPECONST);
|
2016-01-12 15:06:43 -08:00
|
|
|
}
|
|
|
|
|
2016-12-11 10:08:26 -08:00
|
|
|
public function attachMenuItem(PhabricatorProfileMenuItem $item) {
|
|
|
|
$this->menuItem = $item;
|
2016-01-12 10:27:39 -08:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2016-12-11 10:08:26 -08:00
|
|
|
public function getMenuItem() {
|
|
|
|
return $this->assertAttached($this->menuItem);
|
2016-01-12 10:27:39 -08:00
|
|
|
}
|
|
|
|
|
2016-01-14 05:59:25 -08:00
|
|
|
public function attachProfileObject($profile_object) {
|
2016-01-12 10:27:39 -08:00
|
|
|
$this->profileObject = $profile_object;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getProfileObject() {
|
|
|
|
return $this->assertAttached($this->profileObject);
|
|
|
|
}
|
|
|
|
|
2016-12-11 09:38:06 -08:00
|
|
|
public function setMenuItemProperty($key, $value) {
|
|
|
|
$this->menuItemProperties[$key] = $value;
|
2016-01-12 10:27:39 -08:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2016-12-11 09:38:06 -08:00
|
|
|
public function getMenuItemProperty($key, $default = null) {
|
|
|
|
return idx($this->menuItemProperties, $key, $default);
|
2016-01-12 10:27:39 -08:00
|
|
|
}
|
|
|
|
|
2016-12-11 10:08:26 -08:00
|
|
|
public function getMenuItemTypeName() {
|
|
|
|
return $this->getMenuItem()->getMenuItemTypeName();
|
2016-01-12 15:06:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getDisplayName() {
|
2016-12-11 10:08:26 -08:00
|
|
|
return $this->getMenuItem()->getDisplayName($this);
|
2016-01-12 15:06:43 -08:00
|
|
|
}
|
|
|
|
|
2016-01-22 05:33:21 -08:00
|
|
|
public function canMakeDefault() {
|
2016-12-11 10:08:26 -08:00
|
|
|
return $this->getMenuItem()->canMakeDefault($this);
|
2016-01-22 05:33:21 -08:00
|
|
|
}
|
|
|
|
|
2016-12-11 09:38:06 -08:00
|
|
|
public function canHideMenuItem() {
|
2016-12-11 10:08:26 -08:00
|
|
|
return $this->getMenuItem()->canHideMenuItem($this);
|
2016-01-25 03:21:36 -08:00
|
|
|
}
|
|
|
|
|
2016-02-06 12:48:01 -08:00
|
|
|
public function shouldEnableForObject($object) {
|
2016-12-11 10:08:26 -08:00
|
|
|
return $this->getMenuItem()->shouldEnableForObject($object);
|
2016-02-06 12:48:01 -08:00
|
|
|
}
|
|
|
|
|
In ProfileMenu, put more structure between "stored/configured items" and "display items"
Summary:
Depends on D20356. Ref T13275. See also T12871 and T12949.
Currently, the whole "ProfileMenu" API operates around //stored// items. However, stored items are allowed to produce zero or more //display// items, and we sometimes want to highlight display item X but render stored item Y (as is the case with "Link" items pointing at `?filter=xyz` on Workboards).
For the most part, this either: doesn't work; or works by chance; or is kind of glued together with hope and prayer (as in D20353).
Put an actual structural layer in place between "stored/configured item" and "display item" that can link them together more clearly. Now:
- The list of `ItemConfiguration` objects (stored/configured items) is used to build an `ItemViewList`.
- This handles the selection/highlighting/default state, and knows which display items are related to which stored items.
- When we're all done figuring out what we're going to select and what we're going to highlight, it pops out an actual View which can build the HTML.
This requires API changes which are not included in this change, see next change.
This doesn't really do anything on its own, but builds toward a more satisfying fix for T12871. I'd hoped to avoid doing this for now, but wasn't able to get a patch I felt good about for T12871 built without fixing this first.
Test Plan: See next change.
Reviewers: amckinley
Reviewed By: amckinley
Maniphest Tasks: T13275
Differential Revision: https://secure.phabricator.com/D20357
2019-03-31 11:48:44 -07:00
|
|
|
public function willGetMenuItemViewList(array $items) {
|
|
|
|
return $this->getMenuItem()->willGetMenuItemViewList($items);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getMenuItemViewList() {
|
|
|
|
return $this->getMenuItem()->getMenuItemViewList($this);
|
2016-12-15 14:22:42 -08:00
|
|
|
}
|
|
|
|
|
2017-01-20 11:03:09 -08:00
|
|
|
public function validateTransactions(array $map) {
|
|
|
|
$item = $this->getMenuItem();
|
|
|
|
|
|
|
|
$fields = $item->buildEditEngineFields($this);
|
|
|
|
$errors = array();
|
|
|
|
foreach ($fields as $field) {
|
|
|
|
$field_key = $field->getKey();
|
|
|
|
|
|
|
|
$xactions = idx($map, $field_key, array());
|
|
|
|
$value = $this->getMenuItemProperty($field_key);
|
|
|
|
|
|
|
|
$field_errors = $item->validateTransactions(
|
|
|
|
$this,
|
|
|
|
$field_key,
|
|
|
|
$value,
|
|
|
|
$xactions);
|
|
|
|
foreach ($field_errors as $error) {
|
|
|
|
$errors[] = $error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $errors;
|
|
|
|
}
|
|
|
|
|
2017-01-17 12:46:05 -08:00
|
|
|
public function getSortVector() {
|
|
|
|
// Sort custom items above global items.
|
|
|
|
if ($this->getCustomPHID()) {
|
|
|
|
$is_global = 0;
|
|
|
|
} else {
|
|
|
|
$is_global = 1;
|
|
|
|
}
|
|
|
|
|
2019-03-30 14:22:08 -07:00
|
|
|
// Sort "head" items above other items and "tail" items after other items.
|
|
|
|
if ($this->getIsHeadItem()) {
|
|
|
|
$force_position = 0;
|
|
|
|
} else if ($this->getIsTailItem()) {
|
|
|
|
$force_position = 2;
|
|
|
|
} else {
|
|
|
|
$force_position = 1;
|
|
|
|
}
|
|
|
|
|
2017-01-17 12:46:05 -08:00
|
|
|
// Sort items with an explicit order above items without an explicit order,
|
|
|
|
// so any newly created builtins go to the bottom.
|
2016-12-11 09:38:06 -08:00
|
|
|
$order = $this->getMenuItemOrder();
|
2017-01-17 12:46:05 -08:00
|
|
|
if ($order !== null) {
|
|
|
|
$has_order = 0;
|
2016-01-13 09:40:27 -08:00
|
|
|
} else {
|
2017-01-17 12:46:05 -08:00
|
|
|
$has_order = 1;
|
2016-01-13 09:40:27 -08:00
|
|
|
}
|
|
|
|
|
2017-01-17 12:46:05 -08:00
|
|
|
return id(new PhutilSortVector())
|
|
|
|
->addInt($is_global)
|
2019-03-30 14:22:08 -07:00
|
|
|
->addInt($force_position)
|
2017-01-17 12:46:05 -08:00
|
|
|
->addInt($has_order)
|
|
|
|
->addInt((int)$order)
|
|
|
|
->addInt((int)$this->getID());
|
2016-01-13 09:40:27 -08:00
|
|
|
}
|
|
|
|
|
2016-01-13 10:40:31 -08:00
|
|
|
public function isDisabled() {
|
2016-12-11 09:38:06 -08:00
|
|
|
if (!$this->canHideMenuItem()) {
|
2016-01-25 03:21:36 -08:00
|
|
|
return false;
|
|
|
|
}
|
2016-01-13 10:40:31 -08:00
|
|
|
return ($this->getVisibility() === self::VISIBILITY_DISABLED);
|
|
|
|
}
|
|
|
|
|
2016-01-22 05:33:21 -08:00
|
|
|
public function isDefault() {
|
|
|
|
return ($this->getVisibility() === self::VISIBILITY_DEFAULT);
|
|
|
|
}
|
|
|
|
|
Allow menu items to render their own content; make Dashboard items render on-page
Summary:
Ref T11957. When you click a dashboard item, it now sends you to `/<app>/item/view/123/`, which renders the proper crumbs, navigation, etc., with the dashboard as page content.
This works as you'd expect in Projects:
{F2508568}
It's sliiiightly odd in Favorites since we nuke the nav menu, but seems basically fine?
{F2508571}
Test Plan:
- Created a dashboard panel on a project.
- Clicked it, saw it render.
- Made it the default panel, viewed project default screen, saw dashboard.
- Disabled every panel I could, still saw reasonable behavior (this is silly anyway).
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T11957
Differential Revision: https://secure.phabricator.com/D17255
2017-01-26 12:14:02 -08:00
|
|
|
public function getItemIdentifier() {
|
|
|
|
$id = $this->getID();
|
|
|
|
|
|
|
|
if ($id) {
|
|
|
|
return (int)$id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->getBuiltinKey();
|
|
|
|
}
|
|
|
|
|
Replace ProfileMenu bugs with different bugs
Summary:
Ref T12174. This fixes more bugs than it creates, I think:
- Dashboards now show the whole menu.
- Project and home items now show selected state correctly.
- The "choose global vs personal" thing is now part of MenuEngine, and the same code builds it for Home and Favorites.
- Home now handles defaults correctly, I think.
Maybe regression/bad/still buggy?:
- Mobile home is now whatever the default thing was, not the menu?
- Title for dashboard content or other items that render their own content is incorrectly always "Configure Menu" (this was preexisting).
Test Plan:
- Created, edited, reordered, disabled, deleted and pinned personal and global items on home, favorites, and projects.
- Also checked User profiles.
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T12174
Differential Revision: https://secure.phabricator.com/D17273
2017-01-31 10:48:03 -08:00
|
|
|
public function getDefaultMenuItemKey() {
|
|
|
|
if ($this->getBuiltinKey()) {
|
|
|
|
return $this->getBuiltinKey();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->getPHID();
|
|
|
|
}
|
|
|
|
|
Allow menu items to render their own content; make Dashboard items render on-page
Summary:
Ref T11957. When you click a dashboard item, it now sends you to `/<app>/item/view/123/`, which renders the proper crumbs, navigation, etc., with the dashboard as page content.
This works as you'd expect in Projects:
{F2508568}
It's sliiiightly odd in Favorites since we nuke the nav menu, but seems basically fine?
{F2508571}
Test Plan:
- Created a dashboard panel on a project.
- Clicked it, saw it render.
- Made it the default panel, viewed project default screen, saw dashboard.
- Disabled every panel I could, still saw reasonable behavior (this is silly anyway).
Reviewers: chad
Reviewed By: chad
Maniphest Tasks: T11957
Differential Revision: https://secure.phabricator.com/D17255
2017-01-26 12:14:02 -08:00
|
|
|
public function newPageContent() {
|
|
|
|
return $this->getMenuItem()->newPageContent($this);
|
|
|
|
}
|
2016-01-12 10:27:39 -08:00
|
|
|
|
2019-03-30 14:22:08 -07:00
|
|
|
public function setIsHeadItem($is_head_item) {
|
|
|
|
$this->isHeadItem = $is_head_item;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getIsHeadItem() {
|
|
|
|
return $this->isHeadItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setIsTailItem($is_tail_item) {
|
|
|
|
$this->isTailItem = $is_tail_item;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getIsTailItem() {
|
|
|
|
return $this->isTailItem;
|
|
|
|
}
|
|
|
|
|
2019-03-31 13:58:30 -07:00
|
|
|
public function matchesIdentifier($identifier) {
|
|
|
|
if (!strlen($identifier)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ctype_digit($identifier)) {
|
|
|
|
if ((int)$this->getID() === (int)$identifier) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((string)$this->getBuiltinKey() === (string)$identifier) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-03-30 14:22:08 -07:00
|
|
|
|
2016-01-12 10:27:39 -08:00
|
|
|
/* -( 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) {
|
2017-01-19 11:06:42 -08:00
|
|
|
// If this is an item with a custom PHID (like a personal menu item),
|
|
|
|
// we only require that the user can edit the corresponding custom
|
|
|
|
// object (usually their own user profile), not the object that the
|
|
|
|
// menu appears on (which may be an Application like Favorites or Home).
|
|
|
|
if ($capability == PhabricatorPolicyCapability::CAN_EDIT) {
|
|
|
|
if ($this->getCustomPHID()) {
|
|
|
|
return array(
|
|
|
|
array(
|
|
|
|
$this->getCustomPHID(),
|
|
|
|
$capability,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-12 10:27:39 -08:00
|
|
|
return array(
|
|
|
|
array(
|
|
|
|
$this->getProfileObject(),
|
|
|
|
$capability,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-01-12 15:06:43 -08:00
|
|
|
|
|
|
|
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
|
|
|
|
|
|
|
|
|
|
|
public function getApplicationTransactionEditor() {
|
2016-12-11 09:38:06 -08:00
|
|
|
return new PhabricatorProfileMenuEditor();
|
2016-01-12 15:06:43 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getApplicationTransactionTemplate() {
|
2016-12-11 09:38:06 -08:00
|
|
|
return new PhabricatorProfileMenuItemConfigurationTransaction();
|
2016-01-12 15:06:43 -08:00
|
|
|
}
|
|
|
|
|
2016-01-12 10:27:39 -08:00
|
|
|
}
|