From 4efdee17a14485b022f517b25a8b2f768c3890c2 Mon Sep 17 00:00:00 2001 From: Demian Katz <demian.katz@villanova.edu> Date: Mon, 2 Jul 2012 14:32:34 -0400 Subject: [PATCH] Began porting Summon libraries (untested). --- .../VuFind/src/VuFind/Connection/Summon.php | 230 ++++++++++++++++++ .../src/VuFind/Connection/Summon/Query.php | 123 ++++++++++ vendor/SerialsSolutions/Summon/Zend2.php | 124 ++++++++++ 3 files changed, 477 insertions(+) create mode 100644 module/VuFind/src/VuFind/Connection/Summon.php create mode 100644 module/VuFind/src/VuFind/Connection/Summon/Query.php create mode 100644 vendor/SerialsSolutions/Summon/Zend2.php diff --git a/module/VuFind/src/VuFind/Connection/Summon.php b/module/VuFind/src/VuFind/Connection/Summon.php new file mode 100644 index 00000000000..86e1eb514b6 --- /dev/null +++ b/module/VuFind/src/VuFind/Connection/Summon.php @@ -0,0 +1,230 @@ +<?php +/** + * Summon Search API Interface (VuFind implementation) + * + * PHP version 5 + * + * Copyright (C) Andrew Nagy 2009. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind2 + * @package Support_Classes + * @author Andrew S. Nagy <vufind-tech@lists.sourceforge.net> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://api.summon.serialssolutions.com/help/api/ API Documentation + */ +namespace VuFind\Connection; +use SerialsSolutions\Summon\Zend2 as BaseSummon, + VuFind\Config\Reader as ConfigReader, + VuFind\Http\Client as HttpClient, + VuFind\Log\Logger, + VuFind\Solr\Utils as SolrUtils; + +/** + * Summon Search API Interface (VuFind implementation) + * + * @category VuFind2 + * @package Support_Classes + * @author Andrew S. Nagy <vufind-tech@lists.sourceforge.net> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://api.summon.serialssolutions.com/help/api/ API Documentation + */ +class Summon extends BaseSummon; +{ + /** + * Should boolean operators in the search string be treated as + * case-insensitive (false), or must they be ALL UPPERCASE (true)? + */ + protected $caseSensitiveBooleans = true; + + /** + * Will we include snippets in responses? + * @var bool + */ + protected $snippets = false; + + /** + * Constructor + * + * Sets up the Summon API Client + * + * @param string $apiId Summon API ID + * @param string $apiKey Summon API Key + * @param array $options Associative array of additional options; legal keys: + * <ul> + * <li>authedUser - is the end-user authenticated?</li> + * <li>debug - boolean to control debug mode</li> + * <li>host - base URL of Summon API</li> + * <li>sessionId - Summon session ID to apply</li> + * <li>version - API version to use</li> + * </ul> + */ + public function __construct($apiId, $apiKey, $options = array()) + { + $config = ConfigReader::getConfig('Summon'); + + // Store preferred boolean behavior: + if (!isset($options['caseSensitiveBooleans']) + && isset($config->General->case_sensitive_bools) + ) { + $this->caseSensitiveBooleans = $config->General->case_sensitive_bools; + } else { + $this->caseSensitiveBooleans = $options['caseSensitiveBooleans']; + } + + // Set default snippet behavior if necessary: + if (isset($config->General->snippets)) { + $this->snippets = $config->General->snippets; + } + + // Set default debug behavior: + if (!isset($options['debug'])) { + $options['debug'] = Logger::debugNeeded(); + } + + $timeout = isset($config->General->timeout) + ? $config->General->timeout : 30; + parent::__construct( + $apiId, $apiKey, $options, new HttpClient( + null, array('timeout' => $timeout) + ) + ); + } + + /** + * Print a message if debug is enabled. + * + * @param string $msg Message to print + * + * @return void + */ + protected function debugPrint($msg) + { + if ($this->debug) { + Logger::getInstance->debug("<pre>{$msg}</pre>\n"); + } + } + + /** + * 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; + } + + /** + * Build Query string from search parameters + * + * @param array $search An array of search parameters + * + * @return string The query + */ + public function buildQuery($search) + { + $groups = array(); + $excludes = array(); + if (is_array($search)) { + $query = ''; + + foreach ($search as $params) { + // Advanced Search + if (isset($params['group'])) { + $thisGroup = array(); + // Process each search group + foreach ($params['group'] as $group) { + // Build this group individually as a basic search + $thisGroup[] = $this->buildQuery(array($group)); + } + // Is this an exclusion (NOT) group or a normal group? + if ($params['group'][0]['bool'] == 'NOT') { + $excludes[] = join(" OR ", $thisGroup); + } else { + $groups[] = join( + " " . $params['group'][0]['bool'] . " ", $thisGroup + ); + } + } + + // Basic Search + if (isset($params['lookfor']) && $params['lookfor'] != '') { + $query .= $this->buildBasicQuery($params); + } + } + } + + // Put our advanced search together + if (count($groups) > 0) { + $query = "(" . join(") " . $search[0]['join'] . " (", $groups) . ")"; + } + // and concatenate exclusion after that + if (count($excludes) > 0) { + $query .= " NOT ((" . join(") OR (", $excludes) . "))"; + } + + // Ensure we have a valid query to this point + return isset($query) ? $query : ''; + } + + /** + * Perform normalization and analysis of Summon return value. + * + * @param array $input The raw response from Summon + * + * @throws SerialsSolutions_Summon_Exception + * @return array The processed response from Summon + */ + protected function process($input) + { + $result = parent::process($input); + + // Process highlighting/snippets: + foreach ($result['documents'] as $i => $current) { + // Remove snippets if not desired: + if (!$this->snippets) { + unset($result['documents'][$i]['Snippet']); + } + } + + return $result; + } +} diff --git a/module/VuFind/src/VuFind/Connection/Summon/Query.php b/module/VuFind/src/VuFind/Connection/Summon/Query.php new file mode 100644 index 00000000000..0a5aad6ca93 --- /dev/null +++ b/module/VuFind/src/VuFind/Connection/Summon/Query.php @@ -0,0 +1,123 @@ +<?php +/** + * Summon Search API Interface (query model - VuFind implementation) + * + * PHP version 5 + * + * Copyright (C) Serials Solutions 2011. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category SerialsSolutions + * @package Summon + * @author Andrew Nagy <andrew.nagy@serialssolutions.com> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://api.summon.serialssolutions.com/help/api/ API Documentation + */ +namespace VuFind\Connection\Summon; + +/** + * Summon REST API Interface (query model - VuFind implementation) + * + * @category SerialsSolutions + * @package Summon + * @author Andrew Nagy <andrew.nagy@serialssolutions.com> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://api.summon.serialssolutions.com/help/api/ API Documentation + */ +class Query extends SerialsSolutions_Summon_Query +{ + protected $config; + + /** + * Constructor + * + * Sets up the Summon API Client + * + * @param string $query Search query + * @param array $options Other options to set (associative array) + */ + public function __construct($query = null, $options = array()) + { + parent::__construct($query, $options); + $this->config = VF_Config_Reader::getConfig('Summon'); + } + + /** + * Set up facets based on VuFind settings. + * + * @param array $facets Facet settings + * + * @return void + */ + public function initFacets($facets) + { + $this->facets = array(); + foreach ($facets as $facet) { + // See if parameters are included as part of the facet name; + // if not, override them with defaults. + $parts = explode(',', $facet); + $facetName = $parts[0]; + $facetMode = isset($parts[1]) ? $parts[1] : 'and'; + $facetPage = isset($parts[2]) ? $parts[2] : 1; + if (isset($parts[3])) { + $facetLimit = $parts[3]; + } else { + $facetLimit = isset($this->config->Facet_Settings->facet_limit) + ? $this->config->Facet_Settings->facet_limit : 30; + } + $facetParams = "{$facetMode},{$facetPage},{$facetLimit}"; + $this->facets[] = "{$facetName},{$facetParams}"; + } + } + + /** + * 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 = VF_Solr_Utils::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}"); + } + } + } + } + } +} diff --git a/vendor/SerialsSolutions/Summon/Zend2.php b/vendor/SerialsSolutions/Summon/Zend2.php new file mode 100644 index 00000000000..c029e4d38fc --- /dev/null +++ b/vendor/SerialsSolutions/Summon/Zend2.php @@ -0,0 +1,124 @@ +<?php +/** + * Summon Search API Interface (Zend Framework 2 implementation) + * + * PHP version 5 + * + * Copyright (C) Serials Solutions 2011. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category SerialsSolutions + * @package Summon + * @author Andrew Nagy <andrew.nagy@serialssolutions.com> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://api.summon.serialssolutions.com/help/api/ API Documentation + */ +namespace SerialsSolutions\Summon; +use Zend\Http\Client as HttpClient; + +/** + * Summon Search API Interface (Zend Framework 2 implementation) + * + * @category SerialsSolutions + * @package Summon + * @author Andrew Nagy <andrew.nagy@serialssolutions.com> + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link http://api.summon.serialssolutions.com/help/api/ API Documentation + */ +class Zend2 extends SerialsSolutions_Summon_Base +{ + /** + * The HTTP_Request object used for API transactions + * @var object HTTP_Request + */ + protected $client; + + /** + * Constructor + * + * Sets up the Summon API Client + * + * @param string $apiId Summon API ID + * @param string $apiKey Summon API Key + * @param array $options Associative array of additional options; + * legal keys: + * <ul> + * <li>authedUser - is the end-user authenticated?</li> + * <li>debug - boolean to control debug mode</li> + * <li>host - base URL of Summon API</li> + * <li>sessionId - Summon session ID to apply</li> + * <li>version - API version to use</li> + * </ul> + * @param Zend_Http_Client $client HTTP client object (optional) + */ + public function __construct($apiId, $apiKey, $options = array(), $client = null) + { + parent::__construct($apiId, $apiKey, $options); + $this->client = is_object($client) ? $client : new HttpClient(); + } + + /** + * Handle a fatal error. + * + * @param SerialsSolutions_Summon_Exception $e Exception to process. + * + * @return void + */ + public function handleFatalError($e) + { + throw $e; + } + + /** + * Perform an HTTP request. + * + * @param string $baseUrl Base URL for request + * @param string $method HTTP method for request + * @param string $queryString Query string to append to URL + * @param array $headers HTTP headers to send + * + * @throws SerialsSolutions_Summon_Exception + * @return string HTTP response body + */ + protected function httpRequest($baseUrl, $method, $queryString, $headers) + { + $this->debugPrint( + "{$method}: {$baseUrl}?{$queryString}" + ); + + $this->client->resetParameters(); + if ($method == 'GET') { + $baseUrl .= '?' . $queryString; + } elseif ($method == 'POST') { + $this->client->setRawBody( + $queryString, 'application/x-www-form-urlencoded' + ); + } + + foreach ($headers as $key => $value) { + $this->client->setHeaders($key, $value); + } + + // Send Request + $this->client->setUri($baseUrl); + $result = $this->client->setMethod($method)->send(); + if (!$result->isSuccess()) { + throw new SerialsSolutions_Summon_Exception($result->getBody()); + } + return $result->getBody(); + } +} -- GitLab