Subversion Repositories Web Services

Compare Revisions

Ignore whitespace Rev 118294 → Rev 118297

/class_lib/trunk/webServiceServer_class.php
17,7 → 17,7
*
* You should have received a copy of the GNU Affero General Public License
* along with Open Library System. If not, see <http://www.gnu.org/licenses/>.
*/
*/
 
 
/** \brief Webservice server
37,6 → 37,9
require_once('OLS_class_lib/xmlconvert_class.php');
require_once('OLS_class_lib/objconvert_class.php');
 
/**
* Class webServiceServer
*/
abstract class webServiceServer {
 
protected $config; ///< inifile object
48,11 → 51,11
protected $xmlconvert; ///< xml to OLS object convert
protected $xmlns; ///< namespaces and prefixes
protected $default_namespace; ///< -
protected $tag_sequence; /**< tag sequence according to XSD or noame of XSD */
protected $tag_sequence; ///< tag sequence according to XSD or noame of XSD
protected $soap_action; ///< -
protected $dump_timer; ///< -
protected $dump_timer_ip; ///< -
protected $output_type=''; ///< -
protected $output_type = ''; ///< -
protected $curl_recorder; ///< -
protected $debug; ///< -
protected $url_override; ///< array with special url-commands for the actual service
60,15 → 63,15
 
/** \brief Webservice constructer
*
* @param $inifile string
* @param $inifile string
*
*/
public function __construct($inifile) {
public function __construct($inifile) {
// initialize config and verbose objects
$this->config = new inifile($inifile);
 
if ($this->config->error) {
die('Error: '.$this->config->error );
die('Error: ' . $this->config->error);
}
 
// service closed
89,11 → 92,11
$this->version = $this->config->get_value('version', 'setup');
verbose::open($this->config->get_value('logfile', 'setup'),
$this->config->get_value('verbose', 'setup'),
str_replace('_VERSION_', $this->version , $this->config->get_value('syslog_id', 'setup')));
str_replace('_VERSION_', $this->version, $this->config->get_value('syslog_id', 'setup')));
$this->watch = new stopwatch('', ' ', '', '%s:%01.3f');
 
if ($this->config->get_value('xmldir'))
$this->xmldir=$this->config->get_value('xmldir');
$this->xmldir = $this->config->get_value('xmldir');
$this->xmlns = $this->config->get_value('xmlns', 'setup');
$this->default_namespace = $this->xmlns[$this->config->get_value('default_namespace_prefix', 'setup')];
$this->tag_sequence = $this->config->get_value('tag_sequence', 'setup');
113,19 → 116,21
$this->aaa = new aaa($this->config->get_section('aaa'));
}
 
public function __destruct() { }
public function __destruct() {
}
 
/** \brief Handles request from webservice client
*
*/
*
* @return mixed
*/
public function handle_request() {
foreach ($this->url_override as $query_par => $function_name) {
if (strpos($_SERVER['QUERY_STRING'], $query_par) === 0 && method_exists($this, $function_name)) {
return $this-> {$function_name}();
return $this->{$function_name}();
}
}
if (isset($_POST['xml'])) {
$xml=trim(stripslashes($_POST['xml']));
$xml = trim(stripslashes($_POST['xml']));
self::soap_request($xml);
}
elseif (!empty($GLOBALS['HTTP_RAW_POST_DATA'])) {
132,7 → 137,7
self::soap_request($GLOBALS['HTTP_RAW_POST_DATA']);
}
// pjo 11/9/17 for php 7. @see http://php.net/manual/en/reserved.variables.httprawpostdata.php.
elseif($xml=file_get_contents("php://input")){
elseif ($xml = file_get_contents("php://input")) {
self::soap_request($xml);
}
elseif (!empty($_SERVER['QUERY_STRING']) && ($_REQUEST['action'] || $_REQUEST['json'])) {
145,8 → 150,9
self::rest_request();
}
elseif (self::in_house()
|| $this->config->get_value('show_samples', 'setup')
|| ip_func::ip_in_interval($_SERVER['REMOTE_ADDR'], $this->config->get_value('show_samples_ip_list', 'setup'))) {
|| $this->config->get_value('show_samples', 'setup')
|| ip_func::ip_in_interval($_SERVER['REMOTE_ADDR'], $this->config->get_value('show_samples_ip_list', 'setup'))
) {
self::create_sample_forms();
}
else {
155,9 → 161,9
}
 
/** \brief Handles and validates soap request
*
* @param $xml string
*/
*
* @param $xml string
*/
private function soap_request($xml) {
// Debug verbose::log(TRACE, 'Request ' . $xml);
 
165,12 → 171,12
$this->validate = $this->config->get_value('validate');
 
if ($this->validate['soap_request'] || $this->validate['request'])
$error = ! self::validate_soap($xml, $this->validate, 'request');
$error = !self::validate_soap($xml, $this->validate, 'request');
 
if (empty($error)) {
// parse to object
$this->xmlconvert=new xmlconvert();
$xmlobj=$this->xmlconvert->soap2obj($xml);
$this->xmlconvert = new xmlconvert();
$xmlobj = $this->xmlconvert->soap2obj($xml);
// soap envelope?
if ($xmlobj->Envelope) {
$request_xmlobj = &$xmlobj->Envelope->_value->Body->_value;
192,7 → 198,7
// validate response
if ($this->validate['soap_response'] || $this->validate['response']) {
$response_xml = $this->objconvert->obj2soap($response_xmlobj, $soap_namespace);
$error = ! self::validate_soap($response_xml, $this->validate, 'response');
$error = !self::validate_soap($response_xml, $this->validate, 'response');
}
 
if (empty($error)) {
219,7 → 225,7
break;
default:
if (empty($response_xml))
$response_xml = $this->objconvert->obj2soap($response_xmlobj, $soap_namespace);
$response_xml = $this->objconvert->obj2soap($response_xmlobj, $soap_namespace);
if ($soap_namespace == 'http://www.w3.org/2003/05/soap-envelope' && empty($_POST['xml']))
header('Content-Type: application/soap+xml'); // soap 1.2
else
228,7 → 234,7
}
// request done and response send, dump timer
if ($this->dump_timer)
verbose::log(TIMER, sprintf($this->dump_timer, $this->soap_action) . ':: ' . $this->dump_timer_ip . $this->watch->dump());
verbose::log(TIMER, sprintf($this->dump_timer, $this->soap_action) . ':: ' . $this->dump_timer_ip . $this->watch->dump());
}
else
self::soap_error('Error in response validation.');
262,8 → 268,8
}
 
/** \brief Handles rest request, converts it to xml and calls soap_request()
*
*/
*
*/
private function rest_request() {
// convert to soap
if ($_REQUEST['json']) {
278,8 → 284,9
}
 
/** \brief Show the service version
*
*/
*
* @param $operation
*/
private function update_registry($operation) {
$registry = $this->config->get_section('service_registry');
if ($registry && $registry['registry']) {
291,15 → 298,17
}
 
/** \brief Show the service version
*
*/
*
* @noinspection PhpUnusedPrivateMethodInspection
*/
private function version() {
die($this->version);
}
 
/** \brief Show wsdl file for the service replacing __LOCATION__ with ini-file setting or current location
*
*/
*
* @noinspection PhpUnusedPrivateMethodInspection
*/
private function Wsdl() {
if ($wsdl = $this->config->get_value('wsdl', 'setup')) {
if (!$location = $this->config->get_value('service_location', 'setup')) {
308,7 → 317,8
if (strpos($location, '://') < 1) {
if (!empty($_SERVER['HTTPS']) || ($_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https')) {
$protocol = 'https://';
} else {
}
else {
$protocol = 'http://';
}
}
325,25 → 335,27
}
}
 
/** \brief Show selected parts of the ini-file
*
*/
/** \brief Show selected parts of the ini-file
*
* @noinspection PhpUnusedPrivateMethodInspection
*/
private function ShowInfo() {
if (($showinfo = $this->config->get_value('showinfo', 'showinfo')) && self::in_house()) {
foreach ($showinfo as $line) {
echo self::showinfo_line($line) . "\n";
}
die();
die();
}
}
 
/** \brief expands __var__ to the corresponding setting
*
* @param $line string
*/
*
* @param $line string
* @return mixed|string
*/
private function showinfo_line($line) {
while (($s = strpos($line, '__')) !== FALSE) {
$line = substr($line, 0, $s) . substr($line, $s+2);
$line = substr($line, 0, $s) . substr($line, $s + 2);
if (($e = strpos($line, '__')) !== FALSE) {
$var = substr($line, $s, $e - $s);
list($key, $section) = explode('.', $var, 2);
358,10 → 370,11
}
 
/** \brief Helper function to showinfo_line()
*
* @param $arr array
* @param $prefix string
*/
*
* @param $arr array
* @param $prefix string
* @return mixed
*/
private function implode_ini_array($arr, $prefix = '') {
$ret = "\n";
foreach ($arr as $key => $val) {
374,9 → 387,9
}
 
/** \brief
* Return TRUE if the IP is in in_house_ip_list, or if in_house_ip_list is not set, if the name is in in_house_domain
* NB: gethostbyaddr() can take some time or even time out, is the remote name server is slow or wrongly configured
*/
* Return TRUE if the IP is in in_house_ip_list, or if in_house_ip_list is not set, if the name is in in_house_domain
* NB: gethostbyaddr() can take some time or even time out, is the remote name server is slow or wrongly configured
*/
protected function in_house() {
static $homie;
if (!isset($homie)) {
400,18 → 413,19
}
 
/** \brief RegressionTest tests the webservice
*
* @param $arg string
*/
private function RegressionTest($arg='') {
if (! is_dir($this->xmldir.'/regression'))
*
* @param $arg string
*
* @noinspection PhpUnusedPrivateMethodInspection
*/
private function RegressionTest($arg = '') {
if (!is_dir($this->xmldir . '/regression'))
die('No regression catalouge');
 
if ($dh = opendir($this->xmldir.'/regression')) {
chdir($this->xmldir.'/regression');
$reqs = array();
if ($dh = opendir($this->xmldir . '/regression')) {
chdir($this->xmldir . '/regression');
while (($file = readdir($dh)) !== false)
if (!is_dir($file) && preg_match('/xml$/',$file,$matches))
if (!is_dir($file) && preg_match('/xml$/', $file, $matches))
$fnames[] = $file;
if (count($fnames)) {
asort($fnames);
428,15 → 442,17
die('No files found for regression test');
}
else
die('Cannot open regression catalouge: ' . $this->xmldir.'/regression');
die('Cannot open regression catalouge: ' . $this->xmldir . '/regression');
}
 
/** \brief HowRU tests the webservice and answers "Gr8" if none of the tests fail. The test cases resides in the inifile.
*
* Handles zero or more set of tests.
* Each set, can contain one or more tests, where just one of them has to succeed
* If all tests in a given set fails, the corresponding error will be displayed
*/
*
* Handles zero or more set of tests.
* Each set, can contain one or more tests, where just one of them has to succeed
* If all tests in a given set fails, the corresponding error will be displayed
*
* @noinspection PhpUnusedPrivateMethodInspection
*/
private function HowRU() {
$tests = $this->config->get_value('test', 'howru');
if ($tests) {
444,11 → 460,11
$reg_matchs = $this->config->get_value('preg_match', 'howru');
$reg_errors = $this->config->get_value('error', 'howru');
if (!$server_name = $this->config->get_value('server_name', 'howru')) {
if (!$server_name = $_SERVER['SERVER_NAME']) {
$server_name = $_SERVER['HTTP_HOST'];
}
if (!$server_name = $_SERVER['SERVER_NAME']) {
$server_name = $_SERVER['HTTP_HOST'];
}
}
$url = $server_name. $_SERVER['PHP_SELF'];
$url = $server_name . $_SERVER['PHP_SELF'];
if ($_SERVER['HTTPS'] == 'on') $url = 'https://' . $url;
foreach ($tests as $i_test => $test) {
if (is_array($test)) {
460,9 → 476,9
}
$error = $reg_errors[$i_test];
foreach ($test as $i => $t) {
$reply=$curl->get($url.'?action='.$t);
$reply = $curl->get($url . '?action=' . $t);
$preg_match = $reg_match[$i];
if (preg_match("/$preg_match/",$reply)) {
if (preg_match("/$preg_match/", $reply)) {
unset($error);
break;
}
476,23 → 492,23
}
 
/** \brief Validates soap and xml
*
* @param $soap string
* @param $schemas array
* @param $validate_schema string
*
*/
 
*
* @param $soap string
* @param $schemas array
* @param $validate_schema string
* @return bool
*/
protected function validate_soap($soap, $schemas, $validate_schema) {
$validate_soap = new DomDocument;
$validate_soap->preserveWhiteSpace = FALSE;
@ $validate_soap->loadXml($soap);
if (($sc = $schemas['soap_'.$validate_schema]) && ! @ $validate_soap->schemaValidate($sc))
if (($sc = $schemas['soap_' . $validate_schema]) && !@ $validate_soap->schemaValidate($sc))
return FALSE;
 
if ($sc = $schemas[$validate_schema]) {
if ($validate_soap->firstChild->localName == 'Envelope'
&& $validate_soap->firstChild->hasChildNodes()) {
&& $validate_soap->firstChild->hasChildNodes()
) {
foreach ($validate_soap->firstChild->childNodes as $soap_node) {
if ($soap_node->localName == 'Body') {
$xml = &$soap_node->firstChild;
505,7 → 521,7
if (empty($validate_xml))
$validate_xml = &$validate_soap;
 
if (! @ $validate_xml->schemaValidate($sc))
if (!@ $validate_xml->schemaValidate($sc))
return FALSE;
}
 
513,10 → 529,10
}
 
/** \brief send an error header and soap fault
*
* @param $err string
*
*/
*
* @param $err string
*
*/
protected function soap_error($err) {
$elevel = array(LIBXML_ERR_WARNING => "\n Warning",
LIBXML_ERR_ERROR => "\n Error",
523,8 → 539,8
LIBXML_ERR_FATAL => "\n Fatal");
if ($errors = libxml_get_errors()) {
foreach ($errors as $error) {
$xml_err .= $elevel[$error->level] . ": " . trim($error->message) .
($error->file ? " in file " . $error->file : " on line " . $error->line);
$xml_err .= $elevel[$error->level] . ": " . trim($error->message) .
($error->file ? " in file " . $error->file : " on line " . $error->line);
}
}
header('HTTP/1.0 400 Bad Request');
540,14 → 556,13
}
 
/** \brief Validates xml
*
* @param $xml string
* @param $schema_filename string
* @param $resolve_externals boolean
*
*/
 
protected function validate_xml($xml, $schema_filename, $resolve_externals=FALSE) {
*
* @param $xml string
* @param $schema_filename string
* @param $resolve_externals boolean
* @return bool
*/
protected function validate_xml($xml, $schema_filename, $resolve_externals = FALSE) {
$validateXml = new DomDocument;
$validateXml->resolveExternals = $resolve_externals;
$validateXml->loadXml($xml);
560,20 → 575,19
}
 
/** \brief Find operation in object created from xml and and calls this function defined by developer in extended class.
* Authentication is by default found in the authentication node, in userIdAut, groupIdAut and passwordAut
* These names can be changed by doing so in the aaa-section, like:
* userIdAut = theNameOfUserIdInThisService
*
* @param $xmlobj object
* @retval object - from the service entry point called
*
*/
* Authentication is by default found in the authentication node, in userIdAut, groupIdAut and passwordAut
* These names can be changed by doing so in the aaa-section, like:
* userIdAut = theNameOfUserIdInThisService
*
* @param $xmlobj object
* @return bool - from the service entry point called
*/
private function call_xmlobj_function($xmlobj) {
if ($xmlobj) {
$soapActions = $this->config->get_value('soapAction', 'setup');
$request=key($xmlobj);
$request = key($xmlobj);
if ($this->soap_action = array_search($request, $soapActions)) {
$params=$xmlobj->$request->_value;
$params = $xmlobj->$request->_value;
if (method_exists($this, $this->soap_action)) {
if (is_object($this->aaa)) {
foreach (array('authentication', 'userIdAut', 'groupIdAut', 'passwordAut') as $par) {
589,7 → 603,7
}
verbose::set_tracking_id($this->config->get_value('default_namespace_prefix', 'setup'), $params->trackingId->_value);
self::update_registry($this->soap_action);
return $this-> {$this->soap_action}($params);
return $this->{$this->soap_action}($params);
}
}
}
598,9 → 612,9
}
 
/** \brief Create sample form for testing webservice. This is called of no request is send via browser.
*
*
*/
*
*
*/
 
private function create_sample_forms() {
if ($sample_header = $this->config->get_value('sample_header', 'setup')) {
609,17 → 623,17
else {
$sample_header = 'Content-type: text/html; charset=utf-8';
}
header ($sample_header);
header($sample_header);
 
// Open a known directory, and proceed to read its contents
if (is_dir($this->xmldir.'/request')) {
if ($dh = opendir($this->xmldir.'/request')) {
chdir($this->xmldir.'/request');
if (is_dir($this->xmldir . '/request')) {
if ($dh = opendir($this->xmldir . '/request')) {
chdir($this->xmldir . '/request');
$fnames = $reqs = array();
while (($file = readdir($dh)) !== false) {
if (!is_dir($file)) {
if (preg_match('/html$/',$file,$matches)) $info = file_get_contents($file);
if (preg_match('/xml$/',$file,$matches)) $fnames[] = $file;
if (preg_match('/html$/', $file, $matches)) $info = file_get_contents($file);
if (preg_match('/xml$/', $file, $matches)) $fnames[] = $file;
}
}
closedir($dh);
630,35 → 644,38
natsort($fnames);
foreach ($fnames as $fname) {
$contents = str_replace("\r\n", PHP_EOL, file_get_contents($fname));
$contents=addcslashes(str_replace("\n",'\n',$contents), '"');
$reqs[]=$contents;
$names[]=$fname;
$contents = addcslashes(str_replace("\n", '\n', $contents), '"');
$reqs[] = $contents;
$names[] = $fname;
}
 
foreach ($reqs as $key => $req)
$options .= '<option value="' . $key . '">'.$names[$key].'</option>' . "\n";
$options .= '<option value="' . $key . '">' . $names[$key] . '</option>' . "\n";
if ($_GET['debug'] && self::in_house())
$debug = '<input type="hidden" name="debug" value="' . $_GET['debug'] . '">';
 
$html = str_replace('__REQS__', implode("\",\n\"", $reqs), $html);
$html = str_replace('__XML__', htmlspecialchars($_REQUEST['xml']), $html);
$html = str_replace('__OPTIONS__', $options, $html);
$html = str_replace('__REQS__', implode("\",\n\"", $reqs), $html);
$html = str_replace('__XML__', htmlspecialchars($_REQUEST['xml']), $html);
$html = str_replace('__OPTIONS__', $options, $html);
}
else {
$error = 'No example xml files found...';
}
$html = str_replace('__ERROR__', $error, $html);
$html = str_replace('__DEBUG__', $debug, $html);
$html = str_replace('__HEADER_WARNING__', $header_warning, $html);
$html = str_replace('__VERSION__', $this->version, $html);
$html = str_replace('__ERROR__', $error, $html);
$html = str_replace('__DEBUG__', $debug, $html);
$html = str_replace('__HEADER_WARNING__', $header_warning, $html);
$html = str_replace('__VERSION__', $this->version, $html);
}
}
echo $html;
}
 
/**
* @return string
*/
private function sample_form() {
return
'<html><head>
return
'<html><head>
__HEADER_WARNING__
<script language="javascript">
var reqs = Array("__REQS__");
681,5 → 698,3
}
 
}
 
?>
/class_lib/trunk/xmlconvert_class.php
17,7 → 17,7
*
* You should have received a copy of the GNU Affero General Public License
* along with Open Library System. If not, see <http://www.gnu.org/licenses/>.
*/
*/
 
 
/**
25,31 → 25,37
*
* @author Finn Stausgaard - DBC
*/
 
class xmlconvert {
 
/**
* xmlconvert constructor.
*/
public function __construct() {
}
 
/** \brief Create an ols--object out of SOAP xml
*
*
* @param $request
* @return bool|mixed
*/
public function soap2obj(&$request) {
if (empty($request)) return FALSE;
 
$dom = new DomDocument();
$dom->preserveWhiteSpace = FALSE;
if (@ $dom->loadXML($request))
return $this->xml2obj($dom);
if ($request) {
$dom = new DomDocument();
$dom->preserveWhiteSpace = FALSE;
if (@ $dom->loadXML($request))
return $this->xml2obj($dom);
}
return FALSE;
}
 
 
/** \brief Converts domdocument object to object.
*
*
*/
 
public function xml2obj($domobj, $force_NS='') {
*
* @param $domobj
* @param string $force_NS
* @return mixed
*/
public function xml2obj($domobj, $force_NS = '') {
foreach ($domobj->childNodes as $node) {
$subnode = new stdClass();
if ($node->nodeName == '#comment') {
/class_lib/trunk/solr_query_class.php
14,7 → 14,7
* along with Open Library System. If not, see <http://www.gnu.org/licenses/>.
*
* @author Finn Stausgaard - DBC
*/
*/
 
/**
* Parse a cql-search and return the corresponding solr-search
29,6 → 29,9
define('RENAME_HOLDINGS_AGENCYID_INDEX', 'rec.holdingsAgencyId');
 
 
/**
* Class SolrQuery
*/
class SolrQuery {
 
var $cql_dom; ///< -
48,43 → 51,44
var $error = ''; ///< -
var $cqlns = array(); ///< namespaces for the search-fields
var $v2_v3 = array( ///< -
'150005' => '150005-artikel',
'150008' => '150008-academic',
'150012' => '150012-leksikon',
'150014' => '150014-album',
'150015' => '870970-basis',
'150016' => '870971-forfweb',
'150017' => '870971-faktalink',
'150018' => '150018-danhist',
'150021' => '150021-bibliotek',
'150023' => '150023-sicref',
'150025' => '150008-public',
'150027' => '150021-fjern',
'150028' => '870970-basis',
'150030' => '870970-spilmedier',
'150032' => '150018-samfund',
'150033' => '150033-dandyr',
'150034' => '150018-religion',
'150039' => '150015-forlag',
'150040' => '150033-verdyr',
'150043' => '150043-atlas',
'150048' => '870970-basis',
'150052' => '870970-basis',
'150054' => '150018-biologi',
'150055' => '150018-fysikkemi',
'150056' => '150018-geografi',
'159002' => '159002-lokalbibl',
'870971' => '870971-avis',
'870973' => '870973-anmeld',
'870976' => '870976-anmeld'); ///< translate v2 rec.id to v3 rec.id
'150005' => '150005-artikel',
'150008' => '150008-academic',
'150012' => '150012-leksikon',
'150014' => '150014-album',
'150015' => '870970-basis',
'150016' => '870971-forfweb',
'150017' => '870971-faktalink',
'150018' => '150018-danhist',
'150021' => '150021-bibliotek',
'150023' => '150023-sicref',
'150025' => '150008-public',
'150027' => '150021-fjern',
'150028' => '870970-basis',
'150030' => '870970-spilmedier',
'150032' => '150018-samfund',
'150033' => '150033-dandyr',
'150034' => '150018-religion',
'150039' => '150015-forlag',
'150040' => '150033-verdyr',
'150043' => '150043-atlas',
'150048' => '870970-basis',
'150052' => '870970-basis',
'150054' => '150018-biologi',
'150055' => '150018-fysikkemi',
'150056' => '150018-geografi',
'159002' => '159002-lokalbibl',
'870971' => '870971-avis',
'870973' => '870973-anmeld',
'870976' => '870976-anmeld'); ///< translate v2 rec.id to v3 rec.id
 
/** \brief constructor
*
*
* @param $repository string
* @param $config string
* @param $language string
* @param $holdings_include string
*/
public function __construct($repository, $config='', $language='', $holdings_include='') {
public function __construct($repository, $config = '', $language = '', $holdings_include = '') {
$this->cql_dom = new DomDocument();
@ $this->cql_dom->loadXML($repository['cql_settings']);
 
91,7 → 95,7
$this->best_match = ($language == 'bestMatch');
// No boolean translate in strict cql
//if ($language == 'cqldan') {
//$this->operator_translate = array('og' => 'and', 'eller' => 'or', 'ikke' => 'not');
//$this->operator_translate = array('og' => 'and', 'eller' => 'or', 'ikke' => 'not');
//}
// not strict cql $this->set_operators($language);
$this->set_cqlns();
98,9 → 102,9
$this->set_indexes();
//$this->ignore = array('/^prox\//');
 
$this->interval = array('<' => '[* TO %s}',
'<=' => '[* TO %s]',
'>' => '{%s TO *]',
$this->interval = array('<' => '[* TO %s}',
'<=' => '[* TO %s]',
'>' => '{%s TO *]',
'>=' => '[%s TO *]');
 
if ($config) {
113,11 → 117,12
 
 
/** \brief Parse a cql-query and build the solr edismax search string
*
*
* @param $query string
* @retval struct
* @param $holdings_filter string
* @return struct
*/
public function parse($query, $holdings_filter='') {
public function parse($query, $holdings_filter = '') {
$this->holdings_filter = $holdings_filter;
$parser = new CQL_parser();
$parser->set_prefix_namespaces($this->cqlns);
148,7 → 153,7
 
/** \brief build a boost string
* @param $boosts array - boost registers and values
* @retval string
* @return string
*/
public function make_boost($boosts) {
if (is_array($boosts)) {
163,8 → 168,8
// ------------------------- Private functions below -------------------------------------
 
/** \brief locates usage of more than one holdingsItem fields
* @param @tree
* @retval boolean
* @param @tree
* @return boolean
*/
private function mixed_fields($tree) {
if ($tree['type'] == 'boolean') {
176,10 → 181,10
}
}
 
/** \brief Rename HOLDINGS_AGENCYID_INDEX if no other fields with identical prefix are used,
/** \brief Rename HOLDINGS_AGENCYID_INDEX if no other fields with identical prefix are used,
* if other fields are used, copy the tree and renamed the copied one
* @param @trees array - of tree
* @retval array
* @return array
*/
private function handle_holdingsitem_agency_id($trees) {
foreach ($trees as $idx => $tree) {
202,7 → 207,7
 
/** \brief convert all cql-trees to edismax-strings
* @param @trees array - of trees
* @retval struct
* @return array
*/
private function trees_2_edismax($trees) {
//var_dump($trees);
242,18 → 247,19
}
}
if ($this->holdings_filter && empty($found['holding'])) { // inject holdings filter when holding handler is not used
$solr_nodes['fq'][] = $this->holdings_filter;
$solr_nodes['handler']['fq'][] = 'holding';
self::apply_handler($solr_nodes, 'fq', 'holding');
$solr_nodes['fq'][] = $this->holdings_filter;
$solr_nodes['handler']['fq'][] = 'holding';
self::apply_handler($solr_nodes, 'fq', 'holding');
}
}
 
/** \brief locate handlers
/** \brief locate handlers
* @param $solr_nodes array - one or more solr AND nodes
* @param $type string - - q og fq
* @param $handler string - - name of handler
* @param $holdings_filter string -
*/
private function apply_handler(&$solr_nodes, $type, $handler, $holdings_filter='') {
private function apply_handler(&$solr_nodes, $type, $handler, $holdings_filter = '') {
if ($handler && $format = $this->search_term_format[$handler][$type]) {
$q = array();
foreach ($solr_nodes['handler'][$type] as $idx => $h) {
275,7 → 281,7
/** \brief convert on cql-tree to edismax-string
* @param $node array - of tree
* @param $level integer - level of recursion
* @retval array - The term, the associated search handler and the query type (q or fq)
* @return array - The term, the associated search handler and the query type (q or fq)
*/
private function tree_2_edismax($node, $level = 0) {
static $q_type, $ranking;
284,9 → 290,9
$ranking = '';
}
if ($node['type'] == 'boolean') {
list($left_term, $left_handler) = self::tree_2_edismax($node['left'], $level+1);
list($right_term, $right_handler) = self::tree_2_edismax($node['right'], $level+1);
$ret = '(' . $left_term . ' ' . strtoupper($node['op']) . ' ' . $right_term . ')';
list($left_term, $left_handler) = self::tree_2_edismax($node['left'], $level + 1);
list($right_term, $right_handler) = self::tree_2_edismax($node['right'], $level + 1);
$ret = '(' . $left_term . ' ' . strtoupper($node['op']) . ' ' . $right_term . ')';
if ($left_handler == $right_handler) {
$term_handler = $right_handler;
}
298,7 → 304,7
if (!self::is_filter_field($node['prefix'], $node['field'])) {
$q_type = 'q';
}
$term_handler = self::get_term_handler($q_type, $node['prefix'], $node['field']);
$term_handler = self::get_term_handler($node['prefix'], $node['field']);
$ret = self::make_solr_term($node['term'], $node['relation'], $node['prefix'], $node['field'], self::set_slop($node));
}
$ranking = self::use_rank($node, $ranking);
307,7 → 313,7
 
/** \brief modifies slop if word or string modifier is used
* @param $node array - modifiers and slop for the node
* @retval string
* @return string
*/
private function set_slop($node) {
if ($node['relation'] == 'adj') return '0';
319,7 → 325,7
/** \brief if relation modifier "relevant" is used, creates ranking info
* @param $node array
* @param $ranking string
* $retval string
* @return string
*/
private function use_rank($node, $ranking) {
if (!empty($node['modifiers']['relevant'])) {
335,20 → 341,20
 
/** \brief Set an error to send back to the client
* @param $no integer
* @param $desc string
* @retval array - diagnostic structure
* @param $details string
* @return array - diagnostic structure
*/
private function set_error($no, $details = '') {
/* Total list at: http://www.loc.gov/standards/sru/diagnostics/diagnosticsList.html */
static $message =
/* Total list at: http://www.loc.gov/standards/sru/diagnostics/diagnosticsList.html */
static $message =
array(18 => 'Unsupported combination of indexes',
21 => 'Unsupported combination of relation modifers');
return array('no' => $no, 'description' => $message[$no], 'details' => $details); // pos is not defined
return array('no' => $no, 'description' => $message[$no], 'details' => $details); // pos is not defined
}
 
/** \brief convert all cql-trees to edismax-bestmatch-strings and set sort-scoring
* @param $trees array - of trees
* @retval array -
* @return array -
*/
private function trees_2_bestmatch($trees) {
foreach ($trees as $tree) {
362,11 → 368,11
/** \brief convert all cql-trees to edismax-bestmatch-strings and set sort-scoring
* @param $node array - of trees
* @param $level integer - level of recursion
* @retval array -
* @return array -
*/
private function tree_2_bestmatch($node, $level = 0) {
if ($node['type'] == 'boolean') {
$ret = self::tree_2_bestmatch($node['left'], $level+1) . ' or ' . self::tree_2_bestmatch($node['right'], $level+1);
$ret = self::tree_2_bestmatch($node['left'], $level + 1) . ' or ' . self::tree_2_bestmatch($node['right'], $level + 1);
}
else {
$ret = self::make_bestmatch_term($node['term'], $node['relation'], $node['prefix'], $node['field'], $node['slop']);
376,7 → 382,7
 
/** \brief convert all cql-trees to edismax-bestmatch-strings and set sort-scoring
* @param $trees array - of tree
* @retval array -
* @return array -
*/
private function trees_2_operands($trees) {
$ret = array();
385,13 → 391,13
}
return $ret;
}
 
/** \brief convert all cql-trees to edismax-bestmatch-strings and set sort-scoring
* @param $node array -
* @param $level integer - level of recursion
* @retval array -
* @param $node array -
* @return array -
*/
private function tree_2_operands($node, $level = 0) {
static $ret;
private function tree_2_operands($node) {
static $ret;
if ($node['type'] == 'boolean') {
return array_merge(self::tree_2_operands($node['left']), self::tree_2_operands($node['right']));
}
403,7 → 409,7
/** \brief builds: t1 = term1, t1 = term2 to be used as extra solr-parameters referenced fom the sort-parameter
* like "sum(query($t1, 25),query($t2,25),query($t3,25),query($t4,25)) asc" for 4 terms
* @param $query string
* @retval array -
* @return array -
*/
private function make_bestmatch_sort($query) {
$qs = explode(' or ', $query);
425,7 → 431,7
* @param $prefix string
* @param $field string
* @param $slop integer
* @retval string -
* @return string -
*/
private function make_bestmatch_term($term, $relation, $prefix, $field, $slop) {
$ret = array();
447,7 → 453,7
* @param $prefix string
* @param $field string
* @param $slop integer
* @retval string -
* @return string -
*/
private function make_solr_term($term, $relation, $prefix, $field, $slop) {
$quote = self::is_quoted($term);
459,46 → 465,46
if ($field && ($field <> 'serverChoice')) {
$m_field = self::join_prefix_and_field($prefix, $field, ':');
}
if (in_array($prefix, $this->phrase_index)) {
if ($quote) {
if (in_array($prefix, $this->phrase_index)) {
if ($quote) {
$term = self::delete_quotes(self::escape_solr_quoted_term($term), $quote);
}
if ($space) {
}
if ($space) {
$term = str_replace(' ', '\\ ', $term);
}
if (!$m_term = self::make_term_interval($term, $relation, $quote)) {
}
if (!$m_term = self::make_term_interval($term, $relation, $quote)) {
$m_term = $term;
}
}
else {
if (!$m_term = self::make_term_interval($term, $relation, $quote)) {
}
}
else {
if (!$m_term = self::make_term_interval($term, $relation, $quote)) {
$m_slop = '';
if ($space) {
if ($relation == 'any') {
if ($space) {
if ($relation == 'any') {
$term = '(' . preg_replace('/\s+/', ' OR ', self::delete_quotes($term, $quote)) . ')';
}
elseif ($relation == 'all') {
}
elseif ($relation == 'all') {
$term = '(' . preg_replace('/\s+/', ' AND ', self::delete_quotes($term, $quote)) . ')';
}
elseif ($wildcard && $quote) {
}
elseif ($wildcard && $quote) {
$term = '(' . self::delete_quotes($term, $quote) . ')';
}
else {
}
else {
$m_slop = '~' . $slop;
}
}
elseif ($quote) {
}
}
elseif ($quote) {
$term = self::delete_quotes($term, $quote);
}
}
$m_term = self::escape_solr_term($term) . $m_slop;
}
}
return $m_field . $m_term;
}
}
return $m_field . $m_term;
}
 
/** \brief Return the quote used or empty string (FALSE)
* @param $str string
* @retval mixed - the quote or empty string
* @return mixed - the quote or empty string
*/
private function is_quoted($str) {
foreach (array('"', "'") as $ch) {
513,7 → 519,7
/** \brief remove unescaped quotes from string
* @param $str string
* @param $quote character (' or ")
* @retval string
* @return string
*/
private function delete_quotes($str, $quote) {
static $US = '\037';
525,26 → 531,27
/** \brief remove first and last quote from string - not used
* @param $str string
* @param $quote character (' or ")
* @retval string
*/
* @return string
// this one trims as well, and performs many tests
private function delete_first_and_last_quote($str, $quote) {
$first = strpos($str, $quote);
$last = strrpos($str, $quote);
if ($first !== FALSE &&
$first < $last &&
($first == 0 || trim(substr($str, 0, $first)) == '') &&
(trim(substr($str, ($last + 1))) == '') &&
(substr($str, ($last - 1), 1) != '\\')) {
$first < $last &&
($first == 0 || trim(substr($str, 0, $first)) == '') &&
(trim(substr($str, ($last + 1))) == '') &&
(substr($str, ($last - 1), 1) != '\\')
) {
return substr(substr($str, 0, $last), ($first + 1));
}
return $str;
}
*/
 
 
/** \brief Return TRUE if * or ? is used as wildcard
* @param $str string
* @retval boolean
* @return boolean
*/
private function has_wildcard($str) {
for ($i = 0; $i < strlen($str); $i++) {
560,16 → 567,16
 
/** \brief Normalize spaces in term. Remove multiple spaces and space next to $quote
* @param $term string
* @param $quote char
* @retval string
* @param $quote string
* @return string
*/
private function normalize_term($term, $quote='') {
private function normalize_term($term, $quote = '') {
return preg_replace('/(^' . $quote . ' )|( ' . $quote . '$)/', $quote, preg_replace('/\s\s+/', ' ', trim($term)));
}
 
/** \brief Create full search code from a search tree node
* @param $node array
* @retval string
* @return string
*/
private function node_2_index($node) {
return self::join_prefix_and_field($node['prefix'], $node['field']);
578,7 → 585,8
/** \brief Create full search code
* @param $prefix string
* @param $field string
* @retval string
* @param $colon string
* @return string
*/
private function join_prefix_and_field($prefix, $field, $colon = '') {
if ($prefix == 'cql' && $field == 'keywords') {
593,7 → 601,7
* @param $term string
* @param $prefix string
* @param $field string
* @retval string
* @return string
*/
private function convert_old_recid($term, $prefix, $field) {
if ($prefix == 'rec' && $field == 'id' && preg_match("/^([1-9][0-9]{5})(:.*)$/", $term, $match)) {
605,8 → 613,8
}
 
/** \brief Escape character and remove characters to be ignored. And escape ( and )
* @param $phrase string
* @retval string
* @param $term string
* @return string
*/
private function escape_solr_quoted_term($term) {
static $from = array('(', ')');
617,7 → 625,7
 
/** \brief Escape character and remove characters to be ignored
* @param $term string
* @retval string
* @return string
*/
private function escape_solr_term($term) {
static $solr_escapes_from;
639,7 → 647,7
* @param $term string
* @param $relation string
* @param $quot char
* @retval string
* @return string
*/
private function make_term_interval($term, $relation, $quot) {
if (($interval = $this->interval[$relation])) {
649,18 → 657,10
return NULL;
}
 
/** \brief Detects if a term is a date
* @param $term string
* @retval mixed - integer/boolean
*/
private function is_date($term) {
return strtotime($term);
}
 
/** \brief Detects fields which can go into solrs fq=
* @param $prefix string
* @param $field string
* @retval boolean
* @return boolean
*/
private function is_filter_field($prefix, $field) {
return $this->indexes[$field][$prefix]['filter'];
667,18 → 667,17
}
 
/** \brief gets the handler for the term - depending od the search handler being used
* @param $q_type string - q for normal search and fq for the filter query
* @param $prefix string
* @param $field string
* @retval string - the name of the handler or ''
* @return string - the name of the handler or ''
*/
private function get_term_handler($q_type, $prefix, $field) {
private function get_term_handler($prefix, $field) {
return $this->indexes[$field][$prefix]['handler'];
}
 
/** \brief Split cql tree into several and-trees
* @param $tree array
* @retval array - of tree
* @return array - of tree
*/
private function split_tree($tree) {
if ($tree['type'] == 'boolean' && strtolower($tree['op']) == 'and') {
687,7 → 686,7
else {
return array($tree);
}
 
}
 
/** \brief sets clq namespaces
714,18 → 713,18
if (NULL == ($handler = $name_item->getAttribute('searchHandler'))) {
//$handler = 'edismax';
}
$this->indexes[$name_item->nodeValue][$name_item->getAttribute('set')] = array('filter' => $filter,
'slop' => $slop,
$this->indexes[$name_item->nodeValue][$name_item->getAttribute('set')] = array('filter' => $filter,
'slop' => $slop,
'handler' => $handler);
foreach ($map_item->getElementsByTagName('alias') as $alias_item) {
if (NULL == ($slop = $alias_item->getAttribute('slop'))) {
$slop = $this->default_slop;
}
$this->indexes[$alias_item->nodeValue][$alias_item->getAttribute('set')]['alias'] =
array('slop' => $slop,
'handler' => $handler,
'prefix' => $name_item->getAttribute('set'),
'field' => $name_item->nodeValue);
$this->indexes[$alias_item->nodeValue][$alias_item->getAttribute('set')]['alias'] =
array('slop' => $slop,
'handler' => $handler,
'prefix' => $name_item->getAttribute('set'),
'field' => $name_item->nodeValue);
}
}
}
735,7 → 734,7
 
/** \brief Get list of valid operators
* @param $str string
* @retval boolean
* @return boolean
*/
private function xs_boolean($str) {
return ($str == 1 || $str == 'true');
744,18 → 743,18
/** \brief Get list of valid operators
* @param $language string
*/
/* not used any more - operators are given in cql
private function set_operators($language) {
$this->operators = array();
$boolean_lingo = ($language == 'cqldan' ? 'dan' : 'eng');
foreach ($this->cql_dom->getElementsByTagName('supports') as $support_item) {
$type = $support_item->getAttribute('type');
if (in_array($type, array('relation', 'booleanChar', $boolean_lingo . 'BooleanModifier'))) {
$this->operators[] = $support_item->nodeValue;
/* not used any more - operators are given in cql
private function set_operators($language) {
$this->operators = array();
$boolean_lingo = ($language == 'cqldan' ? 'dan' : 'eng');
foreach ($this->cql_dom->getElementsByTagName('supports') as $support_item) {
$type = $support_item->getAttribute('type');
if (in_array($type, array('relation', 'booleanChar', $boolean_lingo . 'BooleanModifier'))) {
$this->operators[] = $support_item->nodeValue;
}
}
}
}
*/
*/
 
}
 
/class_lib/trunk/registry_class.php
30,19 → 30,25
 
require_once('OLS_class_lib/curl_class.php');
 
/**
* Class Registry
*/
class Registry {
 
public static $response = FALSE;
 
private function __construct() { }
private function __destruct() { }
private function __clone() { }
/**
* Registry constructor.
*/
private function __construct() {
}
 
/**
* \brief Sets loglevel and logfile
* @param service_name (string)
* @param operation (string)
* @param version (string)
* @param setting (array)
* @param settings (array)
* */
static public function set($service_name, $operation, $version, $settings) {
if ($registry = $settings['registry']) {
59,7 → 65,7
}
 
/**
* \brief Handle reply - will newer get called unless set_wait_for_connections is set to 1
* \brief Handle reply - will newer get called unless set_wait_for_connections is set to 1
* @param handle (string)
* @param answer (string)
* */
69,7 → 75,7
 
/**
* \brief to return response set by receiveResponse() above
* @retval (string)
* @return string
* */
static public function get_response() {
return self::$response;
92,6 → 98,7
/**
* \brief Send the http request and do no wait for an answer
* @param url (string)
* @return array
* */
static private function curl_options() {
return array(CURLOPT_RETURNTRANSFER => TRUE,
102,7 → 109,6
CURLOPT_CONNECTTIMEOUT_MS => 1000,
CURLOPT_WRITEFUNCTION => array(__CLASS__, 'receiveResponse'));
}
 
}
 
 
/class_lib/trunk/restconvert_class.php
17,16 → 17,15
*
* You should have received a copy of the GNU Affero General Public License
* along with Open Library System. If not, see <http://www.gnu.org/licenses/>.
*/
*/
 
/**
* \brief Converts an url to the 'corresponding" soap-request.
*
*
* Converting is controlled by the [rest] section from the config-object (the ini-file)
*
* @author Finn Stausgaard - DBC
**/
 
**/
class Restconvert {
 
//private $charset = 'ISO-8859-1';
36,13 → 35,13
private $default_namespace = '';
 
/**
* \brief constructor
*
* @param $namespace string -
*/
public function __construct($namespace='') {
$this->soap_header='<?xml version="1.0" encoding="%s"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"%s><SOAP-ENV:Body>';
$this->soap_footer='</SOAP-ENV:Body></SOAP-ENV:Envelope>';
* \brief constructor
*
* @param $namespace string -
*/
public function __construct($namespace = '') {
$this->soap_header = '<?xml version="1.0" encoding="%s"?><SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"%s><SOAP-ENV:Body>';
$this->soap_footer = '</SOAP-ENV:Body></SOAP-ENV:Envelope>';
if ($namespace)
$this->default_namespace = ' xmlns="' . $namespace . '"';
}
50,7 → 49,7
/** \brief Transform REST parameters to SOAP-request
*
* @param $config object - the config object from the ini-file
* @retval string - Soap wrapped xml
* @return string - Soap wrapped xml
*/
public function rest2soap(&$config) {
$soap_actions = $config->get_value('soapAction', 'setup');
58,16 → 57,19
$action = $this->get_post('action');
if (!$all_actions = $action_pars['ALL']) $all_actions = array();
if ($action
&& is_array($soap_actions) && is_array($action_pars)
&& $soap_actions[$action] && $action_pars[$action]) {
&& is_array($soap_actions) && is_array($action_pars)
&& $soap_actions[$action] && $action_pars[$action]
) {
if ($this->get_post('charset')) $this->charset = $this->get_post('charset');
if ($node_value = $this->build_xml(array_merge($all_actions, $action_pars[$action]),
explode('&', $_SERVER['QUERY_STRING']))) {
return sprintf($this->soap_header, $this->charset, $this->default_namespace) .
$this->rest_tag_me($soap_actions[$action], $node_value) .
$this->soap_footer;
explode('&', $_SERVER['QUERY_STRING']))
) {
return sprintf($this->soap_header, $this->charset, $this->default_namespace)
. $this->rest_tag_me($soap_actions[$action], $node_value)
. $this->soap_footer;
}
}
return null;
}
 
/** \brief Build xml controlled by $action with data from query
74,7 → 76,7
*
* @param $action array - list of accepted parameters and the xml-structure they are to create
* @param $query array - of parameters and values like parameter=value
* @retval string - xml
* @return string - xml
*/
private function build_xml($action, $query) {
foreach ($action as $key => $tag) {
81,7 → 83,8
if (is_array($tag)) {
$data = $this->build_xml($tag, $query);
if (isset($data)) $ret .= $this->rest_tag_me($key, $data);
} else {
}
else {
foreach ($query as $parval) {
list($par, $val) = $this->par_split($parval);
if ($tag == $par) $ret .= $this->rest_tag_me($tag, htmlspecialchars($val));
93,8 → 96,8
 
/** \brief Helper function to get a parameter from _GET or _POST
*
* @param $par string
* @retval string - the associated value of $par
* @param $par string
* @return string - the associated value of $par
*/
private function get_post($par) {
return ($_GET[$par] ? $_GET[$par] : $_POST[$par]);
102,8 → 105,8
 
/** \brief Helper function to split values like parameter=value
*
* @param $parval string
* @retval array - of paramter and value
* @param $parval string
* @return array - of paramter and value
*/
private function par_split($parval) {
list($par, $val) = explode('=', $parval, 2);
112,17 → 115,15
 
/** \brief Helper function to produce balanced xml
*
* @param $tag string
* @param $val string
* @retval string - balanced xml string
* @param $tag string
* @param $val string
* @return string - balanced xml string
*/
function rest_tag_me($tag, $val) {
if (!isset($val)) return;
if (!isset($val)) return '';
 
if ($i = strrpos($tag, '.'))
$tag = substr($tag, $i+1);
$tag = substr($tag, $i + 1);
return "<$tag>$val</$tag>";
}
}
 
?>