Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php
index fc04b94603..4f3937e3ae 100644
--- a/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php
+++ b/src/applications/dashboard/controller/dashboard/PhabricatorDashboardAdjustController.php
@@ -1,243 +1,243 @@
<?php
final class PhabricatorDashboardAdjustController
extends PhabricatorDashboardController {
private $contextPHID;
private $panelKey;
private $columnKey;
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$context_phid = $request->getStr('contextPHID');
$dashboard = id(new PhabricatorDashboardQuery())
->setViewer($viewer)
->withPHIDs(array($context_phid))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$dashboard) {
return new Aphront404Response();
}
$this->contextPHID = $context_phid;
$done_uri = $dashboard->getURI();
$ref_list = $dashboard->getPanelRefList();
$panel_ref = null;
$panel_key = $request->getStr('panelKey');
- if (strlen($panel_key)) {
+ if ($panel_key !== null && strlen($panel_key)) {
$panel_ref = $ref_list->getPanelRef($panel_key);
if (!$panel_ref) {
return new Aphront404Response();
}
$this->panelKey = $panel_key;
}
$column_key = $request->getStr('columnKey');
if (phutil_nonempty_string($column_key)) {
$columns = $ref_list->getColumns();
if (!isset($columns[$column_key])) {
return new Aphront404Response();
}
$this->columnKey = $column_key;
}
$after_ref = null;
$after_key = $request->getStr('afterKey');
if (phutil_nonempty_string($after_key)) {
$after_ref = $ref_list->getPanelRef($after_key);
if (!$after_ref) {
return new Aphront404Response();
}
}
switch ($request->getURIData('op')) {
case 'add':
return $this->handleAddRequest($dashboard, $done_uri);
case 'remove':
if (!$panel_ref) {
return new Aphront404Response();
}
return $this->handleRemoveRequest($dashboard, $panel_ref, $done_uri);
case 'move':
return $this->handleMoveRequest($dashboard, $panel_ref, $after_ref);
}
}
private function handleAddRequest(
PhabricatorDashboard $dashboard,
$done_uri) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$errors = array();
$panel_phid = null;
$e_panel = true;
if ($request->isFormPost()) {
$panel_phid = head($request->getArr('panelPHIDs'));
if (!$panel_phid) {
$errors[] = pht('You must choose a panel to add to the dashboard.');
$e_panel = pht('Required');
} else {
$panel = id(new PhabricatorDashboardPanelQuery())
->setViewer($viewer)
->withPHIDs(array($panel_phid))
->executeOne();
if (!$panel) {
$errors[] = pht('You must choose a valid panel.');
$e_panel = pht('Invalid');
}
}
if (!$errors) {
$xactions = array();
$ref_list = clone $dashboard->getPanelRefList();
$ref_list->newPanelRef($panel, $this->columnKey);
$new_panels = $ref_list->toDictionary();
$xactions[] = $dashboard->getApplicationTransactionTemplate()
->setTransactionType(
PhabricatorDashboardPanelsTransaction::TRANSACTIONTYPE)
->setNewValue($new_panels);
$editor = $dashboard->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($dashboard, $xactions);
return id(new AphrontRedirectResponse())->setURI($done_uri);
}
}
if ($panel_phid) {
$panel_phids = array($panel_phid);
} else {
$panel_phids = array();
}
$form = id(new AphrontFormView())
->setViewer($viewer)
->appendRemarkupInstructions(
pht('Choose a panel to add to this dashboard:'))
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorDashboardPanelDatasource())
->setLimit(1)
->setName('panelPHIDs')
->setLabel(pht('Panel'))
->setError($e_panel)
->setValue($panel_phids));
return $this->newEditDialog()
->setTitle(pht('Add Panel'))
->setWidth(AphrontDialogView::WIDTH_FORM)
->setErrors($errors)
->appendForm($form)
->addCancelButton($done_uri)
->addSubmitButton(pht('Add Panel'));
}
private function handleRemoveRequest(
PhabricatorDashboard $dashboard,
PhabricatorDashboardPanelRef $panel_ref,
$done_uri) {
$request = $this->getRequest();
$viewer = $this->getViewer();
// NOTE: If you can edit a dashboard, you can remove panels from it even
// if you don't have permission to see them or they aren't valid. We only
// require that the panel be present on the dashboard.
if ($request->isFormPost()) {
$xactions = array();
$ref_list = clone $dashboard->getPanelRefList();
$ref_list->removePanelRef($panel_ref);
$new_panels = $ref_list->toDictionary();
$xactions[] = $dashboard->getApplicationTransactionTemplate()
->setTransactionType(
PhabricatorDashboardPanelsTransaction::TRANSACTIONTYPE)
->setNewValue($new_panels);
$editor = $dashboard->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($dashboard, $xactions);
return id(new AphrontRedirectResponse())->setURI($done_uri);
}
$panel_phid = $panel_ref->getPanelPHID();
$handles = $viewer->loadHandles(array($panel_phid));
$handle = $handles[$panel_phid];
$message = pht(
'Remove panel %s from dashboard %s?',
phutil_tag('strong', array(), $handle->getFullName()),
phutil_tag('strong', array(), $dashboard->getName()));
return $this->newEditDialog()
->setTitle(pht('Remove Dashboard Panel'))
->appendParagraph($message)
->addCancelButton($done_uri)
->addSubmitButton(pht('Remove Panel'));
}
private function handleMoveRequest(
PhabricatorDashboard $dashboard,
PhabricatorDashboardPanelRef $panel_ref,
PhabricatorDashboardPanelRef $after_ref = null) {
$request = $this->getRequest();
$request->validateCSRF();
$viewer = $this->getViewer();
$xactions = array();
$ref_list = clone $dashboard->getPanelRefList();
$ref_list->movePanelRef($panel_ref, $this->columnKey, $after_ref);
$new_panels = $ref_list->toDictionary();
$xactions[] = $dashboard->getApplicationTransactionTemplate()
->setTransactionType(
PhabricatorDashboardPanelsTransaction::TRANSACTIONTYPE)
->setNewValue($new_panels);
$editor = $dashboard->getApplicationTransactionEditor()
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($dashboard, $xactions);
return id(new AphrontAjaxResponse())->setContent(array());
}
private function newEditDialog() {
return $this->newDialog()
->addHiddenInput('contextPHID', $this->contextPHID)
->addHiddenInput('panelKey', $this->panelKey)
->addHiddenInput('columnKey', $this->columnKey);
}
}
diff --git a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php
index 193e4580d6..7ea0345ceb 100644
--- a/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php
+++ b/src/applications/dashboard/controller/panel/PhabricatorDashboardPanelTabsController.php
@@ -1,437 +1,437 @@
<?php
final class PhabricatorDashboardPanelTabsController
extends PhabricatorDashboardController {
private $contextObject;
private function setContextObject($context_object) {
$this->contextObject = $context_object;
return $this;
}
private function getContextObject() {
return $this->contextObject;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$panel = id(new PhabricatorDashboardPanelQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$panel) {
return new Aphront404Response();
}
$tabs_type = id(new PhabricatorDashboardTabsPanelType())
->getPanelTypeKey();
// This controller may only be used to edit tab panels.
$panel_type = $panel->getPanelType();
if ($panel_type !== $tabs_type) {
return new Aphront404Response();
}
$op = $request->getURIData('op');
$after = $request->getStr('after');
- if (!strlen($after)) {
+ if ($after === '') {
$after = null;
}
$target = $request->getStr('target');
- if (!strlen($target)) {
+ if ($target === '') {
$target = null;
}
$impl = $panel->getImplementation();
$config = $impl->getPanelConfiguration($panel);
$cancel_uri = $panel->getURI();
if ($after !== null) {
$found = false;
foreach ($config as $key => $spec) {
if ((string)$key === $after) {
$found = true;
break;
}
}
if (!$found) {
return $this->newDialog()
->setTitle(pht('Adjacent Tab Not Found'))
->appendParagraph(
pht(
'Adjacent tab ("%s") was not found on this panel. It may have '.
'been removed.',
$after))
->addCancelButton($cancel_uri);
}
}
if ($target !== null) {
$found = false;
foreach ($config as $key => $spec) {
if ((string)$key === $target) {
$found = true;
break;
}
}
if (!$found) {
return $this->newDialog()
->setTitle(pht('Target Tab Not Found'))
->appendParagraph(
pht(
'Target tab ("%s") was not found on this panel. It may have '.
'been removed.',
$target))
->addCancelButton($cancel_uri);
}
}
// Tab panels may be edited from the panel page, or from the context of
// a dashboard. If we're editing from a dashboard, we want to redirect
// back to the dashboard after making changes.
$context_phid = $request->getStr('contextPHID');
$context = null;
if (strlen($context_phid)) {
$context = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs(array($context_phid))
->executeOne();
if (!$context) {
return new Aphront404Response();
}
switch (phid_get_type($context_phid)) {
case PhabricatorDashboardDashboardPHIDType::TYPECONST:
$cancel_uri = $context->getURI();
break;
case PhabricatorDashboardPanelPHIDType::TYPECONST:
$cancel_uri = $context->getURI();
break;
default:
return $this->newDialog()
->setTitle(pht('Context Object Unsupported'))
->appendParagraph(
pht(
'Context object ("%s") has unsupported type. Panels should '.
'be rendered from the context of a dashboard or another '.
'panel.',
$context_phid))
->addCancelButton($cancel_uri);
}
$this->setContextObject($context);
}
switch ($op) {
case 'add':
return $this->handleAddOperation($panel, $after, $cancel_uri);
case 'remove':
return $this->handleRemoveOperation($panel, $target, $cancel_uri);
case 'move':
return $this->handleMoveOperation($panel, $target, $after, $cancel_uri);
case 'rename':
return $this->handleRenameOperation($panel, $target, $cancel_uri);
}
}
private function handleAddOperation(
PhabricatorDashboardPanel $panel,
$after,
$cancel_uri) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$panel_phid = null;
$errors = array();
if ($request->isFormPost()) {
$panel_phid = $request->getArr('panelPHID');
$panel_phid = head($panel_phid);
$add_panel = id(new PhabricatorDashboardPanelQuery())
->setViewer($viewer)
->withPHIDs(array($panel_phid))
->executeOne();
if (!$add_panel) {
$errors[] = pht('You must select a valid panel.');
}
if (!$errors) {
$add_panel_config = array(
'name' => null,
'panelID' => $add_panel->getID(),
);
$add_panel_key = Filesystem::readRandomCharacters(12);
$impl = $panel->getImplementation();
$old_config = $impl->getPanelConfiguration($panel);
$new_config = array();
if ($after === null) {
$new_config = $old_config;
$new_config[] = $add_panel_config;
} else {
foreach ($old_config as $key => $value) {
$new_config[$key] = $value;
if ((string)$key === $after) {
$new_config[$add_panel_key] = $add_panel_config;
}
}
}
$xactions = array();
$xactions[] = $panel->getApplicationTransactionTemplate()
->setTransactionType(
PhabricatorDashboardTabsPanelTabsTransaction::TRANSACTIONTYPE)
->setNewValue($new_config);
$editor = id(new PhabricatorDashboardPanelTransactionEditor())
->setContentSourceFromRequest($request)
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
$editor->applyTransactions($panel, $xactions);
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
}
}
if ($panel_phid) {
$v_panel = array($panel_phid);
} else {
$v_panel = array();
}
$form = id(new AphrontFormView())
->setViewer($viewer)
->appendControl(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorDashboardPanelDatasource())
->setLimit(1)
->setName('panelPHID')
->setLabel(pht('Panel'))
->setValue($v_panel));
return $this->newEditDialog()
->setTitle(pht('Choose Dashboard Panel'))
->setErrors($errors)
->addHiddenInput('after', $after)
->appendForm($form)
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Add Panel'));
}
private function handleRemoveOperation(
PhabricatorDashboardPanel $panel,
$target,
$cancel_uri) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$panel_phid = null;
$errors = array();
if ($request->isFormPost()) {
$impl = $panel->getImplementation();
$old_config = $impl->getPanelConfiguration($panel);
$new_config = $this->removePanel($old_config, $target);
$this->writePanelConfig($panel, $new_config);
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
}
return $this->newEditDialog()
->setTitle(pht('Remove tab?'))
->addHiddenInput('target', $target)
->appendParagraph(pht('Really remove this tab?'))
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Remove Tab'));
}
private function handleRenameOperation(
PhabricatorDashboardPanel $panel,
$target,
$cancel_uri) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$impl = $panel->getImplementation();
$old_config = $impl->getPanelConfiguration($panel);
$spec = $old_config[$target];
$name = idx($spec, 'name');
if ($request->isFormPost()) {
$name = $request->getStr('name');
$new_config = $this->renamePanel($old_config, $target, $name);
$this->writePanelConfig($panel, $new_config);
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
}
$form = id(new AphrontFormView())
->setViewer($viewer)
->appendControl(
id(new AphrontFormTextControl())
->setValue($name)
->setName('name')
->setLabel(pht('Tab Name')));
return $this->newEditDialog()
->setTitle(pht('Rename Panel'))
->addHiddenInput('target', $target)
->appendForm($form)
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Rename Tab'));
}
private function handleMoveOperation(
PhabricatorDashboardPanel $panel,
$target,
$after,
$cancel_uri) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$move = $request->getStr('move');
$impl = $panel->getImplementation();
$old_config = $impl->getPanelConfiguration($panel);
$is_next = ($move === 'next');
if ($target === $after) {
return $this->newDialog()
->setTitle(pht('Impossible!'))
->appendParagraph(
pht(
'You can not move a tab relative to itself.'))
->addCancelButton($cancel_uri);
} else if ($is_next && ((string)last_key($old_config) === $target)) {
return $this->newDialog()
->setTitle(pht('Impossible!'))
->appendParagraph(
pht(
'This is already the last tab. It can not move any farther to '.
'the right.'))
->addCancelButton($cancel_uri);
} else if (!$is_next && (string)head_key($old_config) === $target) {
return $this->newDialog()
->setTitle(pht('Impossible!'))
->appendParagraph(
pht(
'This is already the first tab. It can not move any farther to '.
'the left.'))
->addCancelButton($cancel_uri);
}
if ($request->hasCSRF()) {
$new_config = array();
foreach ($old_config as $old_key => $old_spec) {
$old_key = (string)$old_key;
$is_after = ($old_key === $after);
if (!$is_after) {
if ($old_key === $target) {
continue;
}
}
if ($is_after && !$is_next) {
$new_config[$target] = $old_config[$target];
}
$new_config[$old_key] = $old_spec;
if ($is_after && $is_next) {
$new_config[$target] = $old_config[$target];
}
}
$this->writePanelConfig($panel, $new_config);
return id(new AphrontRedirectResponse())->setURI($cancel_uri);
}
if ($is_next) {
$prompt = pht('Move this tab to the right?');
} else {
$prompt = pht('Move this tab to the left?');
}
return $this->newEditDialog()
->setTitle(pht('Move Tab'))
->addHiddenInput('target', $target)
->addHiddenInput('after', $after)
->addHiddenInput('move', $move)
->appendParagraph($prompt)
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Move Tab'));
}
private function writePanelConfig(
PhabricatorDashboardPanel $panel,
array $config) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$xactions = array();
$xactions[] = $panel->getApplicationTransactionTemplate()
->setTransactionType(
PhabricatorDashboardTabsPanelTabsTransaction::TRANSACTIONTYPE)
->setNewValue($config);
$editor = id(new PhabricatorDashboardPanelTransactionEditor())
->setContentSourceFromRequest($request)
->setActor($viewer)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true);
return $editor->applyTransactions($panel, $xactions);
}
private function removePanel(array $config, $target) {
$result = array();
foreach ($config as $key => $panel_spec) {
if ((string)$key === $target) {
continue;
}
$result[$key] = $panel_spec;
}
return $result;
}
private function renamePanel(array $config, $target, $name) {
$config[$target]['name'] = $name;
return $config;
}
protected function newEditDialog() {
$dialog = $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FORM);
$context = $this->getContextObject();
if ($context) {
$dialog->addHiddenInput('contextPHID', $context->getPHID());
}
return $dialog;
}
}
diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
index cce6ee768f..3b9c3ad192 100644
--- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
+++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php
@@ -1,467 +1,468 @@
<?php
final class PhabricatorDashboardPanelRenderingEngine extends Phobject {
const HEADER_MODE_NORMAL = 'normal';
const HEADER_MODE_NONE = 'none';
const HEADER_MODE_EDIT = 'edit';
private $panel;
private $panelPHID;
private $viewer;
private $enableAsyncRendering;
private $parentPanelPHIDs;
private $headerMode = self::HEADER_MODE_NORMAL;
private $movable;
private $panelHandle;
private $editMode;
private $contextObject;
private $panelKey;
public function setContextObject($object) {
$this->contextObject = $object;
return $this;
}
public function getContextObject() {
return $this->contextObject;
}
public function setPanelKey($panel_key) {
$this->panelKey = $panel_key;
return $this;
}
public function getPanelKey() {
return $this->panelKey;
}
public function setHeaderMode($header_mode) {
$this->headerMode = $header_mode;
return $this;
}
public function getHeaderMode() {
return $this->headerMode;
}
public function setPanelHandle(PhabricatorObjectHandle $panel_handle) {
$this->panelHandle = $panel_handle;
return $this;
}
public function getPanelHandle() {
return $this->panelHandle;
}
public function isEditMode() {
return $this->editMode;
}
public function setEditMode($mode) {
$this->editMode = $mode;
return $this;
}
/**
* Allow the engine to render the panel via Ajax.
*/
public function setEnableAsyncRendering($enable) {
$this->enableAsyncRendering = $enable;
return $this;
}
public function setParentPanelPHIDs(array $parents) {
$this->parentPanelPHIDs = $parents;
return $this;
}
public function getParentPanelPHIDs() {
return $this->parentPanelPHIDs;
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setPanel(PhabricatorDashboardPanel $panel) {
$this->panel = $panel;
return $this;
}
public function setMovable($movable) {
$this->movable = $movable;
return $this;
}
public function getMovable() {
return $this->movable;
}
public function getPanel() {
return $this->panel;
}
public function setPanelPHID($panel_phid) {
$this->panelPHID = $panel_phid;
return $this;
}
public function getPanelPHID() {
return $this->panelPHID;
}
public function renderPanel() {
$panel = $this->getPanel();
if (!$panel) {
$handle = $this->getPanelHandle();
if ($handle->getPolicyFiltered()) {
return $this->renderErrorPanel(
pht('Restricted Panel'),
pht(
'You do not have permission to see this panel.'));
} else {
return $this->renderErrorPanel(
pht('Invalid Panel'),
pht(
'This panel is invalid or does not exist. It may have been '.
'deleted.'));
}
}
$panel_type = $panel->getImplementation();
if (!$panel_type) {
return $this->renderErrorPanel(
$panel->getName(),
pht(
'This panel has type "%s", but that panel type is unknown.',
$panel->getPanelType()));
}
try {
$this->detectRenderingCycle($panel);
if ($this->enableAsyncRendering) {
if ($panel_type->shouldRenderAsync()) {
return $this->renderAsyncPanel();
}
}
return $this->renderNormalPanel();
} catch (Exception $ex) {
+ phlog($ex);
return $this->renderErrorPanel(
$panel->getName(),
pht(
'%s: %s',
phutil_tag('strong', array(), get_class($ex)),
$ex->getMessage()));
}
}
private function renderNormalPanel() {
$panel = $this->getPanel();
$panel_type = $panel->getImplementation();
$content = $panel_type->renderPanelContent(
$this->getViewer(),
$panel,
$this);
$header = $this->renderPanelHeader();
return $this->renderPanelDiv(
$content,
$header);
}
private function renderAsyncPanel() {
$context_phid = $this->getContextPHID();
$panel = $this->getPanel();
$panel_id = celerity_generate_unique_node_id();
Javelin::initBehavior(
'dashboard-async-panel',
array(
'panelID' => $panel_id,
'parentPanelPHIDs' => $this->getParentPanelPHIDs(),
'headerMode' => $this->getHeaderMode(),
'contextPHID' => $context_phid,
'panelKey' => $this->getPanelKey(),
'movable' => $this->getMovable(),
'uri' => '/dashboard/panel/render/'.$panel->getID().'/',
));
$header = $this->renderPanelHeader();
$content = id(new PHUIPropertyListView())
->addTextContent(pht('Loading...'));
return $this->renderPanelDiv(
$content,
$header,
$panel_id);
}
private function renderErrorPanel($title, $body) {
switch ($this->getHeaderMode()) {
case self::HEADER_MODE_NONE:
$header = null;
break;
case self::HEADER_MODE_EDIT:
$header = id(new PHUIHeaderView())
->setHeader($title);
$header = $this->addPanelHeaderActions($header);
break;
case self::HEADER_MODE_NORMAL:
default:
$header = id(new PHUIHeaderView())
->setHeader($title);
break;
}
$icon = id(new PHUIIconView())
->setIcon('fa-warning red msr');
$content = id(new PHUIBoxView())
->addClass('dashboard-box')
->addMargin(PHUI::MARGIN_LARGE)
->appendChild($icon)
->appendChild($body);
return $this->renderPanelDiv(
$content,
$header);
}
private function renderPanelDiv(
$content,
$header = null,
$id = null) {
require_celerity_resource('phabricator-dashboard-css');
$panel = $this->getPanel();
if (!$id) {
$id = celerity_generate_unique_node_id();
}
$box = new PHUIObjectBoxView();
$interface = 'PhabricatorApplicationSearchResultView';
if ($content instanceof $interface) {
if ($content->getObjectList()) {
$box->setObjectList($content->getObjectList());
}
if ($content->getTable()) {
$box->setTable($content->getTable());
}
if ($content->getContent()) {
$box->appendChild($content->getContent());
}
} else {
$box->appendChild($content);
}
$box
->setHeader($header)
->setID($id)
->addClass('dashboard-box')
->addSigil('dashboard-panel');
// Allow to style Archived Panels differently.
if ($panel && $panel->getIsArchived()) {
$box->addClass('dashboard-panel-disabled');
}
if ($this->getMovable()) {
$box->addSigil('panel-movable');
}
if ($panel) {
$box->setMetadata(
array(
'panelKey' => $this->getPanelKey(),
));
}
return $box;
}
private function renderPanelHeader() {
$panel = $this->getPanel();
switch ($this->getHeaderMode()) {
case self::HEADER_MODE_NONE:
$header = null;
break;
case self::HEADER_MODE_EDIT:
// In edit mode, include the panel monogram to make managing boards
// a little easier.
$header_text = pht('%s %s', $panel->getMonogram(), $panel->getName());
$header = id(new PHUIHeaderView())
->setHeader($header_text);
$header = $this->addPanelHeaderActions($header);
// If the Panel is Archived, show in edit mode as such.
if ($panel && $panel->getIsArchived()) {
$header->setSubheader(
id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_RED)
->setIcon('fa-ban')
->setName(pht('Archived')));
}
break;
case self::HEADER_MODE_NORMAL:
default:
$header = id(new PHUIHeaderView())
->setHeader($panel->getName());
$panel_type = $panel->getImplementation();
$header = $panel_type->adjustPanelHeader(
$this->getViewer(),
$panel,
$this,
$header);
break;
}
return $header;
}
private function addPanelHeaderActions(
PHUIHeaderView $header) {
$viewer = $this->getViewer();
$panel = $this->getPanel();
$context_phid = $this->getContextPHID();
$actions = array();
if ($panel) {
try {
$panel_actions = $panel->newHeaderEditActions(
$viewer,
$context_phid);
} catch (Exception $ex) {
$error_action = id(new PhabricatorActionView())
->setIcon('fa-exclamation-triangle red')
->setName(pht('<Rendering Exception>'));
$panel_actions[] = $error_action;
}
if ($panel_actions) {
foreach ($panel_actions as $panel_action) {
$actions[] = $panel_action;
}
$actions[] = id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER);
}
$panel_id = $panel->getID();
$edit_uri = "/dashboard/panel/edit/{$panel_id}/";
$params = array(
'contextPHID' => $context_phid,
);
$edit_uri = new PhutilURI($edit_uri, $params);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Panel'))
->setHref($edit_uri);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-window-maximize')
->setName(pht('View Panel Details'))
->setHref($panel->getURI());
}
if ($context_phid) {
$panel_phid = $this->getPanelPHID();
$remove_uri = urisprintf('/dashboard/adjust/remove/');
$params = array(
'contextPHID' => $context_phid,
'panelKey' => $this->getPanelKey(),
);
$remove_uri = new PhutilURI($remove_uri, $params);
$actions[] = id(new PhabricatorActionView())
->setIcon('fa-times')
->setHref($remove_uri)
->setName(pht('Remove Panel'))
->setWorkflow(true);
}
$dropdown_menu = id(new PhabricatorActionListView())
->setViewer($viewer);
foreach ($actions as $action) {
$dropdown_menu->addAction($action);
}
$action_menu = id(new PHUIButtonView())
->setTag('a')
->setIcon('fa-cog')
->setText(pht('Manage Panel'))
->setDropdownMenu($dropdown_menu);
$header->addActionLink($action_menu);
return $header;
}
/**
* Detect graph cycles in panels, and deeply nested panels.
*
* This method throws if the current rendering stack is too deep or contains
* a cycle. This can happen if you embed layout panels inside each other,
* build a big stack of panels, or embed a panel in remarkup inside another
* panel. Generally, all of this stuff is ridiculous and we just want to
* shut it down.
*
* @param PhabricatorDashboardPanel Panel being rendered.
* @return void
*/
private function detectRenderingCycle(PhabricatorDashboardPanel $panel) {
if ($this->parentPanelPHIDs === null) {
throw new PhutilInvalidStateException('setParentPanelPHIDs');
}
$max_depth = 4;
if (count($this->parentPanelPHIDs) >= $max_depth) {
throw new Exception(
pht(
'To render more than %s levels of panels nested inside other '.
'panels, purchase a subscription to %s Gold.',
new PhutilNumber($max_depth),
PlatformSymbols::getPlatformServerName()));
}
if (in_array($panel->getPHID(), $this->parentPanelPHIDs)) {
throw new Exception(
pht(
'You awake in a twisting maze of mirrors, all alike. '.
'You are likely to be eaten by a graph cycle. '.
'Should you escape alive, you resolve to be more careful about '.
'putting dashboard panels inside themselves.'));
}
}
private function getContextPHID() {
$context = $this->getContextObject();
if ($context) {
return $context->getPHID();
}
return null;
}
}
diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php
index 9bc84d820e..3cf73b539e 100644
--- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php
+++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php
@@ -1,361 +1,361 @@
<?php
final class PhabricatorDashboardTabsPanelType
extends PhabricatorDashboardPanelType {
public function getPanelTypeKey() {
return 'tabs';
}
public function getPanelTypeName() {
return pht('Tab Panel');
}
public function getIcon() {
return 'fa-columns';
}
public function getPanelTypeDescription() {
return pht('Use tabs to switch between several other panels.');
}
protected function newEditEngineFields(PhabricatorDashboardPanel $panel) {
return array();
}
public function shouldRenderAsync() {
// The actual tab panel itself is cheap to render.
return false;
}
public function getPanelConfiguration(PhabricatorDashboardPanel $panel) {
$config = $panel->getProperty('config');
if (!is_array($config)) {
// NOTE: The older version of this panel stored raw JSON.
try {
$config = phutil_json_decode($config);
} catch (PhutilJSONParserException $ex) {
$config = array();
}
}
return $config;
}
public function renderPanelContent(
PhabricatorUser $viewer,
PhabricatorDashboardPanel $panel,
PhabricatorDashboardPanelRenderingEngine $engine) {
$is_edit = $engine->isEditMode();
$config = $this->getPanelConfiguration($panel);
$context_object = $engine->getContextObject();
if (!$context_object) {
$context_object = $panel;
}
$context_phid = $context_object->getPHID();
$list = id(new PHUIListView())
->setType(PHUIListView::NAVBAR_LIST);
$ids = ipull($config, 'panelID');
if ($ids) {
$panels = id(new PhabricatorDashboardPanelQuery())
->setViewer($viewer)
->withIDs($ids)
->execute();
} else {
$panels = array();
}
$id = $panel->getID();
$add_uri = urisprintf('/dashboard/panel/tabs/%d/add/', $id);
$add_uri = id(new PhutilURI($add_uri))
->replaceQueryParam('contextPHID', $context_phid);
$remove_uri = urisprintf('/dashboard/panel/tabs/%d/remove/', $id);
$remove_uri = id(new PhutilURI($remove_uri))
->replaceQueryParam('contextPHID', $context_phid);
$rename_uri = urisprintf('/dashboard/panel/tabs/%d/rename/', $id);
$rename_uri = id(new PhutilURI($rename_uri))
->replaceQueryParam('contextPHID', $context_phid);
$selected = 0;
$key_list = array_keys($config);
$next_keys = array();
$prev_keys = array();
for ($ii = 0; $ii < count($key_list); $ii++) {
$next_keys[$key_list[$ii]] = idx($key_list, $ii + 1);
$prev_keys[$key_list[$ii]] = idx($key_list, $ii - 1);
}
foreach ($config as $idx => $tab_spec) {
$panel_id = idx($tab_spec, 'panelID');
$subpanel = idx($panels, $panel_id);
- $name = idx($tab_spec, 'name');
+ $name = coalesce(idx($tab_spec, 'name'), '');
if (!strlen($name)) {
if ($subpanel) {
$name = $subpanel->getName();
}
}
if (!strlen($name)) {
$name = pht('Unnamed Tab');
}
$is_selected = (string)$idx === (string)$selected;
$tab_view = id(new PHUIListItemView())
->setHref('#')
->setSelected($is_selected)
->addSigil('dashboard-tab-panel-tab')
->setMetadata(array('panelKey' => $idx))
->setName($name);
if ($is_edit) {
$dropdown_menu = id(new PhabricatorActionListView())
->setViewer($viewer);
$remove_tab_uri = id(clone $remove_uri)
->replaceQueryParam('target', $idx);
$rename_tab_uri = id(clone $rename_uri)
->replaceQueryParam('target', $idx);
if ($subpanel) {
$details_uri = $subpanel->getURI();
} else {
$details_uri = null;
}
$edit_uri = urisprintf(
'/dashboard/panel/edit/%d/',
$panel_id);
if ($subpanel) {
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$subpanel,
PhabricatorPolicyCapability::CAN_EDIT);
} else {
$can_edit = false;
}
$dropdown_menu->addAction(
id(new PhabricatorActionView())
->setName(pht('Rename Tab'))
->setIcon('fa-pencil')
->setHref($rename_tab_uri)
->setWorkflow(true));
$move_uri = urisprintf('/dashboard/panel/tabs/%d/move/', $id);
$prev_key = $prev_keys[$idx];
$prev_params = array(
'target' => $idx,
'after' => $prev_key,
'move' => 'prev',
'contextPHID' => $context_phid,
);
$prev_uri = new PhutilURI($move_uri, $prev_params);
$next_key = $next_keys[$idx];
$next_params = array(
'target' => $idx,
'after' => $next_key,
'move' => 'next',
'contextPHID' => $context_phid,
);
$next_uri = new PhutilURI($move_uri, $next_params);
$dropdown_menu->addAction(
id(new PhabricatorActionView())
->setName(pht('Move Tab Left'))
->setIcon('fa-chevron-left')
->setHref($prev_uri)
->setWorkflow(true)
->setDisabled($prev_key === null));
$dropdown_menu->addAction(
id(new PhabricatorActionView())
->setName(pht('Move Tab Right'))
->setIcon('fa-chevron-right')
->setHref($next_uri)
->setWorkflow(true)
->setDisabled($next_key === null));
$dropdown_menu->addAction(
id(new PhabricatorActionView())
->setName(pht('Remove Tab'))
->setIcon('fa-times')
->setHref($remove_tab_uri)
->setWorkflow(true));
$dropdown_menu->addAction(
id(new PhabricatorActionView())
->setType(PhabricatorActionView::TYPE_DIVIDER));
$dropdown_menu->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Panel'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setWorkflow(true)
->setDisabled(!$can_edit));
$dropdown_menu->addAction(
id(new PhabricatorActionView())
->setName(pht('View Panel Details'))
->setIcon('fa-window-maximize')
->setHref($details_uri)
->setDisabled(!$subpanel));
$tab_view
->setActionIcon('fa-caret-down', '#')
->setDropdownMenu($dropdown_menu);
}
$list->addMenuItem($tab_view);
}
if ($is_edit) {
$actions = id(new PhabricatorActionListView())
->setViewer($viewer);
$add_last_uri = clone $add_uri;
$last_idx = last_key($config);
if ($last_idx) {
$add_last_uri->replaceQueryParam('after', $last_idx);
}
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Add Existing Panel'))
->setIcon('fa-window-maximize')
->setHref($add_last_uri)
->setWorkflow(true));
$list->addMenuItem(
id(new PHUIListItemView())
->setHref('#')
->setSelected(false)
->setName(pht('Add Tab...'))
->setDropdownMenu($actions));
}
$parent_phids = $engine->getParentPanelPHIDs();
$parent_phids[] = $panel->getPHID();
// TODO: Currently, we'll load all the panels on page load. It would be
// vaguely nice to load hidden panels only when the user selects them.
// TODO: Maybe we should persist which panel the user selected, so it
// remains selected across page loads.
$content = array();
$panel_list = array();
$no_headers = PhabricatorDashboardPanelRenderingEngine::HEADER_MODE_NONE;
foreach ($config as $idx => $tab_spec) {
$panel_id = idx($tab_spec, 'panelID');
$subpanel = idx($panels, $panel_id);
if ($subpanel) {
$panel_content = id(new PhabricatorDashboardPanelRenderingEngine())
->setViewer($viewer)
->setEnableAsyncRendering(true)
->setContextObject($context_object)
->setParentPanelPHIDs($parent_phids)
->setPanel($subpanel)
->setPanelPHID($subpanel->getPHID())
->setHeaderMode($no_headers)
->renderPanel();
} else {
$panel_content = pht('(Invalid Panel)');
}
$is_selected = (string)$idx === (string)$selected;
$content_id = celerity_generate_unique_node_id();
$content[] = phutil_tag(
'div',
array(
'id' => $content_id,
'style' => $is_selected ? null : 'display: none',
),
$panel_content);
$panel_list[] = array(
'panelKey' => (string)$idx,
'panelContentID' => $content_id,
);
}
if (!$content) {
if ($is_edit) {
$message = pht(
'This tab panel does not have any tabs yet. Use "Add Tab..." to '.
'create or place a tab.');
} else {
$message = pht(
'This tab panel does not have any tabs yet.');
}
$content = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NODATA)
->setErrors(
array(
$message,
));
$content = id(new PHUIBoxView())
->addClass('mlt mlb')
->appendChild($content);
}
Javelin::initBehavior('dashboard-tab-panel');
return javelin_tag(
'div',
array(
'sigil' => 'dashboard-tab-panel-container',
'meta' => array(
'panels' => $panel_list,
),
),
array(
$list,
$content,
));
}
public function getSubpanelPHIDs(PhabricatorDashboardPanel $panel) {
$config = $this->getPanelConfiguration($panel);
$panel_ids = array();
foreach ($config as $tab_key => $tab_spec) {
$panel_ids[] = $tab_spec['panelID'];
}
if ($panel_ids) {
$panels = id(new PhabricatorDashboardPanelQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withIDs($panel_ids)
->execute();
} else {
$panels = array();
}
return mpull($panels, 'getPHID');
}
}
diff --git a/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php b/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php
index 55b7c2225a..2544f8b7da 100644
--- a/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php
+++ b/src/applications/search/engineextension/PhabricatorFerretSearchEngineExtension.php
@@ -1,73 +1,73 @@
<?php
final class PhabricatorFerretSearchEngineExtension
extends PhabricatorSearchEngineExtension {
const EXTENSIONKEY = 'ferret';
public function isExtensionEnabled() {
return true;
}
public function getExtensionName() {
return pht('Fulltext Search');
}
public function getExtensionOrder() {
return 1000;
}
public function supportsObject($object) {
return ($object instanceof PhabricatorFerretInterface);
}
public function applyConstraintsToQuery(
$object,
$query,
PhabricatorSavedQuery $saved,
array $map) {
- if (!strlen($map['query'])) {
+ if (!(isset($map['query']) && strlen($map['query']))) {
return;
}
$engine = $object->newFerretEngine();
$raw_query = $map['query'];
$compiler = id(new PhutilSearchQueryCompiler())
->setEnableFunctions(true);
$raw_tokens = $compiler->newTokens($raw_query);
$fulltext_tokens = array();
foreach ($raw_tokens as $raw_token) {
$fulltext_token = id(new PhabricatorFulltextToken())
->setToken($raw_token);
$fulltext_tokens[] = $fulltext_token;
}
$query->withFerretConstraint($engine, $fulltext_tokens);
}
public function getSearchFields($object) {
$fields = array();
$fields[] = id(new PhabricatorSearchTextField())
->setKey('query')
->setLabel(pht('Query'))
->setDescription(
pht(
'Find objects matching a fulltext search query. See '.
'"Search User Guide" in the documentation for details.'));
return $fields;
}
public function getSearchAttachments($object) {
return array();
}
}

File Metadata

Mime Type
text/x-diff
Expires
Thu, May 1, 4:53 AM (1 d, 13 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
108866
Default Alt Text
(46 KB)

Event Timeline