diff --git a/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php b/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php --- a/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardQueryPanelInstallController.php @@ -7,186 +7,160 @@ $viewer = $request->getViewer(); $v_dashboard = null; - $v_name = null; - $v_column = 0; - $v_engine = $request->getURIData('engineKey'); - $v_query = $request->getURIData('queryKey'); + $e_dashboard = null; + $v_name = null; $e_name = true; - // Validate Engines - $engines = PhabricatorApplicationSearchEngine::getAllEngines(); - foreach ($engines as $name => $engine) { - if (!$engine->canUseInPanelContext()) { - unset($engines[$name]); - } - } - if (!in_array($v_engine, array_keys($engines))) { - return new Aphront404Response(); + $v_engine = $request->getStr('engine'); + if (!strlen($v_engine)) { + $v_engine = $request->getURIData('engineKey'); } - // Validate Queries - $engine = $engines[$v_engine]; - $engine->setViewer($viewer); - $good_query = false; - if ($engine->isBuiltinQuery($v_query)) { - $good_query = true; - } else { - $saved_query = id(new PhabricatorSavedQueryQuery()) - ->setViewer($viewer) - ->withEngineClassNames(array($v_engine)) - ->withQueryKeys(array($v_query)) - ->executeOne(); - if ($saved_query) { - $good_query = true; - } - } - if (!$good_query) { - return new Aphront404Response(); + $v_query = $request->getStr('query'); + if (!strlen($v_query)) { + $v_query = $request->getURIData('queryKey'); } - $named_query = idx($engine->loadEnabledNamedQueries(), $v_query); - if ($named_query) { - $v_name = $named_query->getQueryName(); + $engines = PhabricatorApplicationSearchEngine::getAllEngines(); + $engine = idx($engines, $v_engine); + if ($engine) { + $engine = id(clone $engine) + ->setViewer($viewer); + + $redirect_uri = $engine->getQueryResultsPageURI($v_query); + + $named_query = idx($engine->loadEnabledNamedQueries(), $v_query); + if ($named_query) { + $v_name = $named_query->getQueryName(); + } + } else { + $redirect_uri = '/'; } $errors = array(); + $xaction_name = PhabricatorDashboardPanelNameTransaction::TRANSACTIONTYPE; + $xaction_engine = + PhabricatorDashboardQueryPanelApplicationTransaction::TRANSACTIONTYPE; + $xaction_query = + PhabricatorDashboardQueryPanelQueryTransaction::TRANSACTIONTYPE; + if ($request->isFormPost()) { - $v_dashboard = $request->getInt('dashboardID'); $v_name = $request->getStr('name'); if (!$v_name) { $errors[] = pht('You must provide a name for this panel.'); $e_name = pht('Required'); } - $dashboard = id(new PhabricatorDashboardQuery()) - ->setViewer($viewer) - ->withIDs(array($v_dashboard)) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->executeOne(); - - if (!$dashboard) { - $errors[] = pht('Please select a valid dashboard.'); + $v_dashboard = head($request->getArr('dashboardPHIDs')); + if (!$v_dashboard) { + $errors[] = pht('You must select a dashboard.'); + $e_dashboard = pht('Required'); + } else { + $dashboard = id(new PhabricatorDashboardQuery()) + ->setViewer($viewer) + ->withPHIDs(array($v_dashboard)) + ->executeOne(); + if (!$dashboard) { + $errors[] = pht('You must select a valid dashboard.'); + $e_dashboard = pht('Invalid'); + } + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $dashboard, + PhabricatorPolicyCapability::CAN_EDIT); + if (!$can_edit) { + $errors[] = pht( + 'You must select a dashboard you have permission to edit.'); + } } if (!$errors) { - $redirect_uri = "/dashboard/view/{$v_dashboard}/"; + $done_uri = $dashboard->getURI(); + + // First, create a new panel. $panel_type = id(new PhabricatorDashboardQueryPanelType()) ->getPanelTypeKey(); - $panel = PhabricatorDashboardPanel::initializeNewPanel($viewer); - $panel->setPanelType($panel_type); - $field_list = PhabricatorCustomField::getObjectFields( - $panel, - PhabricatorCustomField::ROLE_EDIT); - - $field_list - ->setViewer($viewer) - ->readFieldsFromStorage($panel); - - $panel->requireImplementation()->initializeFieldsFromRequest( - $panel, - $field_list, - $request); + $panel = PhabricatorDashboardPanel::initializeNewPanel($viewer) + ->setPanelType($panel_type); $xactions = array(); - $xactions[] = id(new PhabricatorDashboardPanelTransaction()) - ->setTransactionType( - PhabricatorDashboardPanelNameTransaction::TRANSACTIONTYPE) - ->setNewValue($v_name); - - $xactions[] = id(new PhabricatorDashboardPanelTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) - ->setMetadataValue('customfield:key', 'std:dashboard:core:class') - ->setOldValue(null) + $xactions[] = $panel->getApplicationTransactionTemplate() + ->setTransactionType($xaction_engine) ->setNewValue($v_engine); - $xactions[] = id(new PhabricatorDashboardPanelTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_CUSTOMFIELD) - ->setMetadataValue('customfield:key', 'std:dashboard:core:key') - ->setOldValue(null) + $xactions[] = $panel->getApplicationTransactionTemplate() + ->setTransactionType($xaction_query) ->setNewValue($v_query); - $editor = id(new PhabricatorDashboardPanelTransactionEditor()) + $xactions[] = $panel->getApplicationTransactionTemplate() + ->setTransactionType($xaction_name) + ->setNewValue($v_name); + + $editor = $panel->getApplicationTransactionEditor() ->setActor($viewer) - ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->applyTransactions($panel, $xactions); - PhabricatorDashboardTransactionEditor::addPanelToDashboard( - $viewer, - PhabricatorContentSource::newFromRequest($request), - $panel, - $dashboard, - $request->getInt('column', 0)); + // Now that we've created a panel, add it to the dashboard. + + $xactions = array(); + + $ref_list = clone $dashboard->getPanelRefList(); + $ref_list->newPanelRef($panel); + $new_panels = $ref_list->toDictionary(); + + $xactions[] = $dashboard->getApplicationTransactionTemplate() + ->setTransactionType( + PhabricatorDashboardPanelsTransaction::TRANSACTIONTYPE) + ->setNewValue($new_panels); - return id(new AphrontRedirectResponse())->setURI($redirect_uri); + $editor = $dashboard->getApplicationTransactionEditor() + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($dashboard, $xactions); + + return id(new AphrontRedirectResponse())->setURI($done_uri); } } - // Make this a select for now, as we don't expect someone to have - // edit access to a vast number of dashboards. - // Can add optiongroup if needed down the road. - $dashboards = id(new PhabricatorDashboardQuery()) - ->setViewer($viewer) - ->withStatuses(array( - PhabricatorDashboard::STATUS_ACTIVE, - )) - ->requireCapabilities( - array( - PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_EDIT, - )) - ->execute(); - $options = mpull($dashboards, 'getName', 'getID'); - asort($options); - - $redirect_uri = $engine->getQueryResultsPageURI($v_query); - - if (!$options) { - $notice = id(new PHUIInfoView()) - ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) - ->appendChild(pht('You do not have access to any dashboards. To '. - 'continue, please create a dashboard first.')); - - return $this->newDialog() - ->setTitle(pht('No Dashboards')) - ->setWidth(AphrontDialogView::WIDTH_FORM) - ->appendChild($notice) - ->addCancelButton($redirect_uri); + if ($v_dashboard) { + $dashboard_phids = array($v_dashboard); + } else { + $dashboard_phids = array(); } $form = id(new AphrontFormView()) - ->setUser($viewer) - ->addHiddenInput('engine', $v_engine) - ->addHiddenInput('query', $v_query) - ->addHiddenInput('column', $v_column) - ->appendChild( + ->setViewer($viewer) + ->appendControl( id(new AphrontFormTextControl()) ->setLabel(pht('Name')) ->setName('name') ->setValue($v_name) ->setError($e_name)) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setUser($this->getViewer()) - ->setValue($v_dashboard) - ->setName('dashboardID') - ->setOptions($options) + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setValue($dashboard_phids) + ->setError($e_dashboard) + ->setName('dashboardPHIDs') + ->setLimit(1) + ->setDatasource(new PhabricatorDashboardDatasource()) ->setLabel(pht('Dashboard'))); return $this->newDialog() ->setTitle(pht('Add Panel to Dashboard')) ->setErrors($errors) ->setWidth(AphrontDialogView::WIDTH_FORM) - ->appendChild($form->buildLayoutView()) + ->addHiddenInput('engine', $v_engine) + ->addHiddenInput('query', $v_query) + ->appendForm($form) ->addCancelButton($redirect_uri) ->addSubmitButton(pht('Add Panel')); diff --git a/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRefList.php b/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRefList.php --- a/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRefList.php +++ b/src/applications/dashboard/layoutconfig/PhabricatorDashboardPanelRefList.php @@ -87,7 +87,14 @@ return array_values(mpull($this->getPanelRefs(), 'toDictionary')); } - public function newPanelRef(PhabricatorDashboardPanel $panel, $column_key) { + public function newPanelRef( + PhabricatorDashboardPanel $panel, + $column_key = null) { + + if ($column_key === null) { + $column_key = head_key($this->columns); + } + $ref = id(new PhabricatorDashboardPanelRef()) ->setPanelKey($this->newPanelKey()) ->setPanelPHID($panel->getPHID()) diff --git a/src/applications/dashboard/storage/PhabricatorDashboardPanel.php b/src/applications/dashboard/storage/PhabricatorDashboardPanel.php --- a/src/applications/dashboard/storage/PhabricatorDashboardPanel.php +++ b/src/applications/dashboard/storage/PhabricatorDashboardPanel.php @@ -45,9 +45,8 @@ ) + parent::getConfiguration(); } - public function generatePHID() { - return PhabricatorPHID::generateNewPHID( - PhabricatorDashboardPanelPHIDType::TYPECONST); + public function getPHIDType() { + return PhabricatorDashboardPanelPHIDType::TYPECONST; } public function getProperty($key, $default = null) { diff --git a/src/applications/dashboard/xaction/panel/PhabricatorDashboardQueryPanelApplicationTransaction.php b/src/applications/dashboard/xaction/panel/PhabricatorDashboardQueryPanelApplicationTransaction.php --- a/src/applications/dashboard/xaction/panel/PhabricatorDashboardQueryPanelApplicationTransaction.php +++ b/src/applications/dashboard/xaction/panel/PhabricatorDashboardQueryPanelApplicationTransaction.php @@ -9,4 +9,31 @@ return 'class'; } + public function validateTransactions($object, array $xactions) { + $errors = array(); + + $engines = PhabricatorApplicationSearchEngine::getAllEngines(); + + $old_value = $object->getProperty($this->getPropertyKey()); + foreach ($xactions as $xaction) { + $new_value = $xaction->getNewValue(); + + if ($new_value === $old_value) { + continue; + } + + if (!isset($engines[$new_value])) { + $errors[] = $this->newInvalidError( + pht( + 'Application search engine class "%s" is unknown. Query panels '. + 'must use a known search engine class.', + $new_value), + $xaction); + continue; + } + } + + return $errors; + } + }