diff --git a/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php b/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php index f34bdd06ae..46712d79e9 100644 --- a/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php +++ b/src/applications/dashboard/controller/PhabricatorDashboardAddPanelController.php @@ -1,102 +1,95 @@ getViewer(); $id = $request->getURIData('id'); $dashboard = id(new PhabricatorDashboardQuery()) ->setViewer($viewer) ->withIDs(array($id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$dashboard) { return new Aphront404Response(); } $redirect_uri = $this->getApplicationURI( 'arrange/'.$dashboard->getID().'/'); - $v_panel = $request->getStr('panel'); + $v_panel = head($request->getArr('panel')); $e_panel = true; $errors = array(); if ($request->isFormPost()) { if (strlen($v_panel)) { $panel = id(new PhabricatorDashboardPanelQuery()) ->setViewer($viewer) ->withIDs(array($v_panel)) ->executeOne(); if (!$panel) { $errors[] = pht('No such panel!'); $e_panel = pht('Invalid'); } } else { $errors[] = pht('Select a panel to add.'); $e_panel = pht('Required'); } if (!$errors) { PhabricatorDashboardTransactionEditor::addPanelToDashboard( $viewer, PhabricatorContentSource::newFromRequest($request), $panel, $dashboard, $request->getInt('column', 0)); return id(new AphrontRedirectResponse())->setURI($redirect_uri); } } $panels = id(new PhabricatorDashboardPanelQuery()) ->setViewer($viewer) ->withArchived(false) ->execute(); if (!$panels) { return $this->newDialog() ->setTitle(pht('No Panels Exist Yet')) ->appendParagraph( pht( 'You have not created any dashboard panels yet, so you can not '. 'add an existing panel.')) ->appendParagraph( pht('Instead, add a new panel.')) ->addCancelButton($redirect_uri); } - $panel_options = array(); - foreach ($panels as $panel) { - $panel_options[$panel->getID()] = pht( - '%s %s', - $panel->getMonogram(), - $panel->getName()); - } - $form = id(new AphrontFormView()) ->setUser($viewer) ->addHiddenInput('column', $request->getInt('column')) ->appendRemarkupInstructions( pht('Choose a panel to add to this dashboard:')) ->appendChild( - id(new AphrontFormSelectControl()) + id(new AphrontFormTokenizerControl()) + ->setUser($this->getViewer()) + ->setDatasource(new PhabricatorDashboardPanelDatasource()) + ->setLimit(1) ->setName('panel') ->setLabel(pht('Panel')) - ->setValue($v_panel) - ->setError($e_panel) - ->setOptions($panel_options)); + ->setValue($v_panel)); return $this->newDialog() ->setTitle(pht('Add Panel')) ->setErrors($errors) ->appendChild($form->buildLayoutView()) ->addCancelButton($redirect_uri) ->addSubmitButton(pht('Add Panel')); } } diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardPanelType.php index 81e7ec80cc..34c7b1c3ee 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardPanelType.php @@ -1,55 +1,56 @@ setAncestorClass(__CLASS__) ->setUniqueMethod('getPanelTypeKey') ->execute(); } } diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php index 76e6e8432b..0781d71b16 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardQueryPanelType.php @@ -1,148 +1,152 @@ array( 'name' => pht('Search For'), 'type' => 'search.application', ), 'key' => array( 'name' => pht('Query'), 'type' => 'search.query', 'control.application' => 'class', ), 'limit' => array( 'name' => pht('Limit'), 'caption' => pht('Leave this blank for the default number of items.'), 'type' => 'text', ), ); } public function initializeFieldsFromRequest( PhabricatorDashboardPanel $panel, PhabricatorCustomFieldList $field_list, AphrontRequest $request) { $map = array(); if (strlen($request->getStr('engine'))) { $map['class'] = $request->getStr('engine'); } if (strlen($request->getStr('query'))) { $map['key'] = $request->getStr('query'); } $full_map = array(); foreach ($map as $key => $value) { $full_map["std:dashboard:core:{$key}"] = $value; } foreach ($field_list->getFields() as $field) { $field_key = $field->getFieldKey(); if (isset($full_map[$field_key])) { $field->setValueFromStorage($full_map[$field_key]); } } } public function renderPanelContent( PhabricatorUser $viewer, PhabricatorDashboardPanel $panel, PhabricatorDashboardPanelRenderingEngine $engine) { $engine = $this->getSearchEngine($panel); $engine->setViewer($viewer); $engine->setContext(PhabricatorApplicationSearchEngine::CONTEXT_PANEL); $key = $panel->getProperty('key'); if ($engine->isBuiltinQuery($key)) { $saved = $engine->buildSavedQueryFromBuiltin($key); } else { $saved = id(new PhabricatorSavedQueryQuery()) ->setViewer($viewer) ->withEngineClassNames(array(get_class($engine))) ->withQueryKeys(array($key)) ->executeOne(); } if (!$saved) { throw new Exception( pht( 'Query "%s" is unknown to application search engine "%s"!', $key, get_class($engine))); } $query = $engine->buildQueryFromSavedQuery($saved); $pager = $engine->newPagerForSavedQuery($saved); if ($panel->getProperty('limit')) { $limit = (int)$panel->getProperty('limit'); if ($pager->getPageSize() !== 0xFFFF) { $pager->setPageSize($limit); } } $results = $engine->executeQuery($query, $pager); return $engine->renderResults($results, $saved); } public function adjustPanelHeader( PhabricatorUser $viewer, PhabricatorDashboardPanel $panel, PhabricatorDashboardPanelRenderingEngine $engine, PHUIHeaderView $header) { $search_engine = $this->getSearchEngine($panel); $key = $panel->getProperty('key'); $href = $search_engine->getQueryResultsPageURI($key); $icon = id(new PHUIIconView()) ->setIcon('fa-search') ->setHref($href); $header->addActionItem($icon); return $header; } private function getSearchEngine(PhabricatorDashboardPanel $panel) { $class = $panel->getProperty('class'); $engine = PhabricatorApplicationSearchEngine::getEngineByClassName($class); if (!$engine) { throw new Exception( pht( 'The application search engine "%s" is not known to Phabricator!', $class)); } if (!$engine->canUseInPanelContext()) { throw new Exception( pht( 'Application search engines of class "%s" can not be used to build '. 'dashboard panels.', $class)); } return $engine; } } diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php index 3cb758a11a..292446fe55 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTabsPanelType.php @@ -1,126 +1,130 @@ array( 'name' => pht('Tabs'), 'type' => 'dashboard.tabs', ), ); } public function shouldRenderAsync() { // The actual tab panel itself is cheap to render. return false; } public function renderPanelContent( PhabricatorUser $viewer, PhabricatorDashboardPanel $panel, PhabricatorDashboardPanelRenderingEngine $engine) { $config = $panel->getProperty('config'); if (!is_array($config)) { // NOTE: The older version of this panel stored raw JSON. $config = phutil_json_decode($config); } $list = id(new PHUIListView()) ->setType(PHUIListView::NAVBAR_LIST); $selected = 0; $node_ids = array(); foreach ($config as $idx => $tab_spec) { $node_ids[$idx] = celerity_generate_unique_node_id(); } foreach ($config as $idx => $tab_spec) { $list->addMenuItem( id(new PHUIListItemView()) ->setHref('#') ->setSelected($idx == $selected) ->addSigil('dashboard-tab-panel-tab') ->setMetadata(array('idx' => $idx)) ->setName(idx($tab_spec, 'name', pht('Nameless Tab')))); } $ids = ipull($config, 'panelID'); if ($ids) { $panels = id(new PhabricatorDashboardPanelQuery()) ->setViewer($viewer) ->withIDs($ids) ->execute(); } else { $panels = array(); } $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(); $no_headers = PhabricatorDashboardPanelRenderingEngine::HEADER_MODE_NONE; foreach ($config as $idx => $tab_spec) { $panel_id = idx($tab_spec, 'panelID'); $panel = idx($panels, $panel_id); if ($panel) { $panel_content = id(new PhabricatorDashboardPanelRenderingEngine()) ->setViewer($viewer) ->setEnableAsyncRendering(true) ->setParentPanelPHIDs($parent_phids) ->setPanel($panel) ->setPanelPHID($panel->getPHID()) ->setHeaderMode($no_headers) ->renderPanel(); } else { $panel_content = pht('(Invalid Panel)'); } $content[] = phutil_tag( 'div', array( 'id' => $node_ids[$idx], 'style' => ($idx == $selected) ? null : 'display: none', ), $panel_content); } Javelin::initBehavior('dashboard-tab-panel'); return javelin_tag( 'div', array( 'sigil' => 'dashboard-tab-panel-container', 'meta' => array( 'panels' => $node_ids, ), ), array( $list, $content, )); } } diff --git a/src/applications/dashboard/paneltype/PhabricatorDashboardTextPanelType.php b/src/applications/dashboard/paneltype/PhabricatorDashboardTextPanelType.php index 413d3dc856..5ecde8501f 100644 --- a/src/applications/dashboard/paneltype/PhabricatorDashboardTextPanelType.php +++ b/src/applications/dashboard/paneltype/PhabricatorDashboardTextPanelType.php @@ -1,64 +1,68 @@ array( 'name' => pht('Text'), 'type' => 'remarkup', ), ); } public function shouldRenderAsync() { // Rendering text panels is normally a cheap cache hit. return false; } public function renderPanelContent( PhabricatorUser $viewer, PhabricatorDashboardPanel $panel, PhabricatorDashboardPanelRenderingEngine $engine) { $text = $panel->getProperty('text', ''); $oneoff = id(new PhabricatorMarkupOneOff())->setContent($text); $field = 'default'; // NOTE: We're taking extra steps here to prevent creation of a text panel // which embeds itself using `{Wnnn}`, recursing indefinitely. $parent_key = PhabricatorDashboardRemarkupRule::KEY_PARENT_PANEL_PHIDS; $parent_phids = $engine->getParentPanelPHIDs(); $parent_phids[] = $panel->getPHID(); $markup_engine = id(new PhabricatorMarkupEngine()) ->setViewer($viewer) ->setContextObject($panel) ->setAuxiliaryConfig($parent_key, $parent_phids); $text_content = $markup_engine ->addObject($oneoff, $field) ->process() ->getOutput($oneoff, $field); return id(new PHUIPropertyListView()) ->addTextContent($text_content); } } diff --git a/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php b/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php index 35c8c5c212..958883d34e 100644 --- a/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php +++ b/src/applications/dashboard/typeahead/PhabricatorDashboardPanelDatasource.php @@ -1,46 +1,65 @@ buildResults(); + return $this->filterResultsAgainstTokens($results); + } + + + protected function renderSpecialTokens(array $values) { + return $this->renderTokensFromResults($this->buildResults(), $values); + } + public function buildResults() { + $query = id(new PhabricatorDashboardPanelQuery()); $panels = $this->executeQuery($query); + $results = array(); foreach ($panels as $panel) { $impl = $panel->getImplementation(); if ($impl) { $type_text = $impl->getPanelTypeName(); } else { $type_text = nonempty($panel->getPanelType(), pht('Unknown Type')); } + $id = $panel->getID(); + $monogram = $panel->getMonogram(); + $properties = $panel->getProperties(); $result = id(new PhabricatorTypeaheadResult()) ->setName($panel->getName()) - ->setPHID($panel->getPHID()) + ->setDisplayName($monogram.' '.$panel->getName()) + ->setPHID($id) + ->setIcon($impl->getIcon()) ->addAttribute($type_text); + if (!empty($properties['class'])) { + $result->addAttribute($properties['class']); + } + if ($panel->getIsArchived()) { $result->setClosed(pht('Archived')); } - $results[] = $result; + $results[$id] = $result; } - return $this->filterResultsAgainstTokens($results); + return $results; } }