2016-01-12 19:27:39 +01:00
|
|
|
<?php
|
|
|
|
|
2016-01-14 14:59:25 +01:00
|
|
|
abstract class PhabricatorProfilePanelEngine extends Phobject {
|
2016-01-12 19:27:39 +01:00
|
|
|
|
|
|
|
private $viewer;
|
|
|
|
private $profileObject;
|
|
|
|
private $panels;
|
2016-01-22 14:33:21 +01:00
|
|
|
private $defaultPanel;
|
2016-01-13 00:06:43 +01:00
|
|
|
private $controller;
|
2016-01-19 19:35:32 +01:00
|
|
|
private $navigation;
|
2016-01-12 19:27:39 +01:00
|
|
|
|
|
|
|
public function setViewer(PhabricatorUser $viewer) {
|
|
|
|
$this->viewer = $viewer;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getViewer() {
|
|
|
|
return $this->viewer;
|
|
|
|
}
|
|
|
|
|
2016-01-14 14:59:25 +01:00
|
|
|
public function setProfileObject($profile_object) {
|
2016-01-12 19:27:39 +01:00
|
|
|
$this->profileObject = $profile_object;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getProfileObject() {
|
|
|
|
return $this->profileObject;
|
|
|
|
}
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
public function setController(PhabricatorController $controller) {
|
|
|
|
$this->controller = $controller;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getController() {
|
|
|
|
return $this->controller;
|
|
|
|
}
|
|
|
|
|
2016-01-22 14:33:21 +01:00
|
|
|
private function setDefaultPanel(
|
|
|
|
PhabricatorProfilePanelConfiguration $default_panel) {
|
|
|
|
$this->defaultPanel = $default_panel;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getDefaultPanel() {
|
|
|
|
$this->loadPanels();
|
|
|
|
return $this->defaultPanel;
|
|
|
|
}
|
|
|
|
|
2016-01-14 14:59:25 +01:00
|
|
|
abstract protected function getPanelURI($path);
|
|
|
|
|
2016-01-14 15:47:06 +01:00
|
|
|
protected function isPanelEngineConfigurable() {
|
|
|
|
return PhabricatorEnv::getEnvConfig('phabricator.show-prototypes');
|
|
|
|
}
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
public function buildResponse() {
|
|
|
|
$controller = $this->getController();
|
|
|
|
|
|
|
|
$viewer = $controller->getViewer();
|
|
|
|
$this->setViewer($viewer);
|
|
|
|
|
|
|
|
$request = $controller->getRequest();
|
|
|
|
|
|
|
|
$panel_action = $request->getURIData('panelAction');
|
2016-01-14 15:47:06 +01:00
|
|
|
|
|
|
|
// If the engine is not configurable, don't respond to any of the editing
|
|
|
|
// or configuration routes.
|
|
|
|
if (!$this->isPanelEngineConfigurable()) {
|
|
|
|
switch ($panel_action) {
|
|
|
|
case 'view':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
$panel_id = $request->getURIData('panelID');
|
|
|
|
|
|
|
|
$panel_list = $this->loadPanels();
|
|
|
|
|
|
|
|
$selected_panel = null;
|
|
|
|
if (strlen($panel_id)) {
|
|
|
|
$panel_id_int = (int)$panel_id;
|
|
|
|
foreach ($panel_list as $panel) {
|
|
|
|
if ($panel_id_int) {
|
2016-01-13 19:40:31 +01:00
|
|
|
if ((int)$panel->getID() === $panel_id_int) {
|
2016-01-13 00:06:43 +01:00
|
|
|
$selected_panel = $panel;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$builtin_key = $panel->getBuiltinKey();
|
|
|
|
if ($builtin_key === (string)$panel_id) {
|
|
|
|
$selected_panel = $panel;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ($panel_action) {
|
|
|
|
case 'view':
|
|
|
|
case 'info':
|
|
|
|
case 'hide':
|
2016-01-22 14:33:21 +01:00
|
|
|
case 'default':
|
2016-01-13 00:06:43 +01:00
|
|
|
case 'builtin':
|
|
|
|
if (!$selected_panel) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
$navigation = $this->buildNavigation();
|
|
|
|
$navigation->selectFilter('panel.configure');
|
|
|
|
|
|
|
|
$crumbs = $controller->buildApplicationCrumbsForEditEngine();
|
|
|
|
|
|
|
|
switch ($panel_action) {
|
|
|
|
case 'view':
|
|
|
|
$content = $this->buildPanelViewContent($selected_panel);
|
|
|
|
break;
|
|
|
|
case 'configure':
|
|
|
|
$content = $this->buildPanelConfigureContent($panel_list);
|
|
|
|
$crumbs->addTextCrumb(pht('Configure Menu'));
|
|
|
|
break;
|
2016-01-13 18:40:27 +01:00
|
|
|
case 'reorder':
|
|
|
|
$content = $this->buildPanelReorderContent($panel_list);
|
|
|
|
break;
|
2016-01-13 00:06:43 +01:00
|
|
|
case 'new':
|
|
|
|
$panel_key = $request->getURIData('panelKey');
|
|
|
|
$content = $this->buildPanelNewContent($panel_key);
|
|
|
|
break;
|
|
|
|
case 'builtin':
|
|
|
|
$content = $this->buildPanelBuiltinContent($selected_panel);
|
|
|
|
break;
|
2016-01-13 19:40:31 +01:00
|
|
|
case 'hide':
|
|
|
|
$content = $this->buildPanelHideContent($selected_panel);
|
|
|
|
break;
|
2016-01-22 14:33:21 +01:00
|
|
|
case 'default':
|
|
|
|
$content = $this->buildPanelDefaultContent(
|
|
|
|
$selected_panel,
|
|
|
|
$panel_list);
|
|
|
|
break;
|
2016-01-13 00:06:43 +01:00
|
|
|
case 'edit':
|
|
|
|
$content = $this->buildPanelEditContent();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Unsupported panel action "%s".',
|
|
|
|
$panel_action));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($content instanceof AphrontResponse) {
|
|
|
|
return $content;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($content instanceof AphrontResponseProducerInterface) {
|
|
|
|
return $content;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $controller->newPage()
|
|
|
|
->setTitle(pht('Profile Stuff'))
|
|
|
|
->setNavigation($navigation)
|
|
|
|
->setCrumbs($crumbs)
|
|
|
|
->appendChild($content);
|
|
|
|
}
|
|
|
|
|
2016-01-12 19:27:39 +01:00
|
|
|
public function buildNavigation() {
|
2016-01-19 19:35:32 +01:00
|
|
|
if ($this->navigation) {
|
|
|
|
return $this->navigation;
|
|
|
|
}
|
|
|
|
|
2016-01-12 19:27:39 +01:00
|
|
|
$nav = id(new AphrontSideNavFilterView())
|
2016-01-14 01:07:04 +01:00
|
|
|
->setIsProfileMenu(true)
|
2016-01-14 14:59:25 +01:00
|
|
|
->setBaseURI(new PhutilURI($this->getPanelURI('')));
|
2016-01-12 19:27:39 +01:00
|
|
|
|
|
|
|
$panels = $this->getPanels();
|
|
|
|
|
|
|
|
foreach ($panels as $panel) {
|
2016-01-13 19:40:31 +01:00
|
|
|
if ($panel->isDisabled()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-01-12 19:27:39 +01:00
|
|
|
$items = $panel->buildNavigationMenuItems();
|
|
|
|
foreach ($items as $item) {
|
|
|
|
$this->validateNavigationMenuItem($item);
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the panel 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 = $panel->getBuiltinKey();
|
|
|
|
$panel_phid = $panel->getPHID();
|
|
|
|
if ($builtin_key !== null) {
|
|
|
|
$item->setKey($builtin_key);
|
|
|
|
} else if ($panel_phid !== null) {
|
|
|
|
$item->setKey($panel_phid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($items as $item) {
|
|
|
|
$nav->addMenuItem($item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-19 19:35:32 +01:00
|
|
|
$more_items = $this->newAutomaticMenuItems($nav);
|
|
|
|
foreach ($more_items as $item) {
|
|
|
|
$nav->addMenuItem($item);
|
2016-01-13 00:06:43 +01:00
|
|
|
}
|
|
|
|
|
2016-01-12 19:27:39 +01:00
|
|
|
$nav->selectFilter(null);
|
|
|
|
|
2016-01-19 19:35:32 +01:00
|
|
|
$this->navigation = $nav;
|
|
|
|
return $this->navigation;
|
2016-01-12 19:27:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private function getPanels() {
|
|
|
|
if ($this->panels === null) {
|
|
|
|
$this->panels = $this->loadPanels();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this->panels;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function loadPanels() {
|
|
|
|
$viewer = $this->getViewer();
|
2016-01-13 00:06:43 +01:00
|
|
|
$object = $this->getProfileObject();
|
2016-01-12 19:27:39 +01:00
|
|
|
|
|
|
|
$panels = $this->loadBuiltinProfilePanels();
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
$stored_panels = id(new PhabricatorProfilePanelConfigurationQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withProfilePHIDs(array($object->getPHID()))
|
|
|
|
->execute();
|
|
|
|
|
|
|
|
// Merge the stored panels into the builtin panels. If a builtin panel has
|
|
|
|
// a stored version, replace the defaults with the stored changes.
|
|
|
|
foreach ($stored_panels as $stored_panel) {
|
|
|
|
$builtin_key = $stored_panel->getBuiltinKey();
|
|
|
|
if ($builtin_key !== null) {
|
2016-01-22 14:33:21 +01:00
|
|
|
// 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
|
|
|
|
// panel.
|
|
|
|
if (isset($panels[$builtin_key])) {
|
|
|
|
$panels[$builtin_key] = $stored_panel;
|
|
|
|
} else {
|
|
|
|
continue;
|
|
|
|
}
|
2016-01-13 00:06:43 +01:00
|
|
|
} else {
|
|
|
|
$panels[] = $stored_panel;
|
|
|
|
}
|
|
|
|
}
|
2016-01-12 19:27:39 +01:00
|
|
|
|
|
|
|
foreach ($panels as $panel) {
|
|
|
|
$impl = $panel->getPanel();
|
|
|
|
|
|
|
|
$impl->setViewer($viewer);
|
|
|
|
}
|
|
|
|
|
2016-01-13 18:40:27 +01:00
|
|
|
$panels = msort($panels, 'getSortKey');
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
// Normalize keys since callers shouldn't rely on this array being
|
|
|
|
// partially keyed.
|
|
|
|
$panels = array_values($panels);
|
|
|
|
|
2016-01-22 14:33:21 +01:00
|
|
|
|
|
|
|
// Make sure exactly one valid panel is marked as default.
|
|
|
|
$default = null;
|
|
|
|
$first = null;
|
|
|
|
foreach ($panels as $panel) {
|
|
|
|
if (!$panel->canMakeDefault()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($panel->isDefault()) {
|
|
|
|
$default = $panel;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($first === null) {
|
|
|
|
$first = $panel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$default) {
|
|
|
|
$default = $first;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($default) {
|
|
|
|
$this->setDefaultPanel($default);
|
|
|
|
}
|
|
|
|
|
2016-01-12 19:27:39 +01:00
|
|
|
return $panels;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function loadBuiltinProfilePanels() {
|
|
|
|
$object = $this->getProfileObject();
|
2016-01-14 14:59:25 +01:00
|
|
|
$builtins = $this->getBuiltinProfilePanels($object);
|
2016-01-12 19:27:39 +01:00
|
|
|
|
|
|
|
$panels = PhabricatorProfilePanel::getAllPanels();
|
|
|
|
|
|
|
|
$order = 1;
|
|
|
|
$map = array();
|
|
|
|
foreach ($builtins as $builtin) {
|
|
|
|
$builtin_key = $builtin->getBuiltinKey();
|
|
|
|
|
|
|
|
if (!$builtin_key) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Object produced a builtin panel with no builtin panel key! '.
|
|
|
|
'Builtin panels must have a unique key.'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($map[$builtin_key])) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Object produced two panels with the same builtin key ("%s"). '.
|
|
|
|
'Each panel must have a unique builtin key.',
|
|
|
|
$builtin_key));
|
|
|
|
}
|
|
|
|
|
|
|
|
$panel_key = $builtin->getPanelKey();
|
|
|
|
|
|
|
|
$panel = idx($panels, $panel_key);
|
|
|
|
if (!$panel) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'Builtin panel ("%s") specifies a bad panel key ("%s"); there '.
|
|
|
|
'is no corresponding panel implementation available.',
|
|
|
|
$builtin_key,
|
|
|
|
$panel_key));
|
|
|
|
}
|
|
|
|
|
|
|
|
$builtin
|
2016-01-13 00:06:43 +01:00
|
|
|
->setProfilePHID($object->getPHID())
|
2016-01-12 19:27:39 +01:00
|
|
|
->attachPanel($panel)
|
|
|
|
->attachProfileObject($object)
|
|
|
|
->setPanelOrder($order);
|
|
|
|
|
|
|
|
$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.'));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-19 19:35:32 +01:00
|
|
|
private function newAutomaticMenuItems(AphrontSideNavFilterView $nav) {
|
|
|
|
$items = array();
|
|
|
|
|
|
|
|
// NOTE: We're adding a spacer item for the fixed footer, so that if the
|
|
|
|
// menu taller than the page content you can still scroll down the page far
|
|
|
|
// enough to access the last item without the content being obscured by the
|
|
|
|
// fixed items.
|
|
|
|
$items[] = id(new PHUIListItemView())
|
|
|
|
->setHideInApplicationMenu(true)
|
|
|
|
->addClass('phui-profile-menu-spacer');
|
|
|
|
|
|
|
|
if ($this->isPanelEngineConfigurable()) {
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$object = $this->getProfileObject();
|
|
|
|
|
|
|
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
|
|
|
$viewer,
|
|
|
|
$object,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
2016-01-21 23:07:09 +01:00
|
|
|
$expanded_edit_icon = id(new PHUIIconCircleView())
|
2016-01-19 19:35:32 +01:00
|
|
|
->addClass('phui-list-item-icon')
|
|
|
|
->addClass('phui-profile-menu-visible-when-expanded')
|
|
|
|
->setIconFont('fa-pencil');
|
|
|
|
|
2016-01-21 23:07:09 +01:00
|
|
|
$collapsed_edit_icon = id(new PHUIIconCircleView())
|
2016-01-19 19:35:32 +01:00
|
|
|
->addClass('phui-list-item-icon')
|
|
|
|
->addClass('phui-profile-menu-visible-when-collapsed')
|
|
|
|
->setIconFont('fa-pencil')
|
|
|
|
->addSigil('has-tooltip')
|
|
|
|
->setMetadata(
|
|
|
|
array(
|
|
|
|
'tip' => pht('Edit Menu'),
|
|
|
|
'align' => 'E',
|
|
|
|
));
|
|
|
|
|
|
|
|
$items[] = id(new PHUIListItemView())
|
|
|
|
->setName('Edit Menu')
|
|
|
|
->setKey('panel.configure')
|
|
|
|
->addIcon($expanded_edit_icon)
|
|
|
|
->addIcon($collapsed_edit_icon)
|
|
|
|
->addClass('phui-profile-menu-footer')
|
|
|
|
->addClass('phui-profile-menu-footer-1')
|
|
|
|
->setHref($this->getPanelURI('configure/'))
|
|
|
|
->setDisabled(!$can_edit)
|
|
|
|
->setWorkflow(!$can_edit);
|
2016-01-13 00:06:43 +01:00
|
|
|
}
|
|
|
|
|
2016-01-19 19:35:32 +01:00
|
|
|
$collapse_id = celerity_generate_unique_node_id();
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
2016-01-19 19:35:32 +01:00
|
|
|
$collapse_key =
|
|
|
|
PhabricatorUserPreferences::PREFERENCE_PROFILE_MENU_COLLAPSED;
|
|
|
|
|
|
|
|
$preferences = $viewer->loadPreferences();
|
|
|
|
$is_collapsed = $preferences->getPreference($collapse_key, false);
|
|
|
|
|
|
|
|
if ($is_collapsed) {
|
|
|
|
$nav->addClass('phui-profile-menu-collapsed');
|
|
|
|
} else {
|
|
|
|
$nav->addClass('phui-profile-menu-expanded');
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($viewer->isLoggedIn()) {
|
|
|
|
$settings_uri = '/settings/adjust/?key='.$collapse_key;
|
|
|
|
} else {
|
|
|
|
$settings_uri = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
Javelin::initBehavior(
|
|
|
|
'phui-profile-menu',
|
|
|
|
array(
|
|
|
|
'menuID' => $nav->getMainID(),
|
|
|
|
'collapseID' => $collapse_id,
|
2016-01-22 13:01:55 +01:00
|
|
|
'isCollapsed' => (bool)$is_collapsed,
|
2016-01-19 19:35:32 +01:00
|
|
|
'settingsURI' => $settings_uri,
|
|
|
|
));
|
|
|
|
|
2016-01-21 23:07:09 +01:00
|
|
|
$collapse_icon = id(new PHUIIconCircleView())
|
2016-01-19 19:35:32 +01:00
|
|
|
->addClass('phui-list-item-icon')
|
|
|
|
->addClass('phui-profile-menu-visible-when-expanded')
|
|
|
|
->setIconFont('fa-angle-left');
|
|
|
|
|
2016-01-21 23:07:09 +01:00
|
|
|
$expand_icon = id(new PHUIIconCircleView())
|
2016-01-19 19:35:32 +01:00
|
|
|
->addClass('phui-list-item-icon')
|
|
|
|
->addClass('phui-profile-menu-visible-when-collapsed')
|
|
|
|
->addSigil('has-tooltip')
|
|
|
|
->setMetadata(
|
|
|
|
array(
|
|
|
|
'tip' => pht('Expand'),
|
|
|
|
'align' => 'E',
|
|
|
|
))
|
|
|
|
->setIconFont('fa-angle-right');
|
|
|
|
|
|
|
|
$items[] = id(new PHUIListItemView())
|
|
|
|
->setName('Collapse')
|
|
|
|
->addIcon($collapse_icon)
|
|
|
|
->addIcon($expand_icon)
|
|
|
|
->setID($collapse_id)
|
|
|
|
->addClass('phui-profile-menu-footer')
|
|
|
|
->addClass('phui-profile-menu-footer-2')
|
|
|
|
->setHideInApplicationMenu(true)
|
|
|
|
->setHref('#');
|
|
|
|
|
|
|
|
return $items;
|
2016-01-13 00:06:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function getConfigureURI() {
|
|
|
|
return $this->getPanelURI('configure/');
|
|
|
|
}
|
|
|
|
|
2016-01-13 18:40:27 +01:00
|
|
|
private function buildPanelReorderContent(array $panels) {
|
|
|
|
$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 ($panels as $key => $panel) {
|
|
|
|
$id = $panel->getID();
|
|
|
|
if ($id) {
|
|
|
|
$by_id[$id] = $key;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$builtin_key = $panel->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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$panels = array_select_keys($panels, $key_order) + $panels;
|
|
|
|
|
|
|
|
$type_order =
|
|
|
|
PhabricatorProfilePanelConfigurationTransaction::TYPE_ORDER;
|
|
|
|
|
|
|
|
$order = 1;
|
|
|
|
foreach ($panels as $panel) {
|
|
|
|
$xactions = array();
|
|
|
|
|
|
|
|
$xactions[] = id(new PhabricatorProfilePanelConfigurationTransaction())
|
|
|
|
->setTransactionType($type_order)
|
|
|
|
->setNewValue($order);
|
|
|
|
|
|
|
|
$editor = id(new PhabricatorProfilePanelEditor())
|
|
|
|
->setContentSourceFromRequest($request)
|
|
|
|
->setActor($viewer)
|
|
|
|
->setContinueOnMissingFields(true)
|
|
|
|
->setContinueOnNoEffect(true)
|
|
|
|
->applyTransactions($panel, $xactions);
|
|
|
|
|
|
|
|
$order++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setURI($this->getConfigureURI());
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
private function buildPanelConfigureContent(array $panels) {
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$object = $this->getProfileObject();
|
|
|
|
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$viewer,
|
|
|
|
$object,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
2016-01-13 18:40:27 +01:00
|
|
|
$list_id = celerity_generate_unique_node_id();
|
|
|
|
|
|
|
|
Javelin::initBehavior(
|
|
|
|
'reorder-profile-menu-items',
|
|
|
|
array(
|
|
|
|
'listID' => $list_id,
|
|
|
|
'orderURI' => $this->getPanelURI('reorder/'),
|
|
|
|
));
|
|
|
|
|
|
|
|
$list = id(new PHUIObjectItemListView())
|
|
|
|
->setID($list_id);
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
foreach ($panels as $panel) {
|
|
|
|
$id = $panel->getID();
|
|
|
|
$builtin_key = $panel->getBuiltinKey();
|
|
|
|
|
|
|
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
|
|
|
$viewer,
|
|
|
|
$panel,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
|
|
|
$item = id(new PHUIObjectItemView());
|
|
|
|
|
|
|
|
$name = $panel->getDisplayName();
|
|
|
|
$type = $panel->getPanelTypeName();
|
|
|
|
if (!strlen(trim($name))) {
|
|
|
|
$name = pht('Untitled "%s" Item', $type);
|
|
|
|
}
|
|
|
|
|
|
|
|
$item->setHeader($name);
|
|
|
|
$item->addAttribute($type);
|
|
|
|
|
|
|
|
if ($can_edit) {
|
2016-01-13 18:40:27 +01:00
|
|
|
$item
|
|
|
|
->setGrippable(true)
|
|
|
|
->addSigil('profile-menu-item')
|
|
|
|
->setMetadata(
|
|
|
|
array(
|
|
|
|
'key' => nonempty($id, $builtin_key),
|
|
|
|
));
|
|
|
|
|
2016-01-22 14:33:21 +01:00
|
|
|
if ($id) {
|
|
|
|
$default_uri = $this->getPanelURI("default/{$id}/");
|
|
|
|
} else {
|
|
|
|
$default_uri = $this->getPanelURI("default/{$builtin_key}/");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($panel->isDefault()) {
|
|
|
|
$default_icon = 'fa-thumb-tack green';
|
|
|
|
$default_text = pht('Current Default');
|
|
|
|
} else if ($panel->canMakeDefault()) {
|
|
|
|
$default_icon = 'fa-thumb-tack';
|
|
|
|
$default_text = pht('Make Default');
|
|
|
|
} else {
|
|
|
|
$default_text = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($default_text !== null) {
|
|
|
|
$item->addAction(
|
|
|
|
id(new PHUIListItemView())
|
|
|
|
->setHref($default_uri)
|
|
|
|
->setWorkflow(true)
|
|
|
|
->setName($default_text)
|
|
|
|
->setIcon($default_icon));
|
|
|
|
}
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
if ($id) {
|
|
|
|
$item->setHref($this->getPanelURI("edit/{$id}/"));
|
2016-01-13 19:40:31 +01:00
|
|
|
$hide_uri = $this->getPanelURI("hide/{$id}/");
|
2016-01-13 00:06:43 +01:00
|
|
|
} else {
|
|
|
|
$item->setHref($this->getPanelURI("builtin/{$builtin_key}/"));
|
2016-01-13 19:40:31 +01:00
|
|
|
$hide_uri = $this->getPanelURI("hide/{$builtin_key}/");
|
2016-01-13 00:06:43 +01:00
|
|
|
}
|
2016-01-13 19:40:31 +01:00
|
|
|
|
2016-01-22 14:33:21 +01:00
|
|
|
if ($panel->isDisabled()) {
|
|
|
|
$hide_icon = 'fa-plus';
|
2016-01-22 15:49:33 +01:00
|
|
|
$hide_text = pht('Enable');
|
2016-01-22 14:33:21 +01:00
|
|
|
} else if ($panel->getBuiltinKey() !== null) {
|
|
|
|
$hide_icon = 'fa-times';
|
|
|
|
$hide_text = pht('Disable');
|
|
|
|
} else {
|
|
|
|
$hide_icon = 'fa-times';
|
|
|
|
$hide_text = pht('Delete');
|
|
|
|
}
|
|
|
|
|
2016-01-13 19:40:31 +01:00
|
|
|
$item->addAction(
|
|
|
|
id(new PHUIListItemView())
|
|
|
|
->setHref($hide_uri)
|
|
|
|
->setWorkflow(true)
|
2016-01-22 14:33:21 +01:00
|
|
|
->setName($hide_text)
|
|
|
|
->setIcon($hide_icon));
|
2016-01-13 19:40:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if ($panel->isDisabled()) {
|
|
|
|
$item->setDisabled(true);
|
2016-01-13 00:06:43 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
$list->addItem($item);
|
|
|
|
}
|
|
|
|
|
|
|
|
$action_view = id(new PhabricatorActionListView())
|
|
|
|
->setUser($viewer);
|
|
|
|
|
|
|
|
$panel_types = PhabricatorProfilePanel::getAllPanels();
|
|
|
|
|
|
|
|
$action_view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setLabel(true)
|
|
|
|
->setName(pht('Add New Menu Item...')));
|
|
|
|
|
|
|
|
foreach ($panel_types as $panel_type) {
|
|
|
|
if (!$panel_type->canAddToObject($object)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$panel_key = $panel_type->getPanelKey();
|
|
|
|
|
|
|
|
$action_view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setIcon($panel_type->getPanelTypeIcon())
|
|
|
|
->setName($panel_type->getPanelTypeName())
|
|
|
|
->setHref($this->getPanelURI("new/{$panel_key}/")));
|
|
|
|
}
|
|
|
|
|
|
|
|
$action_view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setLabel(true)
|
|
|
|
->setName(pht('Documentation')));
|
|
|
|
|
2016-01-22 15:49:33 +01:00
|
|
|
$doc_link = PhabricatorEnv::getDoclink('Profile Menu User Guide');
|
|
|
|
$doc_name = pht('Profile Menu User Guide');
|
|
|
|
|
2016-01-13 00:06:43 +01:00
|
|
|
$action_view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setIcon('fa-book')
|
2016-01-22 15:49:33 +01:00
|
|
|
->setHref($doc_link)
|
|
|
|
->setName($doc_name));
|
2016-01-13 00:06:43 +01:00
|
|
|
|
|
|
|
$action_button = id(new PHUIButtonView())
|
|
|
|
->setTag('a')
|
|
|
|
->setText(pht('Configure Menu'))
|
|
|
|
->setHref('#')
|
|
|
|
->setIconFont('fa-gear')
|
|
|
|
->setDropdownMenu($action_view);
|
|
|
|
|
|
|
|
$header = id(new PHUIHeaderView())
|
|
|
|
->setHeader(pht('Profile Menu Items'))
|
|
|
|
->addActionLink($action_button);
|
|
|
|
|
|
|
|
$box = id(new PHUIObjectBoxView())
|
|
|
|
->setHeader($header)
|
|
|
|
->setObjectList($list);
|
|
|
|
|
|
|
|
return $box;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildPanelNewContent($panel_key) {
|
|
|
|
$panel_types = PhabricatorProfilePanel::getAllPanels();
|
|
|
|
$panel_type = idx($panel_types, $panel_key);
|
|
|
|
if (!$panel_type) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$object = $this->getProfileObject();
|
|
|
|
if (!$panel_type->canAddToObject($object)) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$configuration =
|
|
|
|
PhabricatorProfilePanelConfiguration::initializeNewPanelConfiguration(
|
|
|
|
$object,
|
|
|
|
$panel_type);
|
|
|
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$viewer,
|
|
|
|
$configuration,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
|
|
|
$controller = $this->getController();
|
|
|
|
|
|
|
|
return id(new PhabricatorProfilePanelEditEngine())
|
|
|
|
->setPanelEngine($this)
|
|
|
|
->setProfileObject($object)
|
|
|
|
->setNewPanelConfiguration($configuration)
|
|
|
|
->setController($controller)
|
|
|
|
->buildResponse();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildPanelEditContent() {
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$object = $this->getProfileObject();
|
|
|
|
$controller = $this->getController();
|
|
|
|
|
|
|
|
return id(new PhabricatorProfilePanelEditEngine())
|
|
|
|
->setPanelEngine($this)
|
|
|
|
->setProfileObject($object)
|
|
|
|
->setController($controller)
|
|
|
|
->buildResponse();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildPanelBuiltinContent(
|
|
|
|
PhabricatorProfilePanelConfiguration $configuration) {
|
|
|
|
|
|
|
|
// If this builtin panel has already been persisted, redirect to the
|
|
|
|
// edit page.
|
|
|
|
$id = $configuration->getID();
|
|
|
|
if ($id) {
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
->setURI($this->getPanelURI("edit/{$id}/"));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Otherwise, act like we're creating a new panel, 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 PhabricatorProfilePanelEditEngine())
|
|
|
|
->setIsBuiltin(true)
|
|
|
|
->setPanelEngine($this)
|
|
|
|
->setProfileObject($object)
|
|
|
|
->setNewPanelConfiguration($configuration)
|
|
|
|
->setController($controller)
|
|
|
|
->buildResponse();
|
|
|
|
}
|
|
|
|
|
2016-01-13 19:40:31 +01:00
|
|
|
private function buildPanelHideContent(
|
|
|
|
PhabricatorProfilePanelConfiguration $configuration) {
|
|
|
|
|
|
|
|
$controller = $this->getController();
|
|
|
|
$request = $controller->getRequest();
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$viewer,
|
|
|
|
$configuration,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
2016-01-22 14:33:21 +01:00
|
|
|
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 = PhabricatorProfilePanelConfiguration::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 = PhabricatorProfilePanelConfiguration::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');
|
|
|
|
}
|
|
|
|
|
2016-01-13 19:40:31 +01:00
|
|
|
$v_visibility = $configuration->getVisibility();
|
|
|
|
if ($request->isFormPost()) {
|
2016-01-22 14:33:21 +01:00
|
|
|
if ($new_value === null) {
|
|
|
|
$configuration->delete();
|
|
|
|
} else {
|
|
|
|
$type_visibility =
|
|
|
|
PhabricatorProfilePanelConfigurationTransaction::TYPE_VISIBILITY;
|
|
|
|
|
|
|
|
$xactions = array();
|
|
|
|
|
|
|
|
$xactions[] = id(new PhabricatorProfilePanelConfigurationTransaction())
|
|
|
|
->setTransactionType($type_visibility)
|
|
|
|
->setNewValue($new_value);
|
|
|
|
|
|
|
|
$editor = id(new PhabricatorProfilePanelEditor())
|
|
|
|
->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 buildPanelDefaultContent(
|
|
|
|
PhabricatorProfilePanelConfiguration $configuration,
|
|
|
|
array $panels) {
|
|
|
|
|
|
|
|
$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);
|
|
|
|
}
|
|
|
|
|
|
|
|
$type_visibility =
|
|
|
|
PhabricatorProfilePanelConfigurationTransaction::TYPE_VISIBILITY;
|
|
|
|
|
|
|
|
$v_visible = PhabricatorProfilePanelConfiguration::VISIBILITY_VISIBLE;
|
|
|
|
$v_default = PhabricatorProfilePanelConfiguration::VISIBILITY_DEFAULT;
|
|
|
|
|
|
|
|
if ($request->isFormPost()) {
|
|
|
|
// First, mark any existing default panels as merely visible.
|
|
|
|
foreach ($panels as $panel) {
|
|
|
|
if (!$panel->isDefault()) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
$xactions = array();
|
2016-01-13 19:40:31 +01:00
|
|
|
|
2016-01-22 14:33:21 +01:00
|
|
|
$xactions[] = id(new PhabricatorProfilePanelConfigurationTransaction())
|
|
|
|
->setTransactionType($type_visibility)
|
|
|
|
->setNewValue($v_visible);
|
|
|
|
|
|
|
|
$editor = id(new PhabricatorProfilePanelEditor())
|
|
|
|
->setContentSourceFromRequest($request)
|
|
|
|
->setActor($viewer)
|
|
|
|
->setContinueOnMissingFields(true)
|
|
|
|
->setContinueOnNoEffect(true)
|
|
|
|
->applyTransactions($panel, $xactions);
|
|
|
|
}
|
2016-01-13 19:40:31 +01:00
|
|
|
|
2016-01-22 14:33:21 +01:00
|
|
|
// Now, make this panel the default.
|
2016-01-13 19:40:31 +01:00
|
|
|
$xactions = array();
|
|
|
|
|
|
|
|
$xactions[] = id(new PhabricatorProfilePanelConfigurationTransaction())
|
|
|
|
->setTransactionType($type_visibility)
|
2016-01-22 14:33:21 +01:00
|
|
|
->setNewValue($v_default);
|
2016-01-13 19:40:31 +01:00
|
|
|
|
|
|
|
$editor = id(new PhabricatorProfilePanelEditor())
|
|
|
|
->setContentSourceFromRequest($request)
|
|
|
|
->setActor($viewer)
|
|
|
|
->setContinueOnMissingFields(true)
|
|
|
|
->setContinueOnNoEffect(true)
|
|
|
|
->applyTransactions($configuration, $xactions);
|
|
|
|
|
|
|
|
return id(new AphrontRedirectResponse())
|
2016-01-22 14:33:21 +01:00
|
|
|
->setURI($done_uri);
|
2016-01-13 19:40:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $controller->newDialog()
|
2016-01-22 14:33:21 +01:00
|
|
|
->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'));
|
2016-01-13 19:40:31 +01:00
|
|
|
}
|
|
|
|
|
2016-01-14 14:59:25 +01:00
|
|
|
protected function newPanel() {
|
|
|
|
return PhabricatorProfilePanelConfiguration::initializeNewBuiltin();
|
|
|
|
}
|
|
|
|
|
2016-01-12 19:27:39 +01:00
|
|
|
}
|