From 2842047c43ea094053d09e92e5580f6e0911f70e Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Fri, 4 Apr 2014 10:25:49 -0400 Subject: [PATCH] More flexible range facet options. - Resolves VUFIND-919. --- config/vufind/Summon.ini | 20 +- config/vufind/facets.ini | 16 +- .../src/VuFind/Controller/AbstractSearch.php | 186 +++++++++++++++--- .../VuFind/Controller/SearchController.php | 11 +- .../VuFind/Controller/SummonController.php | 10 +- .../src/VuFind/Recommend/SideFacets.php | 115 +++++++++-- .../VuFind/src/VuFind/Search/Base/Params.php | 185 ++++++++++++++--- .../VuFind/src/VuFind/Search/Solr/Params.php | 10 + .../src/VuFind/Search/Summon/Params.php | 10 + .../templates/Recommend/SideFacets.phtml | 16 +- .../templates/search/advanced/ranges.phtml | 8 +- .../templates/Recommend/SideFacets.phtml | 70 +++---- .../templates/search/advanced/ranges.phtml | 74 +++---- 13 files changed, 561 insertions(+), 170 deletions(-) diff --git a/config/vufind/Summon.ini b/config/vufind/Summon.ini index 7b7fa5f846c..7cc56c350b7 100644 --- a/config/vufind/Summon.ini +++ b/config/vufind/Summon.ini @@ -69,6 +69,11 @@ timeout = 30 [SpecialFacets] ; Any fields listed below will be treated as date ranges rather than plain facets: dateRange[] = PublicationDate +; Any fields listed below will be treated as numeric ranges rather than plain facets: +;numericRange[] = example_field_str +; Any fields listed below will be treated as free-form ranges rather than plain +; facets: +;genericRange[] = example_field_str ; This section is reserved for special boolean facets. These are displayed ; as checkboxes. If the box is checked, the filter on the left side of the @@ -137,10 +142,17 @@ ContentType = "Format" ; These settings affect the way facets are displayed on the advanced screen [Advanced_Facet_Settings] facet_limit = 100 ; how many values should we show for each facet? -; Some special facets for advanced searching can be turned on by inclusion in -; the comma-separated list below, or turned off by being excluded. Currently, -; just one values is supported: "daterange" for the publication year range -; control. +; The facets listed under the [Advanced] section above will be used as limiters on +; the advanced search screen and will be displayed uniformly as multi-select boxes. +; Some facet types don't lend themselves to this format, and they can be turned on +; by inclusion in the comma-separated list below, or turned off by being excluded. +; Supported values: +; daterange - for the range controls specified by the dateRange setting under +; [Special_Facets] above; if multiple fields are specified above but you +; only want certain ones on the advanced screen, you can filter with a +; colon separated list; e.g. "daterange:field1:field2:field3" +; genericrange - just like daterange above, but for genericRange[] fields. +; numericrange - just like daterange above, but for numericRange[] fields. special_facets = daterange ; Should we OR together facets rather than ANDing them? Set to * for ; all facets, use a comma-separated list to apply to some of the facets, set diff --git a/config/vufind/facets.ini b/config/vufind/facets.ini index 97fbb1bdf5c..ca6ad3793b0 100644 --- a/config/vufind/facets.ini +++ b/config/vufind/facets.ini @@ -28,6 +28,11 @@ topic_facet = "Suggested Topics" [SpecialFacets] ; Any fields listed below will be treated as date ranges rather than plain facets: dateRange[] = publishDate +; Any fields listed below will be treated as numeric ranges rather than plain facets: +;numericRange[] = example_field_str +; Any fields listed below will be treated as free-form ranges rather than plain +; facets: +;genericRange[] = example_field_str ; This section is reserved for special boolean facets. These are displayed ; as checkboxes. If the box is checked, the filter on the left side of the @@ -84,9 +89,14 @@ orFacets = * ; the advanced search screen and will be displayed uniformly as multi-select boxes. ; Some facet types don't lend themselves to this format, and they can be turned on ; by inclusion in the comma-separated list below, or turned off by being excluded. -; Currently, just two values are supported: "illustrated", for the "illustrated/not -; illustrated" radio button limiter and "daterange" for the publication year range -; control. +; Supported values: +; daterange - for the range controls specified by the dateRange setting under +; [Special_Facets] above; if multiple fields are specified above but you +; only want certain ones on the advanced screen, you can filter with a +; colon separated list; e.g. "daterange:field1:field2:field3" +; genericrange - just like daterange above, but for genericRange[] fields. +; illustrated - for the "illustrated/not illustrated" radio button limiter +; numericrange - just like daterange above, but for numericRange[] fields. special_facets = "illustrated,daterange" ; Any facets named in the list below will have their values run through the diff --git a/module/VuFind/src/VuFind/Controller/AbstractSearch.php b/module/VuFind/src/VuFind/Controller/AbstractSearch.php index 970345cc1e0..bfc25aa1c8f 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractSearch.php +++ b/module/VuFind/src/VuFind/Controller/AbstractSearch.php @@ -369,47 +369,177 @@ class AbstractSearch extends AbstractBase } /** - * Get the current settings for the date range facet, if it is set: + * Get the current settings for the specified range facet, if it is set: * + * @param array $fields Fields to check + * @param string $type Type of range to include in return value * @param object $savedSearch Saved search object (false if none) - * @param string $config Name of config file * * @return array */ - protected function getDateRangeSettings($savedSearch = false, $config = 'facets') + protected function getRangeSettings($fields, $type, $savedSearch = false) { $parts = array(); - $config = $this->getServiceLocator()->get('VuFind\Config')->get($config); - - if (isset($config->SpecialFacets->dateRange)) { - foreach ($config->SpecialFacets->dateRange as $field) { - // Default to blank strings: - $from = $to = ''; - - // Check to see if there is an existing range in the search object: - if ($savedSearch) { - $filters = $savedSearch->getParams()->getFilters(); - if (isset($filters[$field])) { - foreach ($filters[$field] as $current) { - if ($range = SolrUtils::parseRange($current)) { - $from = $range['from'] == '*' ? '' : $range['from']; - $to = $range['to'] == '*' ? '' : $range['to']; - $savedSearch->getParams() - ->removeFilter($field . ':' . $current); - break; - } + foreach ($fields as $field) { + // Default to blank strings: + $from = $to = ''; + + // Check to see if there is an existing range in the search object: + if ($savedSearch) { + $filters = $savedSearch->getParams()->getFilters(); + if (isset($filters[$field])) { + foreach ($filters[$field] as $current) { + if ($range = SolrUtils::parseRange($current)) { + $from = $range['from'] == '*' ? '' : $range['from']; + $to = $range['to'] == '*' ? '' : $range['to']; + $savedSearch->getParams() + ->removeFilter($field . ':' . $current); + break; } } } - - // Send back the settings: - $parts[] = array( - 'field' => $field, - 'values' => array($from, $to) - ); } + + // Send back the settings: + $parts[] = array( + 'field' => $field, + 'type' => $type, + 'values' => array($from, $to) + ); } + return $parts; } + + /** + * Get the current settings for the date range facets, if set: + * + * @param object $savedSearch Saved search object (false if none) + * @param string $config Name of config file + * @param array $filter Whitelist of fields to include (if empty, all + * fields will be returned) + * + * @return array + */ + protected function getDateRangeSettings($savedSearch = false, $config = 'facets', + $filter = array() + ) { + $config = $this->getServiceLocator()->get('VuFind\Config')->get($config); + + $fields = isset($config->SpecialFacets->dateRange) + ? $config->SpecialFacets->dateRange->toArray() + : array(); + + if (!empty($filter)) { + $fields = array_intersect($fields, $filter); + } + + return $this->getRangeSettings($fields, 'date', $savedSearch); + } + + /** + * Get the current settings for the generic range facets, if set: + * + * @param object $savedSearch Saved search object (false if none) + * @param string $config Name of config file + * @param array $filter Whitelist of fields to include (if empty, all + * fields will be returned) + * + * @return array + */ + protected function getGenericRangeSettings($savedSearch = false, + $config = 'facets', $filter = array() + ) { + $config = $this->getServiceLocator()->get('VuFind\Config')->get($config); + + $fields = isset($config->SpecialFacets->genericRange) + ? $config->SpecialFacets->genericRange->toArray() + : array(); + + if (!empty($filter)) { + $fields = array_intersect($fields, $filter); + } + + return $this->getRangeSettings($fields, 'generic', $savedSearch); + } + + /** + * Get the current settings for the numeric range facets, if set: + * + * @param object $savedSearch Saved search object (false if none) + * @param string $config Name of config file + * @param array $filter Whitelist of fields to include (if empty, all + * fields will be returned) + * + * @return array + */ + protected function getNumericRangeSettings($savedSearch = false, + $config = 'facets', $filter = array() + ) { + $config = $this->getServiceLocator()->get('VuFind\Config')->get($config); + + $fields = isset($config->SpecialFacets->numericRange) + ? $config->SpecialFacets->numericRange->toArray() + : array(); + + if (!empty($filter)) { + $fields = array_intersect($fields, $filter); + } + + return $this->getRangeSettings($fields, 'numeric', $savedSearch); + } + + /** + * Get all active range facets: + * + * @param array $specialFacets Special facet setting (in parsed format) + * @param object $savedSearch Saved search object (false if none) + * @param string $config Name of config file + * + * @return array + */ + protected function getAllRangeSettings($specialFacets, $savedSearch = false, + $config = 'facets' + ) { + $result = array(); + if (isset($specialFacets['daterange'])) { + $dates = $this->getDateRangeSettings( + $savedSearch, $config, $specialFacets['daterange'] + ); + $result = array_merge($result, $dates); + } + if (isset($specialFacets['genericrange'])) { + $generic = $this->getGenericRangeSettings( + $savedSearch, $config, $specialFacets['genericrange'] + ); + $result = array_merge($result, $generic); + } + if (isset($specialFacets['numericrange'])) { + $numeric = $this->getNumericRangeSettings( + $savedSearch, $config, $specialFacets['numericrange'] + ); + $result = array_merge($result, $numeric); + } + return $result; + } + + /** + * Parse the "special facets" setting. + * + * @param string $specialFacets Unparsed string + * + * @return array + */ + protected function parseSpecialFacetsSetting($specialFacets) + { + // Parse the special facets into a more useful format: + $parsed = array(); + foreach (explode(',', $specialFacets) as $current) { + $parts = explode(':', $current); + $key = array_shift($parts); + $parsed[$key] = $parts; + } + return $parsed; + } } \ No newline at end of file diff --git a/module/VuFind/src/VuFind/Controller/SearchController.php b/module/VuFind/src/VuFind/Controller/SearchController.php index cf5f74e2f7c..8d867453dce 100644 --- a/module/VuFind/src/VuFind/Controller/SearchController.php +++ b/module/VuFind/src/VuFind/Controller/SearchController.php @@ -54,15 +54,14 @@ class SearchController extends AbstractSearch $view->facetList = $this->processAdvancedFacets( $this->getAdvancedFacets()->getFacetList(), $view->saved ); - $specialFacets = $view->options->getSpecialAdvancedFacets(); - if (stristr($specialFacets, 'illustrated')) { + $specialFacets = $this->parseSpecialFacetsSetting( + $view->options->getSpecialAdvancedFacets() + ); + if (isset($specialFacets['illustrated'])) { $view->illustratedLimit = $this->getIllustrationSettings($view->saved); } - if (stristr($specialFacets, 'daterange')) { - $view->ranges - = $this->getDateRangeSettings($view->saved); - } + $view->ranges = $this->getAllRangeSettings($specialFacets, $view->saved); return $view; } diff --git a/module/VuFind/src/VuFind/Controller/SummonController.php b/module/VuFind/src/VuFind/Controller/SummonController.php index f27eb9b650b..888d33113bf 100644 --- a/module/VuFind/src/VuFind/Controller/SummonController.php +++ b/module/VuFind/src/VuFind/Controller/SummonController.php @@ -98,11 +98,11 @@ class SummonController extends AbstractSearch $view->facetList = $this->processAdvancedFacets( $this->getAdvancedFacets()->getFacetList(), $view->saved ); - $specialFacets = $view->options->getSpecialAdvancedFacets(); - if (stristr($specialFacets, 'daterange')) { - $view->ranges - = $this->getDateRangeSettings($view->saved, 'Summon'); - } + $specialFacets = $this->parseSpecialFacetsSetting( + $view->options->getSpecialAdvancedFacets() + ); + $view->ranges = $this + ->getAllRangeSettings($specialFacets, $view->saved, 'Summon'); return $view; } diff --git a/module/VuFind/src/VuFind/Recommend/SideFacets.php b/module/VuFind/src/VuFind/Recommend/SideFacets.php index dfc6661dab3..158d1256cb1 100644 --- a/module/VuFind/src/VuFind/Recommend/SideFacets.php +++ b/module/VuFind/src/VuFind/Recommend/SideFacets.php @@ -48,6 +48,20 @@ class SideFacets extends AbstractFacets */ protected $dateFacets = array(); + /** + * Generic range facet configuration + * + * @var array + */ + protected $genericRangeFacets = array(); + + /** + * Numeric range facet configuration + * + * @var array + */ + protected $numericRangeFacets = array(); + /** * Main facet configuration * @@ -96,11 +110,19 @@ class SideFacets extends AbstractFacets // Load boolean configurations: $this->loadBooleanConfigs($config, array_keys($this->mainFacets)); - // Get a list of fields that should be displayed as date ranges rather than + // Get a list of fields that should be displayed as ranges rather than // standard facet lists. if (isset($config->SpecialFacets->dateRange)) { $this->dateFacets = $config->SpecialFacets->dateRange->toArray(); } + if (isset($config->SpecialFacets->genericRange)) { + $this->genericRangeFacets + = $config->SpecialFacets->genericRange->toArray(); + } + if (isset($config->SpecialFacets->numericRange)) { + $this->numericRangeFacets + = $config->SpecialFacets->numericRange->toArray(); + } // Checkbox facets: if (substr($checkboxSection, 0, 1) == '~') { @@ -166,22 +188,54 @@ class SideFacets extends AbstractFacets */ public function getDateFacets() { - $filters = $this->results->getParams()->getFilters(); - $result = array(); - foreach ($this->dateFacets as $current) { - $from = $to = ''; - if (isset($filters[$current])) { - foreach ($filters[$current] as $filter) { - if ($range = SolrUtils::parseRange($filter)) { - $from = $range['from'] == '*' ? '' : $range['from']; - $to = $range['to'] == '*' ? '' : $range['to']; - break; - } - } + return $this->getRangeFacets('dateFacets'); + } + + /** + * getGenericRangeFacets + * + * Return generic range facet information in a format processed for use in the + * view. + * + * @return array Array of from/to value arrays keyed by field. + */ + public function getGenericRangeFacets() + { + return $this->getRangeFacets('genericRangeFacets'); + } + + /** + * getNumericRangeFacets + * + * Return numeric range facet information in a format processed for use in the + * view. + * + * @return array Array of from/to value arrays keyed by field. + */ + public function getNumericRangeFacets() + { + return $this->getRangeFacets('numericRangeFacets'); + } + + /** + * Get combined range details. + * + * @return array + */ + public function getAllRangeFacets() + { + $raw = array( + 'date' => $this->getDateFacets(), + 'generic' => $this->getGenericRangeFacets(), + 'numeric' => $this->getNumericRangeFacets() + ); + $processed = array(); + foreach ($raw as $type => $values) { + foreach ($values as $field => $range) { + $processed[$field] = array('type' => $type, 'values' => $range); } - $result[$current] = array($from, $to); } - return $result; + return $processed; } /** @@ -198,4 +252,35 @@ class SideFacets extends AbstractFacets } return array_map('trim', explode(',', $this->collapsedFacets)); } + + /** + * getRangeFacets + * + * Return range facet information in a format processed for use in the view. + * + * @param string $property Name of property containing active range facets + * + * @return array Array of from/to value arrays keyed by field. + */ + protected function getRangeFacets($property) + { + $filters = $this->results->getParams()->getFilters(); + $result = array(); + if (isset($this->$property) && is_array($this->$property)) { + foreach ($this->$property as $current) { + $from = $to = ''; + if (isset($filters[$current])) { + foreach ($filters[$current] as $filter) { + if ($range = SolrUtils::parseRange($filter)) { + $from = $range['from'] == '*' ? '' : $range['from']; + $to = $range['to'] == '*' ? '' : $range['to']; + break; + } + } + } + $result[$current] = array($from, $to); + } + } + return $result; + } } diff --git a/module/VuFind/src/VuFind/Search/Base/Params.php b/module/VuFind/src/VuFind/Search/Base/Params.php index b945bc619ec..33c5e555379 100644 --- a/module/VuFind/src/VuFind/Search/Base/Params.php +++ b/module/VuFind/src/VuFind/Search/Base/Params.php @@ -28,7 +28,7 @@ namespace VuFind\Search\Base; use Zend\ServiceManager\ServiceLocatorAwareInterface, Zend\ServiceManager\ServiceLocatorInterface; -use VuFindSearch\Query\Query; +use VuFindSearch\Backend\Solr\LuceneSyntaxHelper, VuFindSearch\Query\Query; use VuFind\Search\QueryAdapter; /** @@ -1065,6 +1065,21 @@ class Params implements ServiceLocatorAwareInterface return $facets; } + /** + * Initialize all range filters. + * + * @param \Zend\StdLib\Parameters $request Parameter object representing + * user request. + * + * @return void + */ + protected function initRangeFilters($request) + { + $this->initDateFilters($request); + $this->initGenericRangeFilters($request); + $this->initNumericRangeFilters($request); + } + /** * Support method for initDateFilters() -- normalize a year for use in a date * range. @@ -1089,16 +1104,108 @@ class Params implements ServiceLocatorAwareInterface } /** - * Support method for initDateFilters() -- build a filter query based on a range - * of dates. + * Support method for initNumericRangeFilters() -- normalize a year for use in + * a date range. + * + * @param string $num Value to format into a number. + * + * @return string Formatted number. + */ + protected function formatValueForNumericRange($num) + { + // empty strings are always wildcards: + if ($num == '') { + return '*'; + } + + // it's a string by default so this will kick it into interpreting it as a + // number + $num = $num + 0; + return $num = !is_float($num) && !is_int($num) ? '*' : $num; + } + + /** + * Support method for initGenericRangeFilters() -- build a filter query based on + * a range of values. * * @param string $field field to use for filtering. - * @param string $from year for start of range. - * @param string $to year for end of range. + * @param string $from start of range. + * @param string $to end of range. + * @param bool $cs Should ranges be case-sensitive? * * @return string filter query. */ - protected function buildDateRangeFilter($field, $from, $to) + protected function buildGenericRangeFilter($field, $from, $to, $cs = true) + { + // Assume Solr syntax -- this should be overridden in child classes where + // other indexing methodologies are used. + $range = "{$field}:[{$from} TO {$to}]"; + if (!$cs) { + // Flip values if out of order: + if (strcmp(strtolower($from), strtolower($to)) > 0) { + $range = "{$field}:[{$to} TO {$from}]"; + } + $helper = new LuceneSyntaxHelper(false, false); + $range = $helper->capitalizeRanges($range); + } + return $range; + } + + /** + * Support method for initFilters() -- initialize range filters. Factored + * out as a separate method so that it can be more easily overridden by child + * classes. + * + * @param \Zend\StdLib\Parameters $request Parameter object representing + * user request. + * @param string $requestParam Name of parameter containing + * names of range filter fields. + * @param Callable $valueFilter Optional callback to process + * values in the range. + * @param Callable $filterGenerator Optional callback to create + * a filter query from the range values. + * + * @return void + */ + protected function initGenericRangeFilters($request, + $requestParam = 'genericrange', $valueFilter = null, $filterGenerator = null + ) { + $rangeFacets = $request->get($requestParam); + if (!empty($rangeFacets)) { + $ranges = is_array($rangeFacets) ? $rangeFacets : array($rangeFacets); + foreach ($ranges as $range) { + // Load start and end of range: + $from = $request->get($range . 'from'); + $to = $request->get($range . 'to'); + + // Apply filtering/validation if necessary: + if (is_callable($valueFilter)) { + $from = call_user_func($valueFilter, $from); + $to = call_user_func($valueFilter, $to); + } + + // Build filter only if necessary: + if (!empty($range) && ($from != '*' || $to != '*')) { + $rangeFacet = is_callable($filterGenerator) + ? call_user_func($filterGenerator, $range, $from, $to) + : $this->buildGenericRangeFilter($range, $from, $to, false); + $this->addFilter($rangeFacet); + } + } + } + } + + /** + * Support method for initNumericRangeFilters() -- build a filter query based on + * a range of numbers. + * + * @param string $field field to use for filtering. + * @param string $from number for start of range. + * @param string $to number for end of range. + * + * @return string filter query. + */ + protected function buildNumericRangeFilter($field, $from, $to) { // Make sure that $to is less than $from: if ($to != '*' && $from!= '*' && $to < $from) { @@ -1107,9 +1214,23 @@ class Params implements ServiceLocatorAwareInterface $from = $tmp; } - // Assume Solr syntax -- this should be overridden in child classes where - // other indexing methodologies are used. - return "{$field}:[{$from} TO {$to}]"; + return $this->buildGenericRangeFilter($field, $from, $to); + } + + /** + * Support method for initDateFilters() -- build a filter query based on a range + * of dates. + * + * @param string $field field to use for filtering. + * @param string $from year for start of range. + * @param string $to year for end of range. + * + * @return string filter query. + */ + protected function buildDateRangeFilter($field, $from, $to) + { + // Dates work just like numbers: + return $this->buildNumericRangeFilter($field, $from, $to); } /** @@ -1124,26 +1245,28 @@ class Params implements ServiceLocatorAwareInterface */ protected function initDateFilters($request) { - $daterange = $request->get('daterange'); - if (!empty($daterange)) { - $ranges = is_array($daterange) ? $daterange : array($daterange); - foreach ($ranges as $range) { - // Validate start and end of range: - $yearFrom = $this->formatYearForDateRange( - $request->get($range . 'from') - ); - $yearTo = $this->formatYearForDateRange( - $request->get($range . 'to') - ); - - // Build filter only if necessary: - if (!empty($range) && ($yearFrom != '*' || $yearTo != '*')) { - $dateFilter - = $this->buildDateRangeFilter($range, $yearFrom, $yearTo); - $this->addFilter($dateFilter); - } - } - } + return $this->initGenericRangeFilters( + $request, 'daterange', array($this, 'formatYearForDateRange'), + array($this, 'buildDateRangeFilter') + ); + } + + /** + * Support method for initFilters() -- initialize numeric range filters. Factored + * out as a separate method so that it can be more easily overridden by child + * classes. + * + * @param \Zend\StdLib\Parameters $request Parameter object representing user + * request. + * + * @return void + */ + protected function initNumericRangeFilters($request) + { + return $this->initGenericRangeFilters( + $request, 'numericrange', array($this, 'formatValueForNumericRange'), + array($this, 'buildNumericRangeFilter') + ); } /** @@ -1168,8 +1291,8 @@ class Params implements ServiceLocatorAwareInterface } } - // Handle date range filters: - $this->initDateFilters($request); + // Handle range filters: + $this->initRangeFilters($request); } /** diff --git a/module/VuFind/src/VuFind/Search/Solr/Params.php b/module/VuFind/src/VuFind/Search/Solr/Params.php index e6c44bd8526..f4ae8fee22f 100644 --- a/module/VuFind/src/VuFind/Search/Solr/Params.php +++ b/module/VuFind/src/VuFind/Search/Solr/Params.php @@ -497,8 +497,18 @@ class Params extends \VuFind\Search\Base\Params ); // Convert range queries to a language-non-specific format: + $caseInsensitiveRegex = '/^\(\[(.*) TO (.*)\] OR \[(.*) TO (.*)\]\)$/'; if (preg_match('/^\[(.*) TO (.*)\]$/', $value, $matches)) { + // Simple case: [X TO Y] $filter['displayText'] = $matches[1] . '-' . $matches[2]; + } else if (preg_match($caseInsensitiveRegex, $value, $matches)) { + // Case insensitive case: [x TO y] OR [X TO Y]; convert + // only if values in both ranges match up! + if (strtolower($matches[3]) == strtolower($matches[1]) + && strtolower($matches[4]) == strtolower($matches[2]) + ) { + $filter['displayText'] = $matches[1] . '-' . $matches[2]; + } } return $filter; diff --git a/module/VuFind/src/VuFind/Search/Summon/Params.php b/module/VuFind/src/VuFind/Search/Summon/Params.php index 9cde5cc1bcb..e01d32764a7 100644 --- a/module/VuFind/src/VuFind/Search/Summon/Params.php +++ b/module/VuFind/src/VuFind/Search/Summon/Params.php @@ -290,8 +290,18 @@ class Params extends \VuFind\Search\Base\Params ); // Convert range queries to a language-non-specific format: + $caseInsensitiveRegex = '/^\(\[(.*) TO (.*)\] OR \[(.*) TO (.*)\]\)$/'; if (preg_match('/^\[(.*) TO (.*)\]$/', $value, $matches)) { + // Simple case: [X TO Y] $filter['displayText'] = $matches[1] . '-' . $matches[2]; + } else if (preg_match($caseInsensitiveRegex, $value, $matches)) { + // Case insensitive case: [x TO y] OR [X TO Y]; convert + // only if values in both ranges match up! + if (strtolower($matches[3]) == strtolower($matches[1]) + && strtolower($matches[4]) == strtolower($matches[2]) + ) { + $filter['displayText'] = $matches[1] . '-' . $matches[2]; + } } return $filter; diff --git a/themes/blueprint/templates/Recommend/SideFacets.phtml b/themes/blueprint/templates/Recommend/SideFacets.phtml index 6edc945ee86..91a056b0039 100644 --- a/themes/blueprint/templates/Recommend/SideFacets.phtml +++ b/themes/blueprint/templates/Recommend/SideFacets.phtml @@ -39,22 +39,24 @@ </ul> <? endif; ?> <?= isset($this->sideFacetExtraControls) ? $this->sideFacetExtraControls : '' ?> - <? $sideFacetSet = $this->recommend->getFacetSet(); $dateFacets = $this->recommend->getDateFacets(); ?> + <? $sideFacetSet = $this->recommend->getFacetSet(); $rangeFacets = $this->recommend->getAllRangeFacets(); ?> <? if (!empty($sideFacetSet) && $results->getResultTotal() > 0): ?> <? foreach ($sideFacetSet as $title => $cluster): ?> <? $allowExclude = $this->recommend->excludeAllowed($title); ?> - <? if (isset($dateFacets[$title])): ?> - <? /* Load the publication date slider UI widget */ $this->headScript()->appendFile('pubdate_slider.js'); ?> + <? if (isset($rangeFacets[$title])): ?> + <? if ($rangeFacets[$title]['type'] == 'date'): ?> + <? /* Load the publication date slider UI widget */ $this->headScript()->appendFile('pubdate_slider.js'); ?> + <? endif; ?> <form action="" name="<?=$this->escapeHtml($title)?>Filter" id="<?=$this->escapeHtml($title)?>Filter"> <?=$results->getUrlQuery()->asHiddenFields(array('page' => '/./', 'filter' => "/^{$title}:.*/"))?> - <input type="hidden" name="daterange[]" value="<?=$this->escapeHtml($title)?>"/> + <input type="hidden" name="<?=$this->escapeHtml($rangeFacets[$title]['type'])?>range[]" value="<?=$this->escapeHtml($title)?>"/> <fieldset class="publishDateLimit" id="<?=$this->escapeHtml($title)?>"> <legend><?=$this->transEsc($cluster['label'])?></legend> <label for="<?=$this->escapeHtml($title)?>from"><?=$this->transEsc('date_from')?>:</label> - <input type="text" size="4" maxlength="4" class="yearbox" name="<?=$this->escapeHtml($title)?>from" id="<?=$this->escapeHtml($title)?>from" value="<?=isset($dateFacets[$title][0])?$this->escapeHtml($dateFacets[$title][0]):''?>" /> + <input type="text" size="4" maxlength="4" class="yearbox" name="<?=$this->escapeHtml($title)?>from" id="<?=$this->escapeHtml($title)?>from" value="<?=isset($rangeFacets[$title]['values'][0])?$this->escapeHtml($rangeFacets[$title]['values'][0]):''?>" /> <label for="<?=$this->escapeHtml($title)?>to"><?=$this->transEsc('date_to')?>:</label> - <input type="text" size="4" maxlength="4" class="yearbox" name="<?=$this->escapeHtml($title)?>to" id="<?=$this->escapeHtml($title)?>to" value="<?=isset($dateFacets[$title][1])?$this->escapeHtml($dateFacets[$title][1]):''?>" /> - <div id="<?=$this->escapeHtml($title)?>Slider" class="dateSlider"></div> + <input type="text" size="4" maxlength="4" class="yearbox" name="<?=$this->escapeHtml($title)?>to" id="<?=$this->escapeHtml($title)?>to" value="<?=isset($rangeFacets[$title]['values'][1])?$this->escapeHtml($rangeFacets[$title]['values'][1]):''?>" /> + <div id="<?=$this->escapeHtml($title)?>Slider" class="<?=$this->escapeHtml($rangeFacets[$title]['type'])?>Slider"></div> <input type="submit" value="<?=$this->transEsc('Set')?>" id="<?=$this->escapeHtml($title)?>goButton"/> </fieldset> </form> diff --git a/themes/blueprint/templates/search/advanced/ranges.phtml b/themes/blueprint/templates/search/advanced/ranges.phtml index ab3f6bff3e5..74d7240a09d 100644 --- a/themes/blueprint/templates/search/advanced/ranges.phtml +++ b/themes/blueprint/templates/search/advanced/ranges.phtml @@ -1,15 +1,17 @@ <? if (isset($this->ranges) && !empty($this->ranges)): ?> <? $params = $this->searchParams($this->searchClassId); $params->activateAllFacets(); ?> - <? /* Load the publication date slider UI widget */ $this->headScript()->appendFile('pubdate_slider.js'); ?> <? foreach ($this->ranges as $current): $escField = $this->escapeHtml($current['field']); ?> - <input type="hidden" name="daterange[]" value="<?=$escField?>"/> + <? if ($current['type'] == 'date'): ?> + <? /* Load the publication date slider UI widget */ $this->headScript()->appendFile('pubdate_slider.js'); ?> + <? endif; ?> + <input type="hidden" name="<?=$this->escapeHtml($current['type'])?>range[]" value="<?=$escField?>"/> <fieldset class="publishDateLimit span-5" id="<?=$escField?>"> <legend><?=$this->transEsc($params->getFacetLabel($current['field']))?></legend> <label for="<?=$escField?>from"><?=$this->transEsc('date_from')?>:</label> <input type="text" size="4" maxlength="4" class="yearbox" name="<?=$escField?>from" id="<?=$escField?>from" value="<?=$this->escapeHtml($current['values'][0])?>" /> <label for="<?=$escField?>to"><?=$this->transEsc('date_to')?>:</label> <input type="text" size="4" maxlength="4" class="yearbox" name="<?=$escField?>to" id="<?=$escField?>to" value="<?=$this->escapeHtml($current['values'][1])?>" /> - <div id="<?=$escField?>Slider" class="dateSlider"></div> + <div id="<?=$escField?>Slider" class="<?=$this->escapeHtml($current['type'])?>Slider"></div> </fieldset> <? endforeach; ?> <? endif; ?> diff --git a/themes/bootstrap/templates/Recommend/SideFacets.phtml b/themes/bootstrap/templates/Recommend/SideFacets.phtml index 0c2b5edbdd2..f8047a75893 100644 --- a/themes/bootstrap/templates/Recommend/SideFacets.phtml +++ b/themes/bootstrap/templates/Recommend/SideFacets.phtml @@ -43,59 +43,63 @@ </ul> <? endif; ?> <?= isset($this->sideFacetExtraControls) ? $this->sideFacetExtraControls : '' ?> -<? $sideFacetSet = $this->recommend->getFacetSet(); $dateFacets = $this->recommend->getDateFacets(); ?> +<? $sideFacetSet = $this->recommend->getFacetSet(); $rangeFacets = $this->recommend->getAllRangeFacets(); ?> <? if (!empty($sideFacetSet) && $results->getResultTotal() > 0): ?> <? foreach ($sideFacetSet as $title => $cluster): ?> <? $allowExclude = $this->recommend->excludeAllowed($title); ?> <ul class="nav nav-list collapsed <? if(!in_array($title, $collapsedFacets)): ?> open<? endif ?>"> - <? if (isset($dateFacets[$title])): ?> + <? if (isset($rangeFacets[$title])): ?> <li class="nav-header"><?=$this->transEsc($cluster['label'])?></li> <li> <form class="form-inline text-center" action="" name="<?=$this->escapeHtml($title)?>Filter" id="<?=$this->escapeHtml($title)?>Filter"> <?=$results->getUrlQuery()->asHiddenFields(array('page' => "/./", 'filter' => "/^{$title}:.*/"))?> - <input type="hidden" name="daterange[]" value="<?=$this->escapeHtml($title)?>"/> + <input type="hidden" name="<?=$this->escapeHtml($rangeFacets[$title]['type'])?>range[]" value="<?=$this->escapeHtml($title)?>"/> <div class="row-fluid"> <label class="span6" for="<?=$this->escapeHtml($title)?>from"> <?=$this->transEsc('date_from')?>:<br/> - <input type="text" maxlength="4" class="span12" name="<?=$this->escapeHtml($title)?>from" id="<?=$this->escapeHtml($title)?>from" value="<?=isset($dateFacets[$title][0])?$dateFacets[$title][0]:''?>" /> + <input type="text" maxlength="4" class="span12" name="<?=$this->escapeHtml($title)?>from" id="<?=$this->escapeHtml($title)?>from" value="<?=isset($rangeFacets[$title]['values'][0])?$this->escapeHtml($rangeFacets[$title]['values'][0]):''?>" /> </label> <label class="span6" for="<?=$this->escapeHtml($title)?>to"> <?=$this->transEsc('date_to')?>:<br/> - <input type="text" maxlength="4" class="span12" name="<?=$this->escapeHtml($title)?>to" id="<?=$this->escapeHtml($title)?>to" value="<?=isset($dateFacets[$title][1])?$dateFacets[$title][1]:''?>" /> + <input type="text" maxlength="4" class="span12" name="<?=$this->escapeHtml($title)?>to" id="<?=$this->escapeHtml($title)?>to" value="<?=isset($rangeFacets[$title]['values'][1])?$this->escapeHtml($rangeFacets[$title]['values'][1]):''?>" /> </label> </div> - <div class="row-fluid"><input type="text" class="span10 hidden" id="<?=$this->escapeHtml($title)?>dateSlider"/></div> + <? if ($rangeFacets[$title]['type'] == 'date'): ?> + <div class="row-fluid"><input type="text" class="span10 hidden" id="<?=$this->escapeHtml($title)?><?=$this->escapeHtml($rangeFacets[$title]['type'])?>Slider"/></div> + <? endif; ?> <input class="btn" type="submit" value="<?=$this->transEsc('Set')?>"/> </form> </li> - <? $this->headScript()->appendFile('bootstrap-slider.js'); ?> - <? - $min = !empty($dateFacets[$title][0]) ? min($dateFacets[$title][0], 1400) : 1400; - $future = date('Y', time()+31536000); - $max = !empty($dateFacets[$title][1]) ? max($future, $dateFacets[$title][1]) : $future; - $low = !empty($dateFacets[$title][0]) ? $dateFacets[$title][0] : $min; - $high = !empty($dateFacets[$title][1]) ? $dateFacets[$title][1] : $max; - $script = <<<JS - $(document).ready(function() { - var fillTexts = function() { - var v = {$this->escapeHtml($title)}dateSlider.getValue(); - $('#{$this->escapeHtml($title)}from').val(v[0]); - $('#{$this->escapeHtml($title)}to').val(v[1]); - }; - var {$this->escapeHtml($title)}dateSlider = $('#{$this->escapeHtml($title)}dateSlider') - .slider({ - 'min':{$min}, - 'max':{$max}, - 'handle':"square", - 'tooltip':"hide", - 'value':[{$low},{$high}] - }) - .on('slide', fillTexts) - .data('slider'); - }); + <? if ($rangeFacets[$title]['type'] == 'date'): ?> + <? $this->headScript()->appendFile('bootstrap-slider.js'); ?> + <? + $min = !empty($rangeFacets[$title][0]) ? min($rangeFacets[$title][0], 1400) : 1400; + $future = date('Y', time()+31536000); + $max = !empty($rangeFacets[$title][1]) ? max($future, $rangeFacets[$title][1]) : $future; + $low = !empty($rangeFacets[$title][0]) ? $rangeFacets[$title][0] : $min; + $high = !empty($rangeFacets[$title][1]) ? $rangeFacets[$title][1] : $max; + $script = <<<JS +$(document).ready(function() { + var fillTexts = function() { + var v = {$this->escapeHtml($title)}dateSlider.getValue(); + $('#{$this->escapeHtml($title)}from').val(v[0]); + $('#{$this->escapeHtml($title)}to').val(v[1]); + }; + var {$this->escapeHtml($title)}dateSlider = $('#{$this->escapeHtml($title)}dateSlider') + .slider({ + 'min':{$min}, + 'max':{$max}, + 'handle':"square", + 'tooltip':"hide", + 'value':[{$low},{$high}] + }) + .on('slide', fillTexts) + .data('slider'); +}); JS; - ?> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> + ?> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> + <? endif; ?> <? else: ?> <li class="nav-header"><?=$this->transEsc($cluster['label'])?></li> <? foreach ($cluster['list'] as $i=>$thisFacet): ?> diff --git a/themes/bootstrap/templates/search/advanced/ranges.phtml b/themes/bootstrap/templates/search/advanced/ranges.phtml index bb780f2a9b6..6dee945bbf2 100644 --- a/themes/bootstrap/templates/search/advanced/ranges.phtml +++ b/themes/bootstrap/templates/search/advanced/ranges.phtml @@ -3,47 +3,51 @@ <? foreach ($this->ranges as $current): $escField = $this->escapeHtml($current['field']); ?> <fieldset class="span4 text-center"> <legend class="text-left"><?=$this->transEsc($params->getFacetLabel($current['field']))?></legend> - <input type="hidden" name="daterange[]" value="<?=$escField?>"/> + <input type="hidden" name="<?=$this->escapeHtml($current['type'])?>range[]" value="<?=$escField?>"/> <label for="<?=$escField?>from"><?=$this->transEsc('date_from')?>:</label> <input type="text" maxlength="4" class="yearbox span4" name="<?=$escField?>from" id="<?=$escField?>from" value="<?=isset($current['values'][0])?$this->escapeHtml($current['values'][0]):''?>" /> <label for="<?=$escField?>to"><?=$this->transEsc('date_to')?>:</label> <input type="text" maxlength="4" class="yearbox span4" name="<?=$escField?>to" id="<?=$escField?>to" value="<?=isset($current['values'][1])?$this->escapeHtml($current['values'][1]):''?>" /> - <div class="pad"><input type="text" id="<?=$escField?>dateSlider"></div> + <? if ($current['type'] == 'date'): ?> + <div class="pad"><input type="text" id="<?=$escField?><?=$this->escapeHtml($current['type'])?>Slider"></div> + <? endif; ?> </fieldset> - <? - $this->headScript()->appendFile('bootstrap-slider.js'); - $min = !empty($current['values'][0]) ? min($current['values'][0], 1400) : 1400; - $future = date('Y', time()+31536000); - $max = !empty($current['values'][1]) ? max($future, $current['values'][1]) : $future; - $low = !empty($current['values'][0]) ? $current['values'][0] : $min; - $high = !empty($current['values'][1]) ? $current['values'][1] : $max; - $min = intval($min); - $max = intval($max); - $low = intval($low); - $high = intval($high); - $init = !empty($current['values'][0]) ? 'fillTexts()' : ''; - $script = <<<JS - $(document).ready(function() { - var fillTexts = function() { - var v = {$escField}dateSlider.getValue(); - $('#${escField}from').val(v[0]); - $('#${escField}to').val(v[1]); - }; - var {$escField}dateSlider = $('#{$escField}dateSlider') - .slider({ - 'min':{$min}, - 'max':{$max}, - 'handle':"square", - 'tooltip':"hide", - 'value':[{$low},{$high}] - }) - .on('slide', fillTexts) - .data('slider'); - {$init} - }); + <? if ($current['type'] == 'date'): ?> + <? + $this->headScript()->appendFile('bootstrap-slider.js'); + $min = !empty($current['values'][0]) ? min($current['values'][0], 1400) : 1400; + $future = date('Y', time()+31536000); + $max = !empty($current['values'][1]) ? max($future, $current['values'][1]) : $future; + $low = !empty($current['values'][0]) ? $current['values'][0] : $min; + $high = !empty($current['values'][1]) ? $current['values'][1] : $max; + $min = intval($min); + $max = intval($max); + $low = intval($low); + $high = intval($high); + $init = !empty($current['values'][0]) ? 'fillTexts()' : ''; + $script = <<<JS + $(document).ready(function() { + var fillTexts = function() { + var v = {$escField}dateSlider.getValue(); + $('#${escField}from').val(v[0]); + $('#${escField}to').val(v[1]); + }; + var {$escField}dateSlider = $('#{$escField}dateSlider') + .slider({ + 'min':{$min}, + 'max':{$max}, + 'handle':"square", + 'tooltip':"hide", + 'value':[{$low},{$high}] + }) + .on('slide', fillTexts) + .data('slider'); + {$init} + }); JS; - ?> - <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> + ?> + <?=$this->inlineScript(\Zend\View\Helper\HeadScript::SCRIPT, $script, 'SET'); ?> + <? endif; ?> <? endforeach; ?> <? endif; ?> </div> -- GitLab