2016-01-12 19:27:39 +01:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorProfilePanelEngine extends Phobject {
|
|
|
|
|
|
|
|
private $viewer;
|
|
|
|
private $profileObject;
|
|
|
|
private $panels;
|
2016-01-13 00:06:43 +01:00
|
|
|
private $controller;
|
2016-01-12 19:27:39 +01:00
|
|
|
|
|
|
|
public function setViewer(PhabricatorUser $viewer) {
|
|
|
|
$this->viewer = $viewer;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getViewer() {
|
|
|
|
return $this->viewer;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setProfileObject(
|
|
|
|
PhabricatorProfilePanelInterface $profile_object) {
|
|
|
|
$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;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function buildResponse() {
|
|
|
|
$controller = $this->getController();
|
|
|
|
|
|
|
|
$viewer = $controller->getViewer();
|
|
|
|
$this->setViewer($viewer);
|
|
|
|
|
|
|
|
$request = $controller->getRequest();
|
|
|
|
|
|
|
|
$panel_action = $request->getURIData('panelAction');
|
|
|
|
$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) {
|
|
|
|
if ((int)$panel->getID() === $panel_id) {
|
|
|
|
$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':
|
|
|
|
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;
|
|
|
|
case 'new':
|
|
|
|
$panel_key = $request->getURIData('panelKey');
|
|
|
|
$content = $this->buildPanelNewContent($panel_key);
|
|
|
|
break;
|
|
|
|
case 'builtin':
|
|
|
|
$content = $this->buildPanelBuiltinContent($selected_panel);
|
|
|
|
break;
|
|
|
|
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() {
|
|
|
|
$nav = id(new AphrontSideNavFilterView())
|
|
|
|
->setIconNav(true)
|
|
|
|
->setBaseURI(new PhutilURI('/project/'));
|
|
|
|
|
|
|
|
$panels = $this->getPanels();
|
|
|
|
|
|
|
|
foreach ($panels as $panel) {
|
|
|
|
$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-13 00:06:43 +01:00
|
|
|
$configure_item = $this->newConfigureMenuItem();
|
|
|
|
if ($configure_item) {
|
|
|
|
$nav->addMenuItem($configure_item);
|
|
|
|
}
|
|
|
|
|
2016-01-12 19:27:39 +01:00
|
|
|
$nav->selectFilter(null);
|
|
|
|
|
|
|
|
return $nav;
|
|
|
|
}
|
|
|
|
|
|
|
|
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) {
|
|
|
|
$panels[$builtin_key] = $stored_panel;
|
|
|
|
} 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 00:06:43 +01:00
|
|
|
// Normalize keys since callers shouldn't rely on this array being
|
|
|
|
// partially keyed.
|
|
|
|
$panels = array_values($panels);
|
|
|
|
|
2016-01-12 19:27:39 +01:00
|
|
|
return $panels;
|
|
|
|
}
|
|
|
|
|
|
|
|
private function loadBuiltinProfilePanels() {
|
|
|
|
$object = $this->getProfileObject();
|
|
|
|
$builtins = $object->getBuiltinProfilePanels();
|
|
|
|
|
|
|
|
$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-13 00:06:43 +01:00
|
|
|
private function newConfigureMenuItem() {
|
|
|
|
if (!PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$object = $this->getProfileObject();
|
|
|
|
|
|
|
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
|
|
|
$viewer,
|
|
|
|
$object,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
|
|
|
return id(new PHUIListItemView())
|
|
|
|
->setName('Configure Menu')
|
|
|
|
->setKey('panel.configure')
|
|
|
|
->setIcon('fa-gear')
|
|
|
|
->setHref($this->getPanelURI('configure/'))
|
|
|
|
->setDisabled(!$can_edit)
|
|
|
|
->setWorkflow(!$can_edit)
|
|
|
|
->setRenderNameAsTooltip(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getConfigureURI() {
|
|
|
|
return $this->getPanelURI('configure/');
|
|
|
|
}
|
|
|
|
|
|
|
|
private function getPanelURI($path) {
|
|
|
|
$project = $this->getProfileObject();
|
|
|
|
$id = $project->getID();
|
|
|
|
return "/project/{$id}/panel/{$path}";
|
|
|
|
}
|
|
|
|
|
|
|
|
private function buildPanelConfigureContent(array $panels) {
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$object = $this->getProfileObject();
|
|
|
|
|
|
|
|
PhabricatorPolicyFilter::requireCapability(
|
|
|
|
$viewer,
|
|
|
|
$object,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT);
|
|
|
|
|
|
|
|
$list = new PHUIObjectItemListView();
|
|
|
|
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) {
|
|
|
|
if ($id) {
|
|
|
|
$item->setHref($this->getPanelURI("edit/{$id}/"));
|
|
|
|
} else {
|
|
|
|
$item->setHref($this->getPanelURI("builtin/{$builtin_key}/"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$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')));
|
|
|
|
|
|
|
|
$action_view->addAction(
|
|
|
|
id(new PhabricatorActionView())
|
|
|
|
->setIcon('fa-book')
|
|
|
|
->setName(pht('TODO: Write Documentation')));
|
|
|
|
|
|
|
|
$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-12 19:27:39 +01:00
|
|
|
}
|