diff --git a/resources/sql/patches/20130508.search_namedquery.sql b/resources/sql/patches/20130508.search_namedquery.sql new file mode 100644 index 0000000000..b07f8a4b44 --- /dev/null +++ b/resources/sql/patches/20130508.search_namedquery.sql @@ -0,0 +1,12 @@ +CREATE TABLE {$NAMESPACE}_search.search_namedquery ( + id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, + userPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, + engineClassName VARCHAR(128) NOT NULL COLLATE utf8_bin, + queryName VARCHAR(255) NOT NULL COLLATE utf8_bin, + queryKey VARCHAR(12) NOT NULL COLLATE utf8_bin, + dateCreated INT(10) UNSIGNED NOT NULL, + dateModified INT(10) UNSIGNED NOT NULL, + UNIQUE KEY `key_userquery` (userPHID, engineClassName, queryKey), + PRIMARY KEY(id) +) +ENGINE=InnoDB, COLLATE utf8_general_ci diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index e7151b68eb..b8af5ff5ec 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1115,6 +1115,7 @@ phutil_register_library_map(array( 'PhabricatorMustVerifyEmailController' => 'applications/auth/controller/PhabricatorMustVerifyEmailController.php', 'PhabricatorMySQLConfigOptions' => 'applications/config/option/PhabricatorMySQLConfigOptions.php', 'PhabricatorMySQLFileStorageEngine' => 'applications/files/engine/PhabricatorMySQLFileStorageEngine.php', + 'PhabricatorNamedQuery' => 'applications/search/storage/PhabricatorNamedQuery.php', 'PhabricatorNoteExample' => 'applications/uiexample/examples/PhabricatorNoteExample.php', 'PhabricatorNotificationBuilder' => 'applications/notification/builder/PhabricatorNotificationBuilder.php', 'PhabricatorNotificationClearController' => 'applications/notification/controller/PhabricatorNotificationClearController.php', @@ -1198,6 +1199,7 @@ phutil_register_library_map(array( 'PhabricatorPasteDAO' => 'applications/paste/storage/PhabricatorPasteDAO.php', 'PhabricatorPasteEditController' => 'applications/paste/controller/PhabricatorPasteEditController.php', 'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php', + 'PhabricatorPasteQueriesController' => 'applications/paste/controller/PhabricatorPasteQueriesController.php', 'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php', 'PhabricatorPasteRemarkupRule' => 'applications/paste/remarkup/PhabricatorPasteRemarkupRule.php', 'PhabricatorPasteSearchEngine' => 'applications/paste/query/PhabricatorPasteSearchEngine.php', @@ -1331,6 +1333,7 @@ phutil_register_library_map(array( 'PhabricatorSearchIndexer' => 'applications/search/index/PhabricatorSearchIndexer.php', 'PhabricatorSearchManagementIndexWorkflow' => 'applications/search/management/PhabricatorSearchManagementIndexWorkflow.php', 'PhabricatorSearchManagementWorkflow' => 'applications/search/management/PhabricatorSearchManagementWorkflow.php', + 'PhabricatorSearchNameController' => 'applications/search/controller/PhabricatorSearchNameController.php', 'PhabricatorSearchQuery' => 'applications/search/storage/PhabricatorSearchQuery.php', 'PhabricatorSearchRelationship' => 'applications/search/constants/PhabricatorSearchRelationship.php', 'PhabricatorSearchResultView' => 'applications/search/view/PhabricatorSearchResultView.php', @@ -2828,6 +2831,7 @@ phutil_register_library_map(array( 'PhabricatorMustVerifyEmailController' => 'PhabricatorAuthController', 'PhabricatorMySQLConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorMySQLFileStorageEngine' => 'PhabricatorFileStorageEngine', + 'PhabricatorNamedQuery' => 'PhabricatorSearchDAO', 'PhabricatorNoteExample' => 'PhabricatorUIExample', 'PhabricatorNotificationClearController' => 'PhabricatorNotificationController', 'PhabricatorNotificationConfigOptions' => 'PhabricatorApplicationConfigOptions', @@ -2908,6 +2912,7 @@ phutil_register_library_map(array( 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', 'PhabricatorPasteEditController' => 'PhabricatorPasteController', 'PhabricatorPasteListController' => 'PhabricatorPasteController', + 'PhabricatorPasteQueriesController' => 'PhabricatorPasteController', 'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorPasteRemarkupRule' => 'PhabricatorRemarkupRuleObject', 'PhabricatorPasteSearchEngine' => 'PhabricatorApplicationSearchEngine', @@ -3038,6 +3043,7 @@ phutil_register_library_map(array( 'PhabricatorSearchHovercardController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchManagementIndexWorkflow' => 'PhabricatorSearchManagementWorkflow', 'PhabricatorSearchManagementWorkflow' => 'PhutilArgumentWorkflow', + 'PhabricatorSearchNameController' => 'PhabricatorSearchBaseController', 'PhabricatorSearchQuery' => 'PhabricatorSearchDAO', 'PhabricatorSearchResultView' => 'AphrontView', 'PhabricatorSearchSelectController' => 'PhabricatorSearchBaseController', @@ -3452,6 +3458,7 @@ phutil_register_library_map(array( 'ReleephBranchEditor' => 'PhabricatorEditor', 'ReleephBranchNamePreviewController' => 'PhabricatorController', 'ReleephBranchPreviewView' => 'AphrontFormControl', + 'ReleephBranchTemplate' => 'PhabricatorMarkupInterface', 'ReleephBranchViewController' => 'ReleephController', 'ReleephCommitFinderException' => 'Exception', 'ReleephCommitMessageFieldSpecification' => 'ReleephFieldSpecification', diff --git a/src/applications/paste/application/PhabricatorApplicationPaste.php b/src/applications/paste/application/PhabricatorApplicationPaste.php index 2af2cf275a..92cd6bb275 100644 --- a/src/applications/paste/application/PhabricatorApplicationPaste.php +++ b/src/applications/paste/application/PhabricatorApplicationPaste.php @@ -37,6 +37,7 @@ final class PhabricatorApplicationPaste extends PhabricatorApplication { 'edit/(?P[1-9]\d*)/' => 'PhabricatorPasteEditController', 'filter/(?P\w+)/' => 'PhabricatorPasteListController', 'query/(?P\w+)/'=> 'PhabricatorPasteListController', + 'savedqueries/' => 'PhabricatorPasteQueriesController', ), ); } diff --git a/src/applications/paste/controller/PhabricatorPasteController.php b/src/applications/paste/controller/PhabricatorPasteController.php index 0797e27e06..7581c5f39d 100644 --- a/src/applications/paste/controller/PhabricatorPasteController.php +++ b/src/applications/paste/controller/PhabricatorPasteController.php @@ -18,7 +18,11 @@ abstract class PhabricatorPasteController extends PhabricatorController { if ($user->isLoggedIn()) { $nav->addFilter('my', pht('My Pastes')); } + + $nav->addLabel(pht('Search')); $nav->addFilter('advanced', pht('Advanced Search')); + $nav->addFilter('', pht('Saved Queries'), + $this->getApplicationURI('/savedqueries/')); $nav->selectFilter($filter, 'all'); diff --git a/src/applications/paste/controller/PhabricatorPasteQueriesController.php b/src/applications/paste/controller/PhabricatorPasteQueriesController.php new file mode 100644 index 0000000000..300caa0ca3 --- /dev/null +++ b/src/applications/paste/controller/PhabricatorPasteQueriesController.php @@ -0,0 +1,64 @@ +getRequest(); + $user = $request->getUser(); + + $nav = $this->buildSideNavView(""); + $filter = $nav->getSelectedFilter(); + + $table = new PhabricatorNamedQuery(); + $conn = $table->establishConnection('r'); + $data = queryfx_all( + $conn, + 'SELECT * FROM %T WHERE userPHID=%s AND engineClassName=%s', + $table->getTableName(), + $user->getPHID(), + 'PhabricatorPasteSearchEngine'); + + $list = new PhabricatorObjectItemListView(); + $list->setUser($user); + + foreach ($data as $key => $saved_query) { + $date_created = phabricator_datetime($saved_query["dateCreated"], $user); + $item = id(new PhabricatorObjectItemView()) + ->setHeader($saved_query["queryName"]) + ->setHref('/paste/query/'.$saved_query["queryKey"].'/') + ->addByline(pht('Date Created: ').$date_created); + $list->addItem($item); + } + + $pager = new AphrontCursorPagerView(); + $pager->readFromRequest($request); + + $list->setPager($pager); + + $list->setNoDataString(pht("No results found for this query.")); + + $nav->appendChild( + array( + $list, + )); + + $crumbs = $this + ->buildApplicationCrumbs($nav) + ->addCrumb( + id(new PhabricatorCrumbView()) + ->setName(pht("Saved Queries")) + ->setHref($this->getApplicationURI('/savedqueries/'))); + + $nav->setCrumbs($crumbs); + + return $this->buildApplicationPage( + $nav, + array( + 'title' => pht("Saved Queries"), + 'device' => true, + 'dust' => true, + )); + } + +} diff --git a/src/applications/paste/query/PhabricatorPasteSearchEngine.php b/src/applications/paste/query/PhabricatorPasteSearchEngine.php index ea92359d34..d342d666f3 100644 --- a/src/applications/paste/query/PhabricatorPasteSearchEngine.php +++ b/src/applications/paste/query/PhabricatorPasteSearchEngine.php @@ -83,7 +83,10 @@ final class PhabricatorPasteSearchEngine $form->appendChild( id(new AphrontFormSubmitControl()) - ->setValue(pht('Filter Pastes'))); + ->setValue(pht('Filter Pastes')) + ->addCancelButton( + '/search/name/'.$saved_query->getQueryKey().'/', + pht('Save Custom Query...'))); return $form; } diff --git a/src/applications/search/application/PhabricatorApplicationSearch.php b/src/applications/search/application/PhabricatorApplicationSearch.php index 06bf280e3a..e0e0ef4951 100644 --- a/src/applications/search/application/PhabricatorApplicationSearch.php +++ b/src/applications/search/application/PhabricatorApplicationSearch.php @@ -34,7 +34,8 @@ final class PhabricatorApplicationSearch extends PhabricatorApplication { 'index/(?P[^/]+)/' => 'PhabricatorSearchIndexController', 'hovercard/(?Pretrieve|test)/' => 'PhabricatorSearchHovercardController', - ), + 'name/(?P\w+)/' => 'PhabricatorSearchNameController', + ), ); } diff --git a/src/applications/search/controller/PhabricatorSearchNameController.php b/src/applications/search/controller/PhabricatorSearchNameController.php new file mode 100644 index 0000000000..d529d21db4 --- /dev/null +++ b/src/applications/search/controller/PhabricatorSearchNameController.php @@ -0,0 +1,71 @@ +queryKey = idx($data, 'queryKey'); + } + + public function processRequest() { + $request = $this->getRequest(); + $user = $request->getUser(); + + if ($this->queryKey) { + $saved_query = id(new PhabricatorSavedQuery())->loadOneWhere( + 'queryKey = %s', + $this->queryKey); + if (!$saved_query) { + return new Aphront404Response(); + } + } else { + return new Aphront404Response(); + } + + if ($request->isFormPost()) { + $request_data = $request->getRequestData(); + + $named_query = id(new PhabricatorNamedQuery()) + ->setUserPHID($user->getPHID()) + ->setQueryKey($saved_query->getQueryKey()) + ->setQueryName($request_data["set_name"]) + ->setEngineClassName($saved_query->getEngineClassName()); + + try { + $named_query->save(); + } catch (AphrontQueryDuplicateKeyException $ex) { + // Ignore, the user is naming an identical query. + } + + return id(new AphrontRedirectResponse()) + ->setURI('/search/'); + } + + $form = id(new AphrontFormView()) + ->setUser($user); + + $form->appendChild( + id(new AphrontFormTextControl()) + ->setName('set_name') + ->setLabel(pht('Query Name'))); + + $form->appendChild( + id(new AphrontFormSubmitControl()) + ->setValue(pht('Save'))); + + return $this->buildStandardPageResponse( + array( + $form, + ), + array( + 'title' => 'Name Query', + )); + } + + +} diff --git a/src/applications/search/storage/PhabricatorNamedQuery.php b/src/applications/search/storage/PhabricatorNamedQuery.php new file mode 100644 index 0000000000..5f97187e50 --- /dev/null +++ b/src/applications/search/storage/PhabricatorNamedQuery.php @@ -0,0 +1,13 @@ + 'php', 'name' => $this->getPatchPath('20130507.releephrqmailkeypop.php'), ), + '20130508.search_namedquery.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20130508.search_namedquery.sql'), + ), ); } }