/** * Test sanitizeDate functionality. * * @return void */ public function testSanitizeDate() { $tests = ['[2014]' => '2014-01-01', 'n.d.' => null, 'may 7, 1981' => '1981-05-07', 'July 1570' => '1570-07-01', 'incomprehensible garbage' => null, '1930/12/21' => '1930-12-21', '1964?' => '1964-01-01', '1947-3' => '1947-03-01', '1973-02-31' => '1973-02-01', '1973-31-31' => '1973-01-01', '1964-zz' => '1964-01-01', '1964-01-zz' => '1964-01-01', 'Winter 2012' => '2012-01-01', '05-1901' => '1901-05-01', '5-1901' => '1901-05-01', '05/1901' => '1901-05-01', '5/1901' => '1901-05-01', '2nd Quarter 2004' => '2004-01-01', 'Nov 2009 and Dec 2009' => '2009-01-01']; foreach ($tests as $in => $out) { $this->assertEquals($out === null ? null : $out . 'T00:00:00Z', Utils::sanitizeDate($in)); } }
/** * Set up filters based on VuFind settings. * * @param ParamBag $params Parameter collection to update * * @return void */ public function createBackendFilterParameters(ParamBag $params) { // flag our non-Standard checkbox filters: $foundIncludeNewspapers = false; # includeNewspapers $foundIncludeWithoutFulltext = false; # includeWithoutFulltext $filterList = $this->getFilterList(); // Which filters should be applied to our query? if (!empty($filterList)) { // Loop through all filters and add appropriate values to request: foreach ($filterList as $filterArray) { foreach ($filterArray as $filt) { $safeValue = SummonQuery::escapeParam($filt['value']); if ($filt['field'] == 'holdingsOnly') { // Special case -- "holdings only" is a separate parameter from // other facets. $params->set('holdings', strtolower(trim($safeValue)) == 'true'); } else { if ($filt['field'] == 'excludeNewspapers') { // support a checkbox for excluding newspapers: // this is now the default behaviour. } else { if ($filt['field'] == 'includeNewspapers') { // explicitly include newspaper articles $foundIncludeNewspapers = true; } else { if ($range = SolrUtils::parseRange($filt['value'])) { // Special case -- range query (translate [x TO y] syntax): $from = SummonQuery::escapeParam($range['from']); $to = SummonQuery::escapeParam($range['to']); $params->add('rangeFilters', "PublicationDate,{$from}:{$to}"); } else { if ($filt['field'] == 'includeWithoutFulltext') { $foundIncludeWithoutFulltext = true; } else { // Standard case: $params->add('filters', "{$filt['field']},{$safeValue}"); } } } } } } } } // special cases (apply also when filter list is empty) // newspaper articles if (!$foundIncludeNewspapers) { // this actually means: do *not* show newspaper articles $params->add('filters', "ContentType,Newspaper Article,true"); } // combined facet "with holdings/with fulltext" if (!$foundIncludeWithoutFulltext) { $params->set('holdings', true); $params->add('filters', 'IsFullText,true'); } else { $params->set('holdings', false); } }
/** * Get new items facets (facet titles) * * @return array */ public function getNewItemsFacets() { if (!isset($this->newItemsFacets)) { return []; } $filters = $this->results->getParams()->getFilters(); $result = []; foreach ($this->newItemsFacets as $current) { $from = ''; if (isset($filters[$current])) { foreach ($filters[$current] as $filter) { if ($range = SolrUtils::parseRange($filter)) { $from = $range['from'] == '*' ? '' : $range['from']; break; } } } $result[$current] = ['raw' => $from, 'date' => preg_match('/^\\d{4}-\\d{2}-\\d{2}/', $from) ? substr($from, 0, 10) : str_replace('/DAY', '', $from)]; } return $result; }
/** * Support function to get publication date range. Return string in the form * "YYYY-YYYY" * * @param string $field Name of filter field to check for * date limits * @param \VuFind\Search\Params\Base $params Search parameter object * @param \Zend\StdLib\Parameters $request Parameter object representing user * request. * * @return string */ protected function getPublishedDates($field, $params, $request) { // Try to extract range details from request parameters or SearchObject: $from = $request->get($field . 'from'); $to = $request->get($field . 'to'); if (!is_null($from) && !is_null($to)) { $range = ['from' => $from, 'to' => $to]; } else { if (is_object($params)) { $currentFilters = $params->getFilters(); if (isset($currentFilters[$field][0])) { $range = SolrUtils::parseRange($currentFilters[$field][0]); } } } // Normalize range if we found one: if (isset($range)) { if (empty($range['from']) || $range['from'] == '*') { $range['from'] = 0; } if (empty($range['to']) || $range['to'] == '*') { $range['to'] = date('Y') + 1; } return $range['from'] . '-' . $range['to']; } // No range found? Return empty string: return ''; }
/** * Format a single filter for use in getFilterList(). * * @param string $field Field name * @param string $value Field value * @param string $operator Operator (AND/OR/NOT) * @param bool $translate Should we translate the label? * * @return array */ protected function formatFilterListEntry($field, $value, $operator, $translate) { if (!in_array($field, $this->newItemsFacets) || !($range = Utils::parseRange($value))) { $result = parent::formatFilterListEntry($field, $value, $operator, $translate); return $this->formatDateRangeFilterListEntry($result, $field, $value); } $domain = $this->getOptions()->getTextDomainForTranslatedFacet($field); list($from, $fromDate) = $this->formatNewItemsDateForDisplay($range['from'], $domain); list($to, $toDate) = $this->formatNewItemsDateForDisplay($range['to'], $domain); $ndash = html_entity_decode('–', ENT_NOQUOTES, 'UTF-8'); if ($fromDate && $toDate) { $displayText = $from ? "{$from} {$ndash}" : $ndash; $displayText .= $to ? " {$to}" : ''; } else { $displayText = $from; $displayText .= $to ? " {$ndash} {$to}" : ''; } return compact('value', 'displayText', 'field', 'operator'); }
/** * Set up filters based on VuFind settings. * * @param ParamBag $params Parameter collection to update * * @return void */ public function createBackendFilterParameters(ParamBag $params) { // Which filters should be applied to our query? $filterList = $this->getFilterList(); if (!empty($filterList)) { $orFacets = []; // Loop through all filters and add appropriate values to request: foreach ($filterList as $filterArray) { foreach ($filterArray as $filt) { $safeValue = SummonQuery::escapeParam($filt['value']); // Special case -- "holdings only" is a separate parameter from // other facets. if ($filt['field'] == 'holdingsOnly') { $params->set('holdings', strtolower(trim($safeValue)) == 'true'); } else { if ($filt['field'] == 'queryExpansion') { // Special case -- "query expansion" is a separate parameter // from other facets. $params->set('expand', strtolower(trim($safeValue)) == 'true'); } else { if ($filt['field'] == 'excludeNewspapers') { // Special case -- support a checkbox for excluding // newspapers: $params->add('filters', "ContentType,Newspaper Article,true"); } else { if ($range = SolrUtils::parseRange($filt['value'])) { // Special case -- range query (translate [x TO y] syntax): $from = SummonQuery::escapeParam($range['from']); $to = SummonQuery::escapeParam($range['to']); $params->add('rangeFilters', "{$filt['field']},{$from}:{$to}"); } else { if ($filt['operator'] == 'OR') { // Special case -- OR facets: $orFacets[$filt['field']] = isset($orFacets[$filt['field']]) ? $orFacets[$filt['field']] : []; $orFacets[$filt['field']][] = $safeValue; } else { // Standard case: $fq = "{$filt['field']},{$safeValue}"; if ($filt['operator'] == 'NOT') { $fq .= ',true'; } $params->add('filters', $fq); } } } } } } // Deal with OR facets: foreach ($orFacets as $field => $values) { $params->add('groupFilters', $field . ',or,' . implode(',', $values)); } } } }
/** * Test parseRange functionality. * * @return void */ public function testParseRange() { // basic range test: $result = Utils::parseRange("[1 TO 100]"); $this->assertEquals('1', $result['from']); $this->assertEquals('100', $result['to']); // test whitespace handling: $result = Utils::parseRange("[1 TO 100]"); $this->assertEquals('1', $result['from']); $this->assertEquals('100', $result['to']); // test invalid ranges: $this->assertFalse(Utils::parseRange('1 TO 100')); $this->assertFalse(Utils::parseRange('[not a range to me]')); }
/** * 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 = []; 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] = [$from, $to]; } } return $result; }
/** * Process the publicationd date range limiter widget * * @param object $searchObject Saved search object (false if none) * * @return array To and from dates */ protected function processPublicationDateRange($searchObject = false) { $from = $to = ''; if ($searchObject) { $filters = $searchObject->getParams()->getFilterList(); foreach ($filters as $key => $value) { if ('PublicationDate' == $key) { if ($range = SolrUtils::parseRange($value[0]['value'])) { $from = $range['from'] == '*' ? '11' : $range['from']; $to = $range['to'] == '*' ? '12' : $range['to']; } $searchObject->getParams()->removeFilter($key . ':' . $value[0]['value']); break; } } } return [$from, $to]; }
/** * Support method for initSearchParams() -- set up query, handler and filters. * * @return void */ protected function initSearchQuery() { // Grab relevant user parameters: $query = $this->userSearchParams['query']; $filter = $this->userSearchParams['filter']; $handler = $this->userSearchParams['handler']; // Determine which handler to use if (!$this->isAdvanced($query)) { $ss = is_null($handler) ? null : $this->getSearchSpecs($handler); // Is this a Dismax search? if (isset($ss['DismaxFields'])) { // Specify the fields to do a Dismax search on and use the default // Dismax search handler so we can use appropriate user-specified // solrconfig.xml settings: $this->solrSearchParams['qf'] = implode(' ', $ss['DismaxFields']); $this->solrSearchParams['qt'] = 'dismax'; // Load any custom Dismax parameters from the YAML search spec file: if (isset($ss['DismaxParams']) && is_array($ss['DismaxParams'])) { foreach ($ss['DismaxParams'] as $current) { $this->addSolrSearchParam($current[0], $current[1]); } } // Apply search-specific filters if necessary: if (isset($ss['FilterQuery'])) { if (is_array($filter)) { $filter[] = $ss['FilterQuery']; } else { $filter = array($ss['FilterQuery']); } } } else { // Not DisMax... but if we have a handler set, we may still need // to build a query using a setting in the YAML search specs or a // simple field name: if (!empty($handler)) { $query = $this->buildQueryComponent($handler, $query); } } } else { // Force boolean operators and ranges to uppercase if we are in a // case-insensitive mode: if (!$this->caseSensitiveBooleans) { $query = SolrUtils::capitalizeBooleans($query); } if (!$this->caseSensitiveRanges) { $query = SolrUtils::capitalizeRanges($query); } // Process advanced search -- if a handler was specified, let's see // if we can adapt the search to work with the appropriate fields. if (!empty($handler)) { // If highlighting is enabled, we only want to use the inner query // for highlighting; anything added outside of this is a boost and // should be ignored for highlighting purposes! if ($this->userSearchParams['highlight']) { $this->solrSearchParams['hl.q'] = $this->buildAdvancedInnerQuery($handler, $query); } $query = $this->buildAdvancedQuery($handler, $query); } } // Now that query and filters are fully processed, add them to the params: $this->solrSearchParams['q'] = $query; if (is_array($filter) && count($filter)) { $this->solrSearchParams['fq'] = $filter; } }
/** * Build basic Query string from search parameters (support method for * buildQuery) * * @param array $params An array of search parameters * * @return string */ protected function buildBasicQuery($params) { // Clean and validate input -- note that index may be in a // different field depending on whether this is a basic or // advanced search. $lookfor = $params['lookfor']; if (isset($params['field'])) { $index = $params['field']; } else { if (isset($params['index'])) { $index = $params['index']; } else { $index = 'AllFields'; } } // Force boolean operators to uppercase if we are in a // case-insensitive mode: if (!$this->caseSensitiveBooleans) { $lookfor = SolrUtils::capitalizeBooleans($lookfor); } // Prepend the index name, unless it's the special "AllFields" // index: return $index != 'AllFields' ? "{$index}:({$lookfor})" : $lookfor; }
/** * Get the current settings for the date range facet, if it is set: * * @param object $savedSearch Saved search object (false if none) * * @return array Date range: Key 0 = from, Key 1 = to. */ protected function getDateRangeSettings($savedSearch = false) { // 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['publishDate'])) { foreach ($filters['publishDate'] as $current) { if ($range = SolrUtils::parseRange($current)) { $from = $range['from'] == '*' ? '' : $range['from']; $to = $range['to'] == '*' ? '' : $range['to']; $savedSearch->getParams()->removeFilter('publishDate:' . $current); break; } } } } // Send back the settings: return array($from, $to); }
/** * Get new items facets (facet titles) * * @return array */ public function getNewItemsFacets() { $filters = $this->results->getParams()->getFilters(); $result = []; foreach ($this->newItemsFacets as $current) { $from = ''; if (isset($filters[$current])) { foreach ($filters[$current] as $filter) { if ($range = SolrUtils::parseRange($filter)) { $from = $range['from'] == '*' ? '' : $range['from']; break; } } } $translatable = ''; if (preg_match('/^NOW-(\\w+)/', $from, $matches)) { $translatable = 'new_items_' . strtolower($matches[1]); } $result[$current] = ['raw' => $from, 'translatable' => $translatable]; } return $result; }
/** * Support method for initFullDateFilters() -- normalize a date for use in a * year/month/day date range. * * @param string $date Value to check for valid date. * * @return string Formatted date. */ protected function formatDateForFullDateRange($date) { // Make sure date is valid; default to wildcard otherwise: $date = SolrUtils::sanitizeDate($date); return $date === null ? '*' : $date; }
/** * Set up filters based on VuFind settings. * * @param array $filterList Filter settings * * @return void */ public function initFilters($filterList) { // Which filters should be applied to our query? if (!empty($filterList)) { // Loop through all filters and add appropriate values to request: foreach ($filterList as $filterArray) { foreach ($filterArray as $filt) { $safeValue = self::escapeParam($filt['value']); // Special case -- "holdings only" is a separate parameter from // other facets. if ($filt['field'] == 'holdingsOnly') { $this->setHoldings(strtolower(trim($safeValue)) == 'true'); } else { if ($filt['field'] == 'excludeNewspapers') { // Special case -- support a checkbox for excluding // newspapers: $this->addFilter("ContentType,Newspaper Article,true"); } else { if ($range = SolrUtils::parseRange($filt['value'])) { // Special case -- range query (translate [x TO y] syntax): $from = self::escapeParam($range['from']); $to = self::escapeParam($range['to']); $this->addRangeFilter("{$filt['field']},{$from}:{$to}"); } else { // Standard case: $this->addFilter("{$filt['field']},{$safeValue}"); } } } } } } }
/** * 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) * * @return array */ protected function getRangeSettings($fields, $type, $savedSearch = false) { $parts = []; 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[] = ['field' => $field, 'type' => $type, 'values' => [$from, $to]]; } return $parts; }
/** * getDateFacets * * Return date facet information in a format processed for use in the view. * * @return array Array of from/to value arrays keyed by field. */ 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; } } } $result[$current] = array($from, $to); } return $result; }