diff --git a/resources/sql/patches/20130530.macrodatekey.sql b/resources/sql/patches/20130530.macrodatekey.sql new file mode 100644 index 0000000000..f061ec34c1 --- /dev/null +++ b/resources/sql/patches/20130530.macrodatekey.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_file.file_imagemacro + ADD KEY `key_dateCreated` (dateCreated); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 197df72f46..514e1ec9bc 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -677,6 +677,7 @@ phutil_register_library_map(array( 'PHUIFeedStoryExample' => 'applications/uiexample/examples/PHUIFeedStoryExample.php', 'PHUIFeedStoryView' => 'view/phui/PHUIFeedStoryView.php', 'PHUIFormDividerControl' => 'view/form/control/PHUIFormDividerControl.php', + 'PHUIFormFreeformDateControl' => 'view/form/control/PHUIFormFreeformDateControl.php', 'PHUIFormMultiSubmitControl' => 'view/form/control/PHUIFormMultiSubmitControl.php', 'PHUIFormPageView' => 'view/form/PHUIFormPageView.php', 'PHUIIconExample' => 'applications/uiexample/examples/PHUIIconExample.php', @@ -2470,6 +2471,7 @@ phutil_register_library_map(array( 'PHUIFeedStoryExample' => 'PhabricatorUIExample', 'PHUIFeedStoryView' => 'AphrontView', 'PHUIFormDividerControl' => 'AphrontFormControl', + 'PHUIFormFreeformDateControl' => 'AphrontFormControl', 'PHUIFormMultiSubmitControl' => 'AphrontFormControl', 'PHUIFormPageView' => 'AphrontView', 'PHUIIconExample' => 'PhabricatorUIExample', diff --git a/src/applications/macro/query/PhabricatorMacroQuery.php b/src/applications/macro/query/PhabricatorMacroQuery.php index afab94e07f..70b3b2018d 100644 --- a/src/applications/macro/query/PhabricatorMacroQuery.php +++ b/src/applications/macro/query/PhabricatorMacroQuery.php @@ -11,6 +11,8 @@ final class PhabricatorMacroQuery private $authors; private $names; private $nameLike; + private $dateCreatedAfter; + private $dateCreatedBefore; private $status = 'status-any'; const STATUS_ANY = 'status-any'; @@ -55,6 +57,16 @@ final class PhabricatorMacroQuery return $this; } + public function withDateCreatedBefore($date_created_before) { + $this->dateCreatedBefore = $date_created_before; + return $this; + } + + public function withDateCreatedAfter($date_created_after) { + $this->dateCreatedAfter = $date_created_after; + return $this; + } + protected function loadPage() { $macro_table = new PhabricatorFileImageMacro(); $conn = $macro_table->establishConnection('r'); @@ -125,6 +137,20 @@ final class PhabricatorMacroQuery throw new Exception("Unknown status '{$this->status}'!"); } + if ($this->dateCreatedAfter) { + $where[] = qsprintf( + $conn, + 'm.dateCreated >= %d', + $this->dateCreatedAfter); + } + + if ($this->dateCreatedBefore) { + $where[] = qsprintf( + $conn, + 'm.dateCreated <= %d', + $this->dateCreatedBefore); + } + $where[] = $this->buildPagingClause($conn); return $this->formatWhereClause($where); diff --git a/src/applications/macro/query/PhabricatorMacroSearchEngine.php b/src/applications/macro/query/PhabricatorMacroSearchEngine.php index e070c58307..a60fdbed9a 100644 --- a/src/applications/macro/query/PhabricatorMacroSearchEngine.php +++ b/src/applications/macro/query/PhabricatorMacroSearchEngine.php @@ -12,6 +12,8 @@ final class PhabricatorMacroSearchEngine $saved->setParameter('status', $request->getStr('status')); $saved->setParameter('names', $request->getStrList('names')); $saved->setParameter('nameLike', $request->getStr('nameLike')); + $saved->setParameter('createdStart', $request->getStr('createdStart')); + $saved->setParameter('createdEnd', $request->getStr('createdEnd')); return $saved; } @@ -39,6 +41,17 @@ final class PhabricatorMacroSearchEngine $query->withNameLike($like); } + $start = $this->parseDateTime($saved->getParameter('createdStart')); + $end = $this->parseDateTime($saved->getParameter('createdEnd')); + + if ($start) { + $query->withDateCreatedAfter($start); + } + + if ($end) { + $query->withDateCreatedBefore($end); + } + return $query; } @@ -79,6 +92,14 @@ final class PhabricatorMacroSearchEngine ->setName('names') ->setLabel(pht('Exact Names')) ->setValue($names)); + + $this->buildDateRange( + $form, + $saved_query, + 'createdStart', + pht('Created After'), + 'createdEnd', + pht('Created Before')); } protected function getURI($path) { diff --git a/src/applications/search/controller/PhabricatorApplicationSearchController.php b/src/applications/search/controller/PhabricatorApplicationSearchController.php index ded01539d6..879d2f1f1b 100644 --- a/src/applications/search/controller/PhabricatorApplicationSearchController.php +++ b/src/applications/search/controller/PhabricatorApplicationSearchController.php @@ -131,6 +131,14 @@ final class PhabricatorApplicationSearchController $engine->buildSearchForm($form, $saved_query); + $errors = $engine->getErrors(); + if ($errors) { + $run_query = false; + $errors = id(new AphrontErrorView()) + ->setTitle(pht('Query Errors')) + ->setErrors($errors); + } + $submit = id(new AphrontFormSubmitControl()) ->setValue(pht('Execute Query')); @@ -184,6 +192,10 @@ final class PhabricatorApplicationSearchController } } + if ($errors) { + $nav->appendChild($errors); + } + if ($named_query) { $title = pht('Query: %s', $named_query->getQueryName()); } else { diff --git a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php index 67b518e587..6bace298b0 100644 --- a/src/applications/search/engine/PhabricatorApplicationSearchEngine.php +++ b/src/applications/search/engine/PhabricatorApplicationSearchEngine.php @@ -6,12 +6,14 @@ * * @task builtin Builtin Queries * @task uri Query URIs + * @task dates Date Filters * * @group search */ abstract class PhabricatorApplicationSearchEngine { private $viewer; + private $errors = array(); public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; @@ -54,6 +56,14 @@ abstract class PhabricatorApplicationSearchEngine { AphrontFormView $form, PhabricatorSavedQuery $query); + public function getErrors() { + return $this->errors; + } + + public function addError($error) { + $this->errors[] = $error; + return $this; + } /** * Return an application URI corresponding to the results page of a query. @@ -182,4 +192,88 @@ abstract class PhabricatorApplicationSearchEngine { public function buildSavedQueryFromBuiltin($query_key) { throw new Exception("Builtin '{$query_key}' is not supported!"); } + + +/* -( Dates )-------------------------------------------------------------- */ + + + /** + * @task dates + */ + protected function parseDateTime($date_time) { + if (!strlen($date_time)) { + return null; + } + + $viewer = $this->requireViewer(); + + $timezone = new DateTimeZone($viewer->getTimezoneIdentifier()); + + try { + $date = new DateTime($date_time, $timezone); + $timestamp = $date->format('U'); + } catch (Exception $e) { + $timestamp = null; + } + + return $timestamp; + } + + + /** + * @task dates + */ + protected function buildDateRange( + AphrontFormView $form, + PhabricatorSavedQuery $saved_query, + $start_key, + $start_name, + $end_key, + $end_name) { + + $start_str = $saved_query->getParameter($start_key); + $start = null; + if (strlen($start_str)) { + $start = $this->parseDateTime($start_str); + if (!$start) { + $this->addError( + pht( + '"%s" date can not be parsed.', + $start_name)); + } + } + + + $end_str = $saved_query->getParameter($end_key); + $end = null; + if (strlen($end_str)) { + $end = $this->parseDateTime($end_str); + if (!$end) { + $this->addError( + pht( + '"%s" date can not be parsed.', + $end_name)); + } + } + + if ($start && $end && ($start >= $end)) { + $this->addError( + pht( + '"%s" must be a date before "%s".', + $start_name, + $end_name)); + } + + $form + ->appendChild( + id(new PHUIFormFreeformDateControl()) + ->setName($start_key) + ->setLabel($start_name) + ->setValue($start_str)) + ->appendChild( + id(new AphrontFormTextControl()) + ->setName($end_key) + ->setLabel($end_name) + ->setValue($end_str)); + } } diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index 8fbbe9dfee..23aa5e06fd 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -1330,6 +1330,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { 'type' => 'php', 'name' => $this->getPatchPath('20130530.sessionhash.php'), ), + '20130530.macrodatekey.sql' => array( + 'type' => 'sql', + 'name' => $this->getPatchPath('20130530.macrodatekey.sql'), + ), ); } } diff --git a/src/view/form/control/PHUIFormFreeformDateControl.php b/src/view/form/control/PHUIFormFreeformDateControl.php new file mode 100644 index 0000000000..74a32d2e0a --- /dev/null +++ b/src/view/form/control/PHUIFormFreeformDateControl.php @@ -0,0 +1,21 @@ + 'text', + 'name' => $this->getName(), + 'value' => $this->getValue(), + 'disabled' => $this->getDisabled() ? 'disabled' : null, + 'id' => $this->getID(), + )); + } + +}