diff --git a/resources/sql/patches/104.searchkey.sql b/resources/sql/patches/104.searchkey.sql new file mode 100644 index 0000000000..6c1961489f --- /dev/null +++ b/resources/sql/patches/104.searchkey.sql @@ -0,0 +1,12 @@ +ALTER TABLE phabricator_search.search_query + DROP authorPHID; + +ALTER TABLE phabricator_search.search_query + ADD queryKey VARCHAR(12) NOT NULL; + +/* Preserve URIs for old queries in case anyone has them bookmarked. */ +UPDATE phabricator_search.search_query + SET queryKey = id; + +ALTER TABLE phabricator_search.search_query + ADD UNIQUE KEY (queryKey); diff --git a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php index e39bfb4476..c4154b4192 100644 --- a/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php +++ b/src/aphront/default/configuration/AphrontDefaultApplicationConfiguration.php @@ -1,605 +1,605 @@ <?php /* * Copyright 2012 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @group aphront */ class AphrontDefaultApplicationConfiguration extends AphrontApplicationConfiguration { public function __construct() { } public function getApplicationName() { return 'aphront-default'; } public function getURIMap() { return $this->getResourceURIMapRules() + array( '/' => array( '$' => 'PhabricatorDirectoryMainController', ), '/directory/' => array( 'item/$' => 'PhabricatorDirectoryItemListController', 'item/edit/(?:(?P<id>\d+)/)?$' => 'PhabricatorDirectoryItemEditController', 'item/delete/(?P<id>\d+)/' => 'PhabricatorDirectoryItemDeleteController', 'category/$' => 'PhabricatorDirectoryCategoryListController', 'category/edit/(?:(?P<id>\d+)/)?$' => 'PhabricatorDirectoryCategoryEditController', 'category/delete/(?P<id>\d+)/' => 'PhabricatorDirectoryCategoryDeleteController', ), '/file/' => array( '$' => 'PhabricatorFileListController', 'filter/(?P<filter>\w+)/$' => 'PhabricatorFileListController', 'upload/$' => 'PhabricatorFileUploadController', 'dropupload/$' => 'PhabricatorFileDropUploadController', 'delete/(?P<id>\d+)/$' => 'PhabricatorFileDeleteController', '(?P<view>info)/(?P<phid>[^/]+)/' => 'PhabricatorFileViewController', '(?P<view>view)/(?P<phid>[^/]+)/' => 'PhabricatorFileViewController', '(?P<view>download)/(?P<phid>[^/]+)/' => 'PhabricatorFileViewController', 'alt/(?P<key>[^/]+)/(?P<phid>[^/]+)/' => 'PhabricatorFileAltViewController', 'macro/' => array( '$' => 'PhabricatorFileMacroListController', 'edit/(?:(?P<id>\d+)/)?$' => 'PhabricatorFileMacroEditController', 'delete/(?P<id>\d+)/$' => 'PhabricatorFileMacroDeleteController', ), 'proxy/$' => 'PhabricatorFileProxyController', 'xform/(?P<transform>[^/]+)/(?P<phid>[^/]+)/' => 'PhabricatorFileTransformController', ), '/phid/' => array( '$' => 'PhabricatorPHIDLookupController', ), '/people/' => array( '$' => 'PhabricatorPeopleListController', 'logs/$' => 'PhabricatorPeopleLogsController', 'edit/(?:(?P<id>\d+)/(?:(?P<view>\w+)/)?)?$' => 'PhabricatorPeopleEditController', ), '/p/(?P<username>\w+)/(?:(?P<page>\w+)/)?$' => 'PhabricatorPeopleProfileController', '/conduit/' => array( '$' => 'PhabricatorConduitConsoleController', 'method/(?P<method>[^/]+)/$' => 'PhabricatorConduitConsoleController', 'log/$' => 'PhabricatorConduitLogController', 'log/view/(?P<view>[^/]+)/$' => 'PhabricatorConduitLogController', 'token/$' => 'PhabricatorConduitTokenController', ), '/api/(?P<method>[^/]+)$' => 'PhabricatorConduitAPIController', '/D(?P<id>\d+)' => 'DifferentialRevisionViewController', '/differential/' => array( '$' => 'DifferentialRevisionListController', 'filter/(?P<filter>\w+)/$' => 'DifferentialRevisionListController', 'diff/' => array( '(?P<id>\d+)/$' => 'DifferentialDiffViewController', 'create/$' => 'DifferentialDiffCreateController', ), 'changeset/$' => 'DifferentialChangesetViewController', 'revision/edit/(?:(?P<id>\d+)/)?$' => 'DifferentialRevisionEditController', 'comment/' => array( 'preview/(?P<id>\d+)/$' => 'DifferentialCommentPreviewController', 'save/$' => 'DifferentialCommentSaveController', 'inline/' => array( 'preview/(?P<id>\d+)/$' => 'DifferentialInlineCommentPreviewController', 'edit/(?P<id>\d+)/$' => 'DifferentialInlineCommentEditController', ), ), 'subscribe/(?P<action>add|rem)/(?P<id>\d+)/$' => 'DifferentialSubscribeController', ), '/typeahead/' => array( 'common/(?P<type>\w+)/$' => 'PhabricatorTypeaheadCommonDatasourceController', ), '/mail/' => array( '$' => 'PhabricatorMetaMTAListController', 'send/$' => 'PhabricatorMetaMTASendController', 'view/(?P<id>\d+)/$' => 'PhabricatorMetaMTAViewController', 'lists/$' => 'PhabricatorMetaMTAMailingListsController', 'lists/edit/(?:(?P<id>\d+)/)?$' => 'PhabricatorMetaMTAMailingListEditController', 'receive/$' => 'PhabricatorMetaMTAReceiveController', 'received/$' => 'PhabricatorMetaMTAReceivedListController', 'sendgrid/$' => 'PhabricatorMetaMTASendGridReceiveController', ), '/login/' => array( '$' => 'PhabricatorLoginController', 'email/$' => 'PhabricatorEmailLoginController', 'etoken/(?P<token>\w+)/$' => 'PhabricatorEmailTokenController', 'refresh/$' => 'PhabricatorRefreshCSRFController', 'validate/$' => 'PhabricatorLoginValidateController', ), '/logout/$' => 'PhabricatorLogoutController', '/oauth/' => array( '(?P<provider>\w+)/' => array( 'login/$' => 'PhabricatorOAuthLoginController', 'diagnose/$' => 'PhabricatorOAuthDiagnosticsController', 'unlink/$' => 'PhabricatorOAuthUnlinkController', ), ), '/xhprof/' => array( 'profile/(?P<phid>[^/]+)/$' => 'PhabricatorXHProfProfileController', ), '/~/' => 'DarkConsoleController', '/settings/' => array( '(?:page/(?P<page>[^/]+)/)?$' => 'PhabricatorUserSettingsController', ), '/maniphest/' => array( '$' => 'ManiphestTaskListController', 'view/(?P<view>\w+)/$' => 'ManiphestTaskListController', 'task/' => array( 'create/$' => 'ManiphestTaskEditController', 'edit/(?P<id>\d+)/$' => 'ManiphestTaskEditController', 'descriptionchange/(?P<id>\d+)/$' => 'ManiphestTaskDescriptionChangeController', 'descriptiondiff/$' => 'ManiphestTaskDescriptionDiffController', 'descriptionpreview/$' => 'ManiphestTaskDescriptionPreviewController', ), 'transaction/' => array( 'save/' => 'ManiphestTransactionSaveController', 'preview/(?P<id>\d+)/$' => 'ManiphestTransactionPreviewController', ), ), '/T(?P<id>\d+)$' => 'ManiphestTaskDetailController', '/repository/' => array( '$' => 'PhabricatorRepositoryListController', 'create/$' => 'PhabricatorRepositoryCreateController', 'edit/(?P<id>\d+)/(?:(?P<view>\w+)?/)?$' => 'PhabricatorRepositoryEditController', 'delete/(?P<id>\d+)/$' => 'PhabricatorRepositoryDeleteController', 'project/(?P<id>\d+)/' => 'PhabricatorRepositoryArcanistProjectEditController', ), '/search/' => array( '$' => 'PhabricatorSearchController', - '(?P<id>\d+)/$' => 'PhabricatorSearchController', + '(?P<key>[^/]+)/$' => 'PhabricatorSearchController', 'attach/(?P<phid>[^/]+)/(?P<type>\w+)/(?:(?P<action>\w+)/)?$' => 'PhabricatorSearchAttachController', 'select/(?P<type>\w+)/$' => 'PhabricatorSearchSelectController', 'index/(?P<phid>[^/]+)/$' => 'PhabricatorSearchIndexController', ), '/project/' => array( '$' => 'PhabricatorProjectListController', 'filter/(?P<filter>[^/]+)/$' => 'PhabricatorProjectListController', 'edit/(?P<id>\d+)/$' => 'PhabricatorProjectProfileEditController', 'view/(?P<id>\d+)/(?:(?P<page>\w+)/)?$' => 'PhabricatorProjectProfileController', 'affiliation/(?P<id>\d+)/$' => 'PhabricatorProjectAffiliationEditController', 'create/$' => 'PhabricatorProjectCreateController', 'update/(?P<id>\d+)/(?P<action>[^/]+)/$' => 'PhabricatorProjectUpdateController', ), '/r(?P<callsign>[A-Z]+)(?P<commit>[a-z0-9]+)$' => 'DiffusionCommitController', '/diffusion/' => array( '$' => 'DiffusionHomeController', '(?P<callsign>[A-Z]+)/' => array( '$' => 'DiffusionRepositoryController', 'repository/'. '(?P<path>[^/]+)/'. '$' => 'DiffusionRepositoryController', 'change/'. '(?P<path>.*?)'. '(?:[;](?P<commit>[a-z0-9]+))?'. '$' => 'DiffusionChangeController', 'history/'. '(?P<path>.*?)'. '(?:[;](?P<commit>[a-z0-9]+))?'. '$' => 'DiffusionHistoryController', 'browse/'. '(?P<path>.*?)'. '(?:[;](?P<commit>[a-z0-9]+))?'. '(?:[$](?P<line>\d+))?'. '$' => 'DiffusionBrowseController', 'diff/'. '(?P<path>.*?)'. '(?:[;](?P<commit>[a-z0-9]+))?'. '$' => 'DiffusionDiffController', 'lastmodified/'. '(?P<path>.*?)'. '(?:[;](?P<commit>[a-z0-9]+))?'. '$' => 'DiffusionLastModifiedController', ), 'services/' => array( 'path/' => array( 'complete/$' => 'DiffusionPathCompleteController', 'validate/$' => 'DiffusionPathValidateController', ), ), 'author/' => array( '$' => 'DiffusionCommitListController', '(?P<username>\w+)/$' => 'DiffusionCommitListController', ), 'symbol/(?P<name>[^/]+)/$' => 'DiffusionSymbolController', ), '/daemon/' => array( 'task/(?P<id>\d+)/$' => 'PhabricatorWorkerTaskDetailController', 'task/(?P<id>\d+)/(?P<action>[^/]+)/$' => 'PhabricatorWorkerTaskUpdateController', 'log/' => array( '$' => 'PhabricatorDaemonLogListController', 'combined/$' => 'PhabricatorDaemonCombinedLogController', '(?P<id>\d+)/$' => 'PhabricatorDaemonLogViewController', ), 'timeline/$' => 'PhabricatorDaemonTimelineConsoleController', 'timeline/(?P<id>\d+)/$' => 'PhabricatorDaemonTimelineEventController', '$' => 'PhabricatorDaemonConsoleController', ), '/herald/' => array( '$' => 'HeraldHomeController', 'view/(?P<view>[^/]+)/' => array( '$' => 'HeraldHomeController', '(?P<global>global)/$' => 'HeraldHomeController' ), 'new/(?:(?P<type>[^/]+)/)?$' => 'HeraldNewController', 'rule/(?:(?P<id>\d+)/)?$' => 'HeraldRuleController', 'history/(?P<id>\d+)/$' => 'HeraldRuleEditHistoryController', 'delete/(?P<id>\d+)/$' => 'HeraldDeleteController', 'test/$' => 'HeraldTestConsoleController', 'all/' => array( '$' => 'HeraldAllRulesController', 'view/(?P<view>[^/]+)/$' => 'HeraldAllRulesController', ), 'transcript/$' => 'HeraldTranscriptListController', 'transcript/(?P<id>\d+)/(?:(?P<filter>\w+)/)?$' => 'HeraldTranscriptController', ), '/uiexample/' => array( '$' => 'PhabricatorUIExampleRenderController', 'view/(?P<class>[^/]+)/$' => 'PhabricatorUIExampleRenderController', ), '/owners/' => array( '$' => 'PhabricatorOwnersListController', 'view/(?P<view>[^/]+)/$' => 'PhabricatorOwnersListController', 'edit/(?P<id>\d+)/$' => 'PhabricatorOwnersEditController', 'new/$' => 'PhabricatorOwnersEditController', 'package/(?P<id>\d+)/$' => 'PhabricatorOwnersDetailController', 'delete/(?P<id>\d+)/$' => 'PhabricatorOwnersDeleteController', 'related/' => array( '$' => 'PhabricatorOwnerRelatedListController', 'view/(?P<view>[^/]+)/$' => 'PhabricatorOwnerRelatedListController', ), ), '/audit/' => array( '$' => 'PhabricatorAuditEditController', 'edit/$' => 'PhabricatorAuditEditController', ), '/xhpast/' => array( '$' => 'PhabricatorXHPASTViewRunController', 'view/(?P<id>\d+)/$' => 'PhabricatorXHPASTViewFrameController', 'frameset/(?P<id>\d+)/$' => 'PhabricatorXHPASTViewFramesetController', 'input/(?P<id>\d+)/$' => 'PhabricatorXHPASTViewInputController', 'tree/(?P<id>\d+)/$' => 'PhabricatorXHPASTViewTreeController', 'stream/(?P<id>\d+)/$' => 'PhabricatorXHPASTViewStreamController', ), '/status/$' => 'PhabricatorStatusController', '/paste/' => array( '$' => 'PhabricatorPasteListController', 'filter/(?P<filter>\w+)/$' => 'PhabricatorPasteListController', ), '/P(?P<id>\d+)$' => 'PhabricatorPasteViewController', '/help/' => array( 'keyboardshortcut/$' => 'PhabricatorHelpKeyboardShortcutController', ), '/countdown/' => array( '$' => 'PhabricatorCountdownListController', '(?P<id>\d+)/$' => 'PhabricatorCountdownViewController', 'edit/(?:(?P<id>\d+)/)?$' => 'PhabricatorCountdownEditController', 'delete/(?P<id>\d+)/$' => 'PhabricatorCountdownDeleteController' ), '/feed/' => array( '$' => 'PhabricatorFeedStreamController', 'public/$' => 'PhabricatorFeedPublicStreamController', ), '/V(?P<id>\d+)$' => 'PhabricatorSlowvotePollController', '/vote/' => array( '(?:view/(?P<view>\w+)/)?$' => 'PhabricatorSlowvoteListController', 'create/' => 'PhabricatorSlowvoteCreateController', ), // Match "/w/" with slug "/". '/w(?P<slug>/)$' => 'PhrictionDocumentController', // Match "/w/x/y/z/" with slug "x/y/z/". '/w/(?P<slug>.+/)$' => 'PhrictionDocumentController', '/phriction/' => array( '$' => 'PhrictionListController', 'list/(?P<view>[^/]+)/$' => 'PhrictionListController', 'history(?P<slug>/)$' => 'PhrictionHistoryController', 'history/(?P<slug>.+/)$' => 'PhrictionHistoryController', 'edit/(?:(?P<id>\d+)/)?$' => 'PhrictionEditController', 'delete/(?P<id>\d+)/$' => 'PhrictionDeleteController', 'preview/$' => 'PhrictionDocumentPreviewController', 'diff/(?P<id>\d+)/$' => 'PhrictionDiffController', ), '/calendar/' => array( '$' => 'PhabricatorCalendarBrowseController', ), '/drydock/' => array( '$' => 'DrydockResourceListController', 'resource/$' => 'DrydockResourceListController', 'resource/allocate/$' => 'DrydockResourceAllocateController', 'host/' => array( '$' => 'DrydockHostListController', 'edit/$' => 'DrydockHostEditController', 'edit/(?P<id>\d+)/$' => 'DrydockhostEditController', ), 'lease/$' => 'DrydockLeaseListController', ), ); } protected function getResourceURIMapRules() { return array( '/res/' => array( '(?P<package>pkg/)?(?P<hash>[a-f0-9]{8})/(?P<path>.+\.(?:css|js))$' => 'CelerityResourceController', ), ); } public function buildRequest() { $request = new AphrontRequest($this->getHost(), $this->getPath()); $request->setRequestData($_GET + $_POST); $request->setApplicationConfiguration($this); return $request; } public function handleException(Exception $ex) { // Always log the unhandled exception. phlog($ex); $class = phutil_escape_html(get_class($ex)); $message = phutil_escape_html($ex->getMessage()); if (PhabricatorEnv::getEnvConfig('phabricator.show-stack-traces')) { $trace = $this->renderStackTrace($ex->getTrace()); } else { $trace = null; } $content = '<div class="aphront-unhandled-exception">'. '<div class="exception-message">'.$message.'</div>'. $trace. '</div>'; $user = $this->getRequest()->getUser(); if (!$user) { // If we hit an exception very early, we won't have a user. $user = new PhabricatorUser(); } $dialog = new AphrontDialogView(); $dialog ->setTitle('Unhandled Exception ("'.$class.'")') ->setClass('aphront-exception-dialog') ->setUser($user) ->appendChild($content); if ($this->getRequest()->isAjax()) { $dialog->addCancelButton('/', 'Close'); } $response = new AphrontDialogResponse(); $response->setDialog($dialog); return $response; } public function willSendResponse(AphrontResponse $response) { $request = $this->getRequest(); $response->setRequest($request); if ($response instanceof AphrontDialogResponse) { if (!$request->isAjax()) { $view = new PhabricatorStandardPageView(); $view->setRequest($request); $view->appendChild( '<div style="padding: 2em 0;">'. $response->buildResponseString(). '</div>'); $response = new AphrontWebpageResponse(); $response->setContent($view->render()); return $response; } else { return id(new AphrontAjaxResponse()) ->setContent(array( 'dialog' => $response->buildResponseString(), )); } } else if ($response instanceof AphrontRedirectResponse) { if ($request->isAjax()) { return id(new AphrontAjaxResponse()) ->setContent( array( 'redirect' => $response->getURI(), )); } } return $response; } public function build404Controller() { return array(new Phabricator404Controller($this->getRequest()), array()); } public function buildRedirectController($uri) { return array( new PhabricatorRedirectController($this->getRequest()), array( 'uri' => $uri, )); } private function renderStackTrace($trace) { $libraries = PhutilBootloader::getInstance()->getAllLibraries(); // TODO: Make this configurable? $host = 'https://secure.phabricator.com'; $browse = array( 'arcanist' => $host.'/diffusion/ARC/browse/origin:master/src/', 'phutil' => $host.'/diffusion/PHU/browse/origin:master/src/', 'phabricator' => $host.'/diffusion/P/browse/origin:master/src/', ); $rows = array(); $depth = count($trace); foreach ($trace as $part) { $lib = null; $file = idx($part, 'file'); $relative = $file; foreach ($libraries as $library) { $root = phutil_get_library_root($library); if (Filesystem::isDescendant($file, $root)) { $lib = $library; $relative = Filesystem::readablePath($file, $root); break; } } $where = ''; if (isset($part['class'])) { $where .= $part['class'].'::'; } if (isset($part['function'])) { $where .= $part['function'].'()'; } if ($file) { if (isset($browse[$lib])) { $file_name = phutil_render_tag( 'a', array( 'href' => $browse[$lib].$relative.'$'.$part['line'], 'title' => $file, 'target' => '_blank', ), phutil_escape_html($relative)); } else { $file_name = phutil_render_tag( 'span', array( 'title' => $file, ), phutil_escape_html($relative)); } $file_name = $file_name.' : '.(int)$part['line']; } else { $file_name = '<em>(Internal)</em>'; } $rows[] = array( $depth--, phutil_escape_html($lib), $file_name, phutil_escape_html($where), ); } $table = new AphrontTableView($rows); $table->setHeaders( array( 'Depth', 'Library', 'File', 'Where', )); $table->setColumnClasses( array( 'n', '', '', 'wide', )); return '<div class="exception-trace">'. '<div class="exception-trace-header">Stack Trace</div>'. $table->render(). '</div>'; } } diff --git a/src/applications/search/controller/search/PhabricatorSearchController.php b/src/applications/search/controller/search/PhabricatorSearchController.php index 6fcd15496f..e33023d703 100644 --- a/src/applications/search/controller/search/PhabricatorSearchController.php +++ b/src/applications/search/controller/search/PhabricatorSearchController.php @@ -1,221 +1,223 @@ <?php /* - * Copyright 2011 Facebook, Inc. + * Copyright 2012 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @group search */ class PhabricatorSearchController extends PhabricatorSearchBaseController { - private $id; + private $key; public function willProcessRequest(array $data) { - $this->id = idx($data, 'id'); + $this->key = idx($data, 'key'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); - if ($this->id) { - $query = id(new PhabricatorSearchQuery())->load($this->id); + if ($this->key) { + $query = id(new PhabricatorSearchQuery())->loadOneWhere( + 'queryKey = %s', + $this->key); if (!$query) { return new Aphront404Response(); } } else { $query = new PhabricatorSearchQuery(); if ($request->isFormPost()) { $query->setQuery($request->getStr('query')); if (strlen($request->getStr('type'))) { $query->setParameter('type', $request->getStr('type')); } if ($request->getArr('author')) { $query->setParameter('author', $request->getArr('author')); } if ($request->getArr('owner')) { $query->setParameter('owner', $request->getArr('owner')); } if ($request->getInt('open')) { $query->setParameter('open', $request->getInt('open')); } if ($request->getArr('project')) { $query->setParameter('project', $request->getArr('project')); } $query->save(); return id(new AphrontRedirectResponse()) - ->setURI('/search/'.$query->getID().'/'); + ->setURI('/search/'.$query->getQueryKey().'/'); } } $more = PhabricatorEnv::getEnvConfig('search.more-document-types', array()); $options = array( '' => 'All Documents', PhabricatorPHIDConstants::PHID_TYPE_DREV => 'Differential Revisions', PhabricatorPHIDConstants::PHID_TYPE_CMIT => 'Repository Commits', PhabricatorPHIDConstants::PHID_TYPE_TASK => 'Maniphest Tasks', PhabricatorPHIDConstants::PHID_TYPE_WIKI => 'Phriction Documents', PhabricatorPHIDConstants::PHID_TYPE_USER => 'Phabricator Users', ) + $more; $status_options = array( 0 => 'Open and Closed Documents', 1 => 'Open Documents', ); $phids = array_merge( $query->getParameter('author', array()), $query->getParameter('owner', array()), $query->getParameter('project', array()) ); $handles = id(new PhabricatorObjectHandleData($phids)) ->loadHandles(); $author_value = array_select_keys( $handles, $query->getParameter('author', array())); $author_value = mpull($author_value, 'getFullName', 'getPHID'); $owner_value = array_select_keys( $handles, $query->getParameter('owner', array())); $owner_value = mpull($owner_value, 'getFullName', 'getPHID'); $project_value = array_select_keys( $handles, $query->getParameter('project', array())); $project_value = mpull($project_value, 'getFullName', 'getPHID'); $search_form = new AphrontFormView(); $search_form ->setUser($user) ->setAction('/search/') ->appendChild( id(new AphrontFormTextControl()) ->setLabel('Search') ->setName('query') ->setValue($query->getQuery())) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Document Type') ->setName('type') ->setOptions($options) ->setValue($query->getParameter('type'))) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel('Document Status') ->setName('open') ->setOptions($status_options) ->setValue($query->getParameter('open'))) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('author') ->setLabel('Author') ->setDatasource('/typeahead/common/users/') ->setValue($author_value)) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('owner') ->setLabel('Owner') ->setDatasource('/typeahead/common/searchowner/') ->setValue($owner_value) ->setCaption( 'Tip: search for "Up For Grabs" to find unowned documents.')) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('project') ->setLabel('Project') ->setDatasource('/typeahead/common/projects/') ->setValue($project_value)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Search')); $search_panel = new AphrontPanelView(); $search_panel->setHeader('Search Phabricator'); $search_panel->appendChild($search_form); require_celerity_resource('phabricator-search-results-css'); if ($query->getID()) { $limit = 20; $pager = new AphrontPagerView(); $pager->setURI($request->getRequestURI(), 'page'); $pager->setPageSize($limit); $pager->setOffset($request->getInt('page')); $query->setParameter('limit', $limit + 1); $query->setParameter('offset', $pager->getOffset()); $engine = PhabricatorSearchEngineSelector::newSelector()->newEngine(); $results = $engine->executeSearch($query); $results = ipull($results, 'phid'); $results = $pager->sliceResults($results); if ($results) { $loader = new PhabricatorObjectHandleData($results); $handles = $loader->loadHandles(); $objects = $loader->loadObjects(); $results = array(); foreach ($handles as $phid => $handle) { $view = new PhabricatorSearchResultView(); $view->setHandle($handle); $view->setQuery($query); $view->setObject($objects[$phid]); $results[] = $view->render(); } $results = '<div class="phabricator-search-result-list">'. implode("\n", $results). '<div class="search-results-pager">'. $pager->render(). '</div>'. '</div>'; } else { $results = '<div class="phabricator-search-result-list">'. '<p class="phabricator-search-no-results">No search results.</p>'. '</div>'; } } else { $results = null; } return $this->buildStandardPageResponse( array( $search_panel, $results, ), array( 'title' => 'Results: what', )); } } diff --git a/src/applications/search/storage/query/PhabricatorSearchQuery.php b/src/applications/search/storage/query/PhabricatorSearchQuery.php index 135750822d..e0666695b6 100644 --- a/src/applications/search/storage/query/PhabricatorSearchQuery.php +++ b/src/applications/search/storage/query/PhabricatorSearchQuery.php @@ -1,45 +1,52 @@ <?php /* - * Copyright 2011 Facebook, Inc. + * Copyright 2012 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * @group search */ -class PhabricatorSearchQuery extends PhabricatorSearchDAO { +final class PhabricatorSearchQuery extends PhabricatorSearchDAO { - protected $authorPHID; protected $query; protected $parameters = array(); + protected $queryKey; public function getConfiguration() { return array( self::CONFIG_SERIALIZATION => array( 'parameters' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } public function setParameter($parameter, $value) { $this->parameters[$parameter] = $value; return $this; } public function getParameter($parameter, $default = null) { return idx($this->parameters, $parameter, $default); } + public function save() { + if (!$this->getQueryKey()) { + $this->setQueryKey(Filesystem::readRandomCharacters(12)); + } + return parent::save(); + } + } diff --git a/src/applications/search/storage/query/__init__.php b/src/applications/search/storage/query/__init__.php index 9fee0d56db..d3c516d0a2 100644 --- a/src/applications/search/storage/query/__init__.php +++ b/src/applications/search/storage/query/__init__.php @@ -1,14 +1,15 @@ <?php /** * This file is automatically generated. Lint this module to rebuild it. * @generated */ phutil_require_module('phabricator', 'applications/search/storage/base'); +phutil_require_module('phutil', 'filesystem'); phutil_require_module('phutil', 'utils'); phutil_require_source('PhabricatorSearchQuery.php');