/** * Check spelling * * @param QueryTerms[] $query_terms */ public function checkSpelling(array $query_terms) { $registry = Registry::getInstance(); $app_id = $registry->getConfig('BING_ID', true); $suggestion = new Suggestion(); $client = Factory::getHttpClient(); // @todo: see if we can't collapse multiple terms into a single spellcheck query foreach ($query_terms as $term) { $query = $term->phrase; $query = urlencode(trim($query)); $correction = null; // get spell suggestion try { $url = "http://api.search.live.net/xml.aspx?Appid={$app_id}&sources=spell&query={$query}"; $response = $client->getUrl($url); // process it $xml = Parser::convertToDOMDocument($response); // echo header("Content-Type: text/xml"); echo $xml->saveXML(); exit; $suggestion_node = $xml->getElementsByTagName('Value')->item(0); if ($suggestion_node != null) { $correction = $suggestion_node->nodeValue; } } catch (\Exception $e) { trigger_error('Could not process spelling suggestion: ' . $e->getTraceAsString(), E_USER_WARNING); } // got one if ($correction != null) { $term->phrase = $suggestion_node->nodeValue; $suggestion->addTerm($term); } } return $suggestion; }
/** * Parse the primo response * * @param string $response * @return ResultSet */ public function parseResponse($response) { // load it $xml = Parser::convertToDOMDocument($response); // header("Content-type:text/xml"); echo $xml->saveXML(); exit; // check for errors $error = $xml->getElementsByTagName("ERROR")->item(0); if ($error != "") { throw new \Exception($error->getAttribute("MESSAGE")); } // set-up the result set $result_set = new Search\ResultSet($this->config); // total $docset = $xml->getElementsByTagName("DOCSET")->item(0); if ($docset == null) { throw new \Exception("Could not determine total number of records"); } $total = $docset->getAttribute("TOTALHITS"); $result_set->total = $total; // extract records foreach ($this->extractRecords($xml) as $xerxes_record) { $result_set->addRecord($xerxes_record); } // facets $facets = $this->extractFacets($xml); $result_set->setFacets($facets); return $result_set; }
public function loadXML($doc) { $id = null; $format = null; $score = null; $xml_data = ""; foreach ($doc->str as $str) { // marc record if ((string) $str["name"] == 'fullrecord') { $marc = trim((string) $str); // marc-xml if (substr($marc, 0, 5) == '<?xml') { $xml_data = $marc; } else { $marc = preg_replace('/#31;/', "", $marc); $marc = preg_replace('/#30;/', "", $marc); $marc_file = new \File_MARC($marc, \File_MARC::SOURCE_STRING); $marc_record = $marc_file->next(); $xml_data = $marc_record->toXML(); } } elseif ((string) $str["name"] == 'id') { $id = (string) $str; } } // format foreach ($doc->arr as $arr) { if ($arr["name"] == "format") { $format = (string) $arr->str; } } // score foreach ($doc->float as $float) { if ($float["name"] == "score") { $score = (string) $float; } } // load marc data $this->marc = new MarcRecord(); $this->marc->loadXML($xml_data); // save for later $this->document = Parser::convertToDOMDocument($doc); $this->serialized = $doc->asXml(); // process it $this->map(); $this->cleanup(); // add non-marc data $this->setRecordID($id); $this->setScore($score); $this->format()->setInternalFormat($format); $this->format()->setPublicFormat($format); // custom mappings if (class_exists('Local\\Model\\Solr\\Record')) { $local = new \Local\Model\Solr\Record(); $local->map($this, $this->marc); } }
/** * Serialize to XML * * @return array */ public function toXML() { $xml = Parser::convertToDOMDocument('<holding />'); foreach ($this->data as $name => $value) { $line = $xml->createElement('data', Parser::escapeXml($value)); $line->setAttribute('key', $name); $xml->documentElement->appendChild($line); } return $xml; }
public function toXML() { $xml = Parser::convertToDOMDocument("<lti />"); $this->appendElement($xml, "id", $this->getID()); $this->appendElement($xml, "instructor", $this->isInstructor()); foreach ($this->request->get_parameters() as $id => $value) { $this->appendElement($xml, $id, $value); } return $xml; }
/** * Parses a validation response from a CAS server to see if the returning CAS request is valid * * @param string $results xml or plain text response from cas server * @return bool true if valid, false otherwise * @exception throws exception if cannot parse response or invalid version */ private function isValid() { // values from the request $ticket = $this->request->getParam("ticket"); // configuration settings $configCasValidate = $this->registry->getConfig("CAS_VALIDATE", true); $configCasValidate = rtrim($configCasValidate, '/'); // figure out which type of response this is based on the service url $arrURL = explode("/", $configCasValidate); $service = array_pop($arrURL); // now get it! $url = $configCasValidate . "?ticket=" . $ticket . "&service=" . urlencode($this->validate_url); $client = Factory::getHttpClient(); $req = $client->get($url); $req->getCurlOptions()->set(CURLOPT_SSL_VERIFYHOST, false); $req->getCurlOptions()->set(CURLOPT_SSL_VERIFYPEER, false); $response = $req->send(); $results = (string) $response->getBody(); // validate is plain text if ($service == "validate") { $message_array = explode("\n", $results); if (count($message_array) >= 2) { if ($message_array[0] == "yes") { return $message_array[1]; } } else { throw new \Exception("Could not parse CAS validation response."); } } elseif ($service == "serviceValidate" || $service == "proxyValidate") { // these are XML based $xml = Parser::convertToDOMDocument($results); $cas_namespace = "http://www.yale.edu/tp/cas"; $user = $xml->getElementsByTagNameNS($cas_namespace, "user")->item(0); $failure = $xml->getElementsByTagNameNS($cas_namespace, "authenticationFailure")->item(0); if ($user != null) { if ($user->nodeValue != "") { return $user->nodeValue; } else { throw new \Exception("CAS validation response missing username value"); } } elseif ($failure != null) { // see if error, rather than failed authentication if ($failure->getAttribute("code") == "INVALID_REQUEST") { throw new \Exception("Invalid request to CAS server: " . $failure->nodeValue); } } else { throw new \Exception("Could not parse CAS validation response."); } } else { throw new \Exception("Unsupported CAS version."); } // if we got this far, the request was invalid return false; }
public function toXML() { $xml = Parser::convertToDOMDocument("<category />"); $xml->documentElement->setAttribute("name", $this->name); $xml->documentElement->setAttribute("normalized", $this->normalized); foreach ($this->subcategories as $subcategory) { $import = $xml->importNode($subcategory->toXML()->documentElement, true); $xml->documentElement->appendChild($import); } return $xml; }
public function toXML() { $xml = Parser::convertToDOMDocument("<subcategory />"); $xml->documentElement->setAttribute("name", $this->name); $xml->documentElement->setAttribute("sequence", $this->sequence); $xml->documentElement->setAttribute("id", $this->databases_id); foreach ($this->databases as $database) { $import = $xml->importNode($database->toXML()->documentElement, true); $xml->documentElement->appendChild($import); } return $xml; }
/** * Add a database to the local knowledgebase * * @param Database $objDatabase */ public function addDatabase(Database $objDatabase) { // load our data into xml object $xml = simplexml_load_string($objDatabase->data); // these fields have boolen values in metalib $boolean_fields = array("proxy", "searchable", "guest_access", "subscription", "sfx_suppress", "new_resource_expiry"); // normalize boolean values foreach ($xml->children() as $child) { $name = (string) $child->getName(); $value = (string) $child; if (in_array($name, $boolean_fields)) { $xml->{$name} = $this->convertMetalibBool($value); } } // remove empty nodes $dom = Parser::convertToDOMDocument($xml->asXML()); $xmlPath = new \DOMXPath($dom); $xmlNullNodes = $xmlPath->query('//*[not(node())]'); foreach ($xmlNullNodes as $node) { $node->parentNode->removeChild($node); } $objDatabase->data = $dom->saveXML(); // add the main database entries $this->doSimpleInsert("xerxes_databases", $objDatabase); // now also extract searchable fields so we can populate the search table // get fields from config foreach ($this->searchable_fields as $search_field) { $search_field = trim($search_field); foreach ($xml->{$search_field} as $field) { $searchable_terms = array(); foreach (explode(" ", (string) $field) as $term) { // only numbers and letters please $term = preg_replace('/[^a-zA-Z0-9]/', '', $term); $term = strtolower($term); // anything over 50 chars is likley a URL or something if (strlen($term) > 50) { continue; } array_push($searchable_terms, $term); } // remove duplicate terms $searchable_terms = array_unique($searchable_terms); // insert em $strSQL = "INSERT INTO xerxes_databases_search ( database_id, field, term ) " . "VALUES ( :metalib_id, :field, :term )"; foreach ($searchable_terms as $unique_term) { $this->insert($strSQL, array(":metalib_id" => $objDatabase->metalib_id, ":field" => $search_field, ":term" => $unique_term)); } } } }
/** * Load data from XML */ public function loadXML($xml) { $this->document = Parser::convertToDOMDocument($xml); $this->xpath = new \DOMXPath($this->document); // test to see what profile the context object is using // set namespace accordingly if ($this->document->getElementsByTagNameNS("info:ofi/fmt:xml:xsd:book", "book")->item(0) != null) { $this->xpath->registerNamespace("rft", "info:ofi/fmt:xml:xsd:book"); } elseif ($this->document->getElementsByTagNameNS("info:ofi/fmt:xml:xsd:dissertation", "dissertation")->item(0) != null) { $this->xpath->registerNamespace("rft", "info:ofi/fmt:xml:xsd:dissertation"); } elseif ($this->document->getElementsByTagNameNS("info:ofi/fmt:xml:xsd", "journal")->item(0) != null) { $this->xpath->registerNamespace("rft", "info:ofi/fmt:xml:xsd"); } else { $this->xpath->registerNamespace("rft", "info:ofi/fmt:xml:xsd:journal"); } $this->map(); $this->cleanup(); }
public function getRecommendations(Xerxes\Record $xerxes_record, $min_relevance = 0, $max_records = 10) { $bx_records = array(); // now get the open url $open_url = $xerxes_record->getOpenURL(null, $this->sid); $open_url = str_replace('genre=unknown', 'genre=article', $open_url); // send it to bx service $url = $this->url . "/recommender/openurl?token=" . $this->token . "&{$open_url}" . "&res_dat=source=global&threshold={$min_relevance}&maxRecords={$max_records}"; try { $client = Factory::getHttpClient(); $client->setUri($url); $client->setOptions(array('timeout' => 4)); $xml = $client->send()->getBody(); if ($xml == "") { throw new \Exception("No response from bx service"); } } catch (\Exception $e) { // just issue the exception as a warning trigger_error("Could not get result from bX service: " . $e->getTraceAsString(), E_USER_WARNING); return $bx_records; } // header("Content-type: text/xml"); echo $xml; exit; $doc = Parser::convertToDOMDocument($xml); $xpath = new \DOMXPath($doc); $xpath->registerNamespace("ctx", "info:ofi/fmt:xml:xsd:ctx"); $records = $xpath->query("//ctx:context-object"); foreach ($records as $record) { $bx_record = new Record(); $bx_record->loadXML($record); array_push($bx_records, $bx_record); } if (count($bx_records) > 0) { // first one is the record we want to find recommendations for // so skip it; any others are actual recommendations array_shift($bx_records); } return $bx_records; }
/** * Load from MARC-XML source * * @param string|DOMNode|DOMDocument $node */ public function loadXML($node = null) { if ($node != null) { // make sure we have a DOMDocument $this->document = Parser::convertToDOMDocument($node); // extract the three data types $leader = $this->document->getElementsByTagName("leader"); $control_fields = $this->document->getElementsByTagName("controlfield"); $data_fields = $this->document->getElementsByTagName("datafield"); // leader $this->leader = new Leader($leader->item(0)); // control fields foreach ($control_fields as $control_field) { $controlfield = new ControlField($control_field); array_push($this->_controlfields, $controlfield); } // data fields foreach ($data_fields as $data_field) { $datafield = new DataField($data_field); array_push($this->_datafields, $datafield); } } }
public function getOriginalXML($bolString = false) { // convert original (JSON-based) array to xml $this->document = Parser::convertToDOMDocument('<original />'); Parser::addToXML($this->document, 'record', $this->original_array); return parent::getOriginalXML($bolString); }
/** * Creates a sorting page element * * @param string $sort current sort * * @return DOMDocument sort navigation */ public function sortDisplay($sort) { $sort_options = $this->config->sortOptions(); if (count($sort_options) == 0) { return null; } $xml = Parser::convertToDOMDocument("<sort_display />"); $x = 1; foreach ($sort_options as $key => $value) { if ($key == $sort) { $here = $xml->createElement("option", $value); $here->setAttribute("active", "true"); $xml->documentElement->appendChild($here); } else { $params = $this->sortLinkParams(); $params["sort"] = $key; $here = $xml->createElement("option", $value); $here->setAttribute("active", "false"); $here->setAttribute("link", $this->request->url_for($params)); $xml->documentElement->appendChild($here); } $x++; } return $xml; }
/** * Do the actual search * * @param mixed $search string or Query, the search query * @param int $start [optional] starting record number * @param int $max [optional] max records * @param string $sort [optional] sort order * * @return Results */ protected function doSearch($search, $start = 1, $max = 10, $sort = "") { // parse the query $query = ""; if ($search instanceof Search\Query) { foreach ($search->getQueryTerms() as $term) { $query .= "&query=" . $term->field_internal . ",contains," . urlencode($term->phrase); } foreach ($search->getLimits(true) as $limit) { $query .= "&query=facet_" . $limit->field . ",exact," . urlencode($limit->value); } } else { $query = "&query=" . urlencode($search); } // on campus as string $on_campus = "true"; if ($this->on_campus == false) { $on_campus = "false"; } // create the url $this->url = $this->server . "/xservice/search/brief?" . "institution=" . $this->institution . "&onCampus=" . $on_campus . $query . "&indx={$start}" . "&bulkSize={$max}"; if ($this->vid != "") { $this->url .= "&vid=" . $this->vid; } foreach ($this->loc as $loc) { $this->url .= "&loc=" . $loc; } if ($sort != "") { $this->url .= "&sortField={$sort}"; } // get the response $client = Factory::getHttpClient(); $client->setUri($this->url); $response = $client->send()->getBody(); // echo $response; if ($response == "") { throw new \Exception("Could not connect to Primo server"); } // load it $xml = Parser::convertToDOMDocument($response); // parse it return $this->parseResponse($xml); }
/** * Parse the ebsco response * * @param string $response * @return ResultSet */ public function parseResponse($response) { // load it in $xml = Parser::convertToDOMDocument($response); // catch a non-xml response if ($xml->documentElement == null) { throw new \Exception("Could not connect to Ebsco search server"); } // check for fatal error if ($xml->documentElement->nodeName == 'Fault') { $message = $xml->getElementsByTagName('Message')->item(0); if ($message != null) { if ($message->nodeValue == "The following parameter(s) have incorrect values: Field query: Greater than 0") { throw new \Exception('Ebsco search error: your search query cannot be empty'); } // need to get a fresh set of databases if (strstr($message->nodeValue, 'User does not have access rights to database')) { throw new DatabaseException($message->nodeValue); } throw new \Exception('Ebsco server error: ' . $message->nodeValue); } } // result set $results = new Search\ResultSet($this->config); // get total $total = 0; $hits = $xml->getElementsByTagName("Hits")->item(0); if ($hits != null) { $total = (int) $hits->nodeValue; } ### hacks until ebsco gives us proper hit counts, they are almost there $check = 0; foreach ($xml->getElementsByTagName("rec") as $hits) { $check++; } // no hits, but we're above the first page, so the user has likely // skipped here, need to increment down until we find the true ceiling if ($check == 0 && $this->query->start > $this->query->max) { // but let's not get crazy here if ($this->deincrementing <= 8) { $this->deincrementing++; $new_start = $this->query->start - $this->query->max; $this->query->start = $new_start; return $this->doSearch($this->query); } } // we've reached the end prematurely, so set this to the end $check_end = $this->query->start + $check; if ($check < $this->query->max) { if ($check_end < $total) { $total = $check_end; } } ## end hacks // set total $results->total = $total; // add records foreach ($this->extractRecords($xml) as $record) { $results->addRecord($record); } // add clusters $facets = $this->extractFacets($xml); $results->setFacets($facets); /* $facets_id = 'facets' . $query->getHash(); // cached clusters $cached_facets = $this->cache->get($facets_id); if ( $cached_facets instanceof Facets ) { $results->setFacets($cached_facets); } else { $facets = $this->extractFacets($xml); $this->cache->set($facets_id, $facets); $results->setFacets($facets); } */ return $results; }
/** * Search and retieve records * * @throws Exception for diagnostic errors * * @param mixed $query the query in CQL format * @param int $iStartRecord [optional] start record to begin with, default 1 * @param int $iMaxiumumRecords [optional] maximum records to return, default 10 * @param string $strSort [optional] index to sort records on * * @return DOMDocument SRU results response */ public function searchRetrieve($query, $iStartRecord = 1, $iMaxiumumRecords = 10, $strSort = null) { // append any limits set earlier $this->query = $query . " " . $this->limits; // construct the request to the server $this->url = $this->base . "?wskey=" . $this->key; $this->url .= "&version=1.1"; $this->url .= "&operation=searchRetrieve"; $this->url .= "&query=" . urlencode($this->query); $this->url .= "&startRecord=" . $iStartRecord; $this->url .= "&maximumRecords=" . $iMaxiumumRecords; $this->url .= "&recordSchema=marcxml"; $this->url .= "&servicelevel=" . $this->service; // workset grouping if ($this->frbr == false) { $this->url .= "&frbrGrouping=off"; } // sort order if ($strSort != "") { $this->url .= "&sortKeys=" . $strSort; } // get the response from the server $results = $this->fetchResults($this->url); $objXml = Parser::convertToDOMDocument($results); $objXPath = new \DOMXPath($objXml); $objXPath->registerNameSpace("zs", "http://www.loc.gov/zing/srw/"); // check for diagnostic errors $objDiagnostics = $objXPath->query("zs:diagnostics"); if ($objDiagnostics->length > 0) { $strError = ""; $diagnostics = simplexml_import_dom($objDiagnostics->item(0)); foreach ($diagnostics->diagnostic as $diagnostic) { $strError .= $diagnostic->message; if ((string) $diagnostic->details != "") { $strError .= ": " . $diagnostic->details; } } throw new \Exception($strError); } // extract total hits $objTotal = $objXPath->query("zs:numberOfRecords")->item(0); if ($objTotal != null) { $this->total = (int) $objTotal->nodeValue; } return $objXml; }
/** * Return values as XML * * @param null|array|\ArrayAccess Values to use during rendering */ public function toXML($vars) { $xml = Parser::convertToDOMDocument('<xerxes />'); foreach ($vars as $id => $object) { Parser::addToXML($xml, $id, $object); } return $xml; }
/** * Serialize to XML */ public function toXML() { $xml = new \SimpleXMLElement('<link />'); if ($this->isFullText()) { $xml->addAttribute("type", "full"); $xml->addAttribute("format", $this->getType()); } else { $xml->addAttribute("type", $this->getType()); } $xml->display = $this->getDisplay(); $xml->url = $this->getURL(); return Parser::convertToDOMDocument($xml); }
/** * Load a MARC-XML document from string or object * * @param string|DOMNode|DOMDocument $xml */ public function loadXML($xml) { $xml = Parser::convertToDOMDocument($xml); $this->parse($xml); }
/** * Serialize to XML * * @return DOMDocument */ public function toXML() { // data is already in xml, we just use this opportunity to // enhance it with a few bits of data we don't already have ### display name for group restrictions if (count($this->simplexml()->group_restriction) > 0) { foreach ($this->simplexml()->group_restriction as $group_restriction) { $group_restriction->addAttribute("display_name", $this->config()->getGroupDisplayName((string) $group_restriction)); } } ### split note fields into separate entries per language $multilingual = $this->config()->getConfig("db_description_multilingual", false, ""); // XML object // build a list of configured description languages $db_languages_code = array(); $db_languages_order = array(); if ($multilingual != "") { $order = 0; foreach ($multilingual->language as $language) { $order++; $code = NULL; foreach ($language->attributes() as $name => $val) { if ($name == "code") { $code = (string) $val; } } $db_languages_order[$code] = $order; $db_languages_code[$order] = $code; } } $notes = array("description", "search_hints"); foreach ($notes as $note_field_name) { $node_queue = array(); // nodes to add when done looping to prevent looping over nodes added inside the loop foreach ($this->simplexml()->{$note_field_name} as $note_field_xml) { $note_field = (string) $note_field_xml; $pos = strpos($note_field, '######'); if ($multilingual == false || $pos === false) { $note_field = str_replace('######', '\\n\\n\\n', $note_field); } else { $descriptions = explode('######', $note_field); $i = 1; foreach ($descriptions as $description) { $description = $this->embedNoteField($description); $node_queue[] = array('note_field_name' => $note_field_name, 'description' => $description, 'code' => $db_languages_code[$i++]); } } $note_field = $this->embedNoteField($note_field); $this->simplexml()->{$note_field_name} = $note_field; $this->simplexml()->{$note_field_name}->addAttribute('lang', 'ALL'); } foreach ($node_queue as $node) { $descNode = $this->simplexml()->addChild($node['note_field_name'], $node['description']); $descNode->addAttribute('lang', $node['code']); } } // convert to DOMDocument $objDom = Parser::convertToDOMDocument($this->simplexml()->asXML()); // add database id $objDatabase = $objDom->documentElement; $objDatabase->setAttribute("database_id", $this->database_id); // is the particular user allowed to search this? $objElement = $objDom->createElement("searchable_by_user", $this->searchable_by_user); // @todo need to change this $objDatabase->appendChild($objElement); return $objDom; }
/** * Fetch the data from Metalib and check for errors * * @param string $url url of the request */ private function getResponse($url, $timeout = null, $retry = 0) { // fetch the data $this->client->setUri($url); $this->client->setConfig(array('timeout' => $timeout)); $response = $this->client->send(); if ($response->isClientError() || $response->getBody() == "") { throw new \Exception("Cannot process search at this time."); } // load into xml $doc = Parser::convertToDOMDocument($response->getBody()); // no response? if ($doc->documentElement == null) { throw new \Exception("cannot connect to metalib server"); } // error in response if ($doc->getElementsByTagName("error_code") != null) { // for easier handling $xml = simplexml_import_dom($doc->documentElement); foreach ($xml->xpath("//global_error|//local_error") as $error) { $error_code = (int) $error->error_code; $error_text = (string) $error->error_text; // now examine them // metalib session has timed out! if ($error_code == 0151) { // this particular metalib error message is confusing to end-users, so make it more clear $error_text = "The Metalib session has expired"; // also try to re-up the session in the event metalib was restarted or something if ($retry == 0) { $this->session = $this->getSession(); return $this->getResponse($url, $timeout, 1); } } // these are just warnings if ($error_code == 2114 || $error_code == 6039 || $error_code == 6033 || $error_code == 6034 || $error_code == 6023 || $error_code == 134 || $error_code == 6022) { trigger_error("Metalib warning ({$error_code}): {$error_text}", E_USER_WARNING); } else { throw new \Exception("Metalib exception ({$error_code}): {$error_text}"); } } } return $doc; }
/** * Fetch record information * * @param string $id bibliographic id */ public function getHoldings($id) { $holdings = new Search\Holdings(); // fetch holdings page from web service $url = $this->server . "GetHoldingsService?bibId={$id}"; $content = $this->client->getUrl($url, 4); // load and parse it $xml = Parser::convertToDOMDocument($content); // header("Content-type: text/xml"); echo $xml->saveXML(); exit; $records = $xml->getElementsByTagName("mfhdRecord"); foreach ($records as $record) { $item = new Search\Item(); $item->id = $record->getAttribute("mfhdId"); $item_count = (int) $record->getElementsByTagName("itemCount")->item(0)->nodeValue; // @todo what is this? foreach ($record->getElementsByTagName("datafield") as $datafield) { if ($datafield->getAttribute("tag") == "866") { $datafield->textContent; } } if ($item_count == 0) { continue; } $unavailable = 0; foreach ($record->getElementsByTagName("itemData") as $itemData) { if ($itemData->getAttribute("name") == "statusCode") { if ($itemData->nodeValue != 1) { $unavailable++; } } } // holding record if ($item_count > 1 && $unavailable > 0) { $holding = new Search\Holding(); // item count summary $available = $item_count - $unavailable; $number_of_items = "{$item_count} items ({$available} available)"; $holding->setProperty('Number of items', $number_of_items); $holdings->addHolding($holding); continue; } // locations $locations = array(); foreach ($record->getElementsByTagName("itemLocation") as $location_node) { $location = ""; $caption = ""; $temp = false; foreach ($location_node->getElementsByTagName("itemLocationData") as $location_data) { if ($location_data->getAttribute("name") == "tempLocation") { if (!in_array($location_data->nodeValue, $this->ignore_locations)) { $location = $location_data->nodeValue; } } elseif ($location_data->getAttribute("name") == "itemCaption") { $caption = $location_data->nodeValue; } elseif ($location_data->getAttribute("name") == "tmpLoc") { $temp = true; } } if ($location != "") { // if this is the temp location, then previous one is the 'old' location if ($temp == true) { array_pop($locations); } $locations[$location] = $caption; } } // no locations, so skip it yo! if (count($locations) == 0) { continue; } // call number foreach ($record->getElementsByTagName("mfhdData") as $data) { if ($data->getAttribute("name") == "callNumber") { $item->callnumber = $data->nodeValue; } } // status foreach ($record->getElementsByTagName("itemData") as $data) { if ($data->getAttribute("name") == "statusCode") { $status = $data->nodeValue; $public_status = $this->config->getPublicStatus($status); if ($public_status != null) { $item->status = $public_status; if ($item_count > count($locations)) { $item->status .= " ({$item_count} items)"; } } if ($status == 1) { $item->availability = true; } else { $item->availability = false; } } elseif ($data->getAttribute("name") == "statusDate") { $date = $data->nodeValue; $matches = array(); if (preg_match('/([0-9]{4})-([0-9]{2})-([0-9]{2})/', $date, $matches)) { $item->duedate = $matches[2] . "-" . $matches[3] . "-" . $matches[1]; $item->status = str_replace('\\d', $item->duedate, $item->status); } } } foreach ($locations as $item_location => $caption) { if ($caption != "") { $item->location = "{$caption} shelved at {$item_location}"; } else { $item->location = $item_location; } $holdings->addItem($item); } } return $holdings; }
/** * Retrieve the MARC Record as MARC-XML * * @return DOMDocument */ public function getMarcXML() { if (!$this->document instanceof \DOMDocument) { // we've created this MARC record from our own objects // instead of a marc-xml source, so create marc-xml now $this->document = Parser::convertToDOMDocument('<record xmlns="' . $this->namespace . '" />'); // leader if ($this->leader instanceof Leader) { $leader_xml = $this->document->createElementNS($this->namespace, "leader", $this->leader->value); $this->document->appendChild($leader_xml); } // control fields foreach ($this->controlfields() as $controlfield) { $controlfield_xml = $this->document->createElementNS($this->namespace, "controlfield", $controlfield->value); $controlfield_xml->setAttribute("tag", $controlfield->tag); $this->document->appendChild($controlfield_xml); } // data fields foreach ($this->datafield() as $datafield) { $datafield_xml = $this->document->createElementNS($this->namespace, "datafield"); $datafield_xml->setAttribute("tag", $datafield->tag); $this->document->appendChild($datafield_xml); // subfields foreach ($datafield->subfield() as $subfield) { $subfield_xml = $this->document->createElementNS($this->namespace, "subfield", $subfield->value); $subfield_xml->setAttribute("code", $subfield->code); $datafield_xml->appendChild($subfield_xml); } } } return $this->document; }
/** * Creates a sorting page element * * @param string $sort current sort * * @return DOMDocument sort navigation */ public function sortDisplay($sort) { $sort_options = $this->config->sortOptions(); if (count($sort_options) == 0) { return null; } $xml = Parser::convertToDOMDocument("<sort_display />"); $x = 1; foreach ($sort_options as $id) { $here = $xml->createElement('option'); $here->setAttribute('id', $id); if ($id == $sort) { $here->setAttribute('active', 'true'); } else { $params = $this->sortLinkParams(); $params['sort'] = $id; $here->setAttribute('active', 'false'); $here->setAttribute('link', $this->request->url_for($params)); } $xml->documentElement->appendChild($here); $x++; } return $xml; }
/** * Extracts the MARC data from the HTML response and converts it to MARC-XML * * @param string $marc marc data as string * @return DOMDocument marc-xml document */ protected function extractMarc($response) { $xml = Parser::convertToDOMDocument("<record xmlns=\"http://www.loc.gov/MARC21/slim\" />"); $marc = ""; // marc data as text $arrTags = array(); // array to hold each MARC tag if (!stristr($response, "<pre>")) { // didn't find a record return $xml; } // parse out MARC data $marc = Parser::removeLeft($response, "<pre>"); $marc = Parser::removeRight($marc, "</pre>"); // remove break-tabs for easier parsing $marc = str_replace(" \n ", " ", $marc); $marc = str_replace("\n ", " ", $marc); $marc = trim($marc); // assign the marc values to the array based on Unix LF as delimiter $arrTags = explode("\n", $marc); foreach ($arrTags as $strTag) { // assign tag # and identifiers $strTagNumber = substr($strTag, 0, 3); $strId1 = substr($strTag, 4, 1); $strId2 = substr($strTag, 5, 1); // assign data and clean it up $data = substr($strTag, 7); // only convert all data to utf8 if told to do so, // but always do it to the leader, since it has mangled chars if ($this->convert_to_utf8 == true || $strTagNumber == "LEA") { if (function_exists("mb_convert_encoding")) { $data = mb_convert_encoding($data, "UTF-8"); } else { $data = utf8_encode($data); } } $data = Parser::escapeXml($data); $data = trim($data); if ($strTagNumber == "LEA") { // leader $objLeader = $xml->createElementNS($this->marc_ns, "leader", $data); $xml->documentElement->appendChild($objLeader); } elseif ($strTagNumber == "REC") { // Pseudo-MARC "REC" data field to store the INNOPAC // bibliographic record number in subfield a. $objRecNum = $xml->createElementNS($this->marc_ns, "datafield"); $objRecNum->setAttribute("tag", "REC"); $objRecNum->setAttribute("ind1", ' '); $objRecNum->setAttribute("ind2", ' '); $objRecNumSub = $xml->createElementNS($this->marc_ns, "subfield", strtolower($data)); $objRecNumSub->setAttribute("code", 'a'); $objRecNum->appendChild($objRecNumSub); $xml->documentElement->appendChild($objRecNum); } elseif ((int) $strTagNumber <= 8) { // control fields $objControlField = $xml->createElementNS($this->marc_ns, "controlfield", $data); $objControlField->setAttribute("tag", $strTagNumber); $xml->documentElement->appendChild($objControlField); } else { // data fields $objDataField = $xml->createElementNS($this->marc_ns, "datafield"); $objDataField->setAttribute("tag", $strTagNumber); $objDataField->setAttribute("ind1", $strId1); $objDataField->setAttribute("ind2", $strId2); // if first character is not a pipe symbol, then this is the default |a subfield // so make that explicit for the array if (substr($data, 0, 1) != "|") { $data = "|a " . $data; } // split the subfield data on the pipe and add them in using the first // character after the delimiter as the subfield code $arrSubFields = explode("|", $data); foreach ($arrSubFields as $strSubField) { if ($strSubField != "") { $code = substr($strSubField, 0, 1); $data = trim(substr($strSubField, 1)); // check for a url, in which case we need to ensure there are no spaces; // which can happen on the wrap of the data in the marc display if (strlen($data) > 4) { if (substr($data, 0, 4) == "http") { $data = str_replace(" ", "", $data); } } $objSubField = $xml->createElementNS($this->marc_ns, "subfield", $data); $objSubField->setAttribute("code", $code); $objDataField->appendChild($objSubField); } } $xml->documentElement->appendChild($objDataField); } } return $xml; }
/** * Do the actual search * * @param mixed $search search object or string * @param string $database [optional] database id * @param int $start [optional] starting record number * @param int $max [optional] max records * @param string $sort [optional] sort order * * @return Results */ protected function doSearch($search, $database_selected, $start, $max, $sort = "relevance") { // default for sort if ($sort == "") { $sort = "relevance"; } // prepare the query $query = ""; if ($search instanceof Search\Query) { $query = $search->toQuery(); } else { $query = $search; } // databases $databases = array(); // we asked for this database specifically if ($database_selected != "") { $databases = array($database_selected); } else { // see if any supplied as facet limit foreach ($search->getLimits(true) as $limit) { if ($limit->field == "database") { array_push($databases, $limit->value); } } // nope if (count($databases) == 0) { // get 'em from config $databases_xml = $this->config->getConfig("EBSCO_DATABASES"); if ($databases_xml == "") { throw new \Exception("No databases defined"); } foreach ($databases_xml->database as $database) { array_push($databases, (string) $database["id"]); } } } // construct url $this->url = "http://eit.ebscohost.com/Services/SearchService.asmx/Search?" . "prof=" . $this->username . "&pwd=" . $this->password . "&authType=&ipprof=" . "&query=" . urlencode($query) . "&startrec={$start}&numrec={$max}" . "&sort={$sort}" . "&format=detailed"; // add in the databases foreach ($databases as $database) { $this->url .= "&db={$database}"; } // get the xml from ebsco $client = Factory::getHttpClient(); $client->setUri($this->url); $response = $client->send()->getBody(); // testing // echo "<pre>$this->url<hr>$response</pre>"; exit; if ($response == null) { throw new \Exception("Could not connect to Ebsco search server"); } // load it in $xml = Parser::convertToDOMDocument($response); // catch a non-xml response if ($xml->documentElement == null) { throw new \Exception("Could not connect to Ebsco search server"); } // check for fatal error if ($xml->documentElement->nodeName == 'Fault') { $message = $xml->getElementsByTagName('Message')->item(0); if ($message != null) { throw new \Exception('Ebsco server error: ' . $message->nodeValue); } } // result set $results = new Search\ResultSet($this->config); // get total $total = 0; $hits = $xml->getElementsByTagName("Hits")->item(0); if ($hits != null) { $total = (int) $hits->nodeValue; } ### hacks until ebsco gives us proper hit counts, they are almost there $check = 0; foreach ($xml->getElementsByTagName("rec") as $hits) { $check++; } // no hits, but we're above the first page, so the user has likely // skipped here, need to increment down until we find the true ceiling if ($check == 0 && $start > $max) { // but let's not get crazy here if ($this->deincrementing <= 8) { $this->deincrementing++; $new_start = $start - $max; return $this->doSearch($search, $database_selected, $new_start, $max, $sort); } } // we've reached the end prematurely, so set this to the end $check_end = $start + $check; if ($check < $max) { if ($check_end < $total) { $total = $check_end; } } ## end hacks // set total $results->total = $total; // add records foreach ($this->extractRecords($xml) as $record) { $results->addRecord($record); } // add clusters $facets = $this->extractFacets($xml); $results->setFacets($facets); return $results; }
/** * Load, map, and clean-up record data from XML * * @param mixed $xml XML as DOM, SimpleXML or string */ public function loadXML($xml) { $this->document = Parser::convertToDOMDocument($xml); $this->map(); $this->cleanup(); }
/** * Add reviews from Good Reads */ public function addReviews() { $xerxes_record = $this->getXerxesRecord(); $isbn = $xerxes_record->getISBN(); $key = $this->registry->getConfig("GOOD_READS_API_KEY", false); if ($key != null) { $url = "http://www.goodreads.com/book/isbn?isbn={$isbn}&key={$key}"; $data = Parser::request($url, 5); if ($data != "") { $xml = Parser::convertToDOMDocument($data); $this->reviews = $xml; } } }