/** * Get data and output in JSON * * @return void * @access public */ public function getSearchLinkStatuses() { $metalib = new MetaLib(); if (!$metalib->available()) { // MetaLib not enabled return $this->output(array(), JSON::STATUS_OK); } // Cache values and status in an array $results = array(); $authorized = UserAccount::isAuthorized(); foreach ($_REQUEST['id'] as $id) { $ird = explode('.', $id, 2); if (!isset($ird[1])) { continue; } $ird = $ird[1]; $irdInfo = $metalib->getIRDInfo($ird); if ($irdInfo && ($authorized || strcasecmp($irdInfo['access'], 'guest') == 0)) { $results[] = array('id' => $id, 'status' => $irdInfo['searchable'] ? 'allowed' : 'nonsearchable'); } else { $results[] = array('id' => $id, 'status' => 'denied'); } } return $this->output($results, JSON::STATUS_OK); }
/** * Process PCI search * * @return void */ function doPCI() { global $interface; global $configArray; if (!$interface->get_template_vars('pciEnabled')) { PEAR::raiseError(new PEAR_Error("PCI is not enabled.")); } // If we're loading PCI results via AJAX, we can't take advantage // of the counter used in the main page for ensuring uniqueness of HTML // elements. To minimize the chance of a clash, let's reset the counter // to a very large number: if (isset($configArray['OpenURL']['embed']) && !empty($configArray['OpenURL']['embed'])) { include_once 'sys/Counter.php'; $interface->assign('openUrlCounter', new Counter(100000)); } // Whether embedded openurl autocheck is enabled if (isset($configArray['OpenURL']['autocheck']) && $configArray['OpenURL']['autocheck']) { $interface->assign('openUrlAutoCheck', true); } // Set Proxy URL if (isset($configArray['EZproxy']['host'])) { $interface->assign('proxy', $configArray['EZproxy']['host']); } // Determine whether to display book previews if (isset($configArray['Content']['previews'])) { $interface->assignPreviews(); } $interface->assign("showContext", isset($configArray['Content']['showHierarchyTree']) ? $configArray['Content']['showHierarchyTree'] : false); // Initialise SearchObject. $searchObject = SearchObjectFactory::initSearchObject('PCI'); $searchObject->init(); $searchObject->setLimit(10); // Search $result = $searchObject->processSearch(true, true); // We'll need recommendations no matter how many results we found: $interface->assign('qtime', round($searchObject->getQuerySpeed(), 2)); $interface->assign('spellingSuggestions', $searchObject->getSpellingSuggestions()); $interface->assign('lookfor', $searchObject->displayQuery()); $interface->assign('more', $searchObject->renderSearchUrl()); if ($showGlobalFiltersNote = $interface->getGlobalFiltersNotification('Primo Central')) { $interface->assign('showGlobalFiltersNote', $showGlobalFiltersNote); } $searchWithoutLocalFilters = $searchObject->renderSearchUrl(false); $searchWithoutLocalFilters = str_replace('/PCI/Search', '/Search/DualResults', $searchWithoutLocalFilters); $interface->assign('searchWithoutLocalFilters', $searchWithoutLocalFilters); // We want to guide the user to login for access to licensed material $interface->assign('methodsAvailable', Login::getActiveAuthorizationMethods()); $interface->assign('userAuthorized', UserAccount::isAuthorized()); if ($result['recordCount'] > 0) { // Display Listing of Results $summary = $searchObject->getResultSummary(); $page = $summary['page']; $interface->assign('recordCount', $summary['resultTotal']); $interface->assign('recordStart', $summary['startRecord']); $interface->assign('recordEnd', $summary['endRecord']); $interface->assign('recordSet', $result['response']['docs']); // If our result set is larger than the number of records that // PCI will let us page through, we should cut off the number // before passing it to our paging mechanism: $config = getExtraConfigArray('PCI'); $pageLimit = isset($config['General']['result_limit']) ? $config['General']['result_limit'] : 2000; $totalPagerItems = $summary['resultTotal'] < $pageLimit ? $summary['resultTotal'] : $pageLimit; } else { if ($searchObject->isEmptySearch()) { $interface->assign('noQuery', true); } } // 'Finish' the search... complete timers and log search history. $searchObject->close(); $interface->assign('time', round($searchObject->getTotalSpeed(), 2)); return $interface->fetch('Search/list-dual-pci.tpl'); }
/** * Process MetaLib search * * @return void */ function doMetaLib() { global $interface; global $configArray; // Initialise SearchObject. $searchObject = SearchObjectFactory::initSearchObject('MetaLib'); $searchObject->init(); if (isset($_GET['page'])) { $searchObject->setPage($_GET['page']); } $displayQuery = $searchObject->displayQuery(); $interface->assign('lookfor', $displayQuery); $interface->assign('searchType', $searchObject->getSearchType()); // Search MetaLib $template = null; if (!empty($displayQuery)) { $result = $searchObject->processSearch(true, true); // Whether RSI is enabled if (isset($configArray['OpenURL']['use_rsi']) && $configArray['OpenURL']['use_rsi']) { $interface->assign('rsi', true); } // Whether embedded openurl autocheck is enabled if (isset($configArray['OpenURL']['autocheck']) && $configArray['OpenURL']['autocheck']) { $interface->assign('openUrlAutoCheck', true); } // We'll need recommendations no matter how many results we found: $interface->assign('qtime', round($searchObject->getQuerySpeed(), 2)); $interface->assign('spellingSuggestions', $searchObject->getSpellingSuggestions()); $interface->assign('topRecommendations', $searchObject->getRecommendationsTemplates('top')); $interface->assign('sideRecommendations', $searchObject->getRecommendationsTemplates('side')); $interface->assign('disallowedDatabases', $result['disallowedDatabases']); $interface->assign('failedDatabases', $result['failedDatabases']); $interface->assign('successDatabases', $result['successDatabases']); $methodsAvailable = Login::getActiveAuthorizationMethods(); $userAuthorized = UserAccount::isAuthorized(); $setNotification = array(); // We want to guide the user to login for access to licensed material if (!$userAuthorized && $methodsAvailable) { $setNotification[] = '<p>' . translate('authorize_user_notification') . '</p>'; } if ($result['recordCount'] > 0) { $summary = $searchObject->getResultSummary(); $page = $summary['page']; $interface->assign('recordCount', $summary['resultTotal']); $interface->assign('recordStart', $summary['startRecord']); $interface->assign('recordEnd', $summary['endRecord']); $interface->assign('recordSet', $result['documents']); $interface->assign('sortList', $searchObject->getSortList()); // If our result set is larger than the number of records that // MetaLib will let us page through (10000 records per database), // we should cut off the number before passing it to our paging // mechanism: $config = getExtraConfigArray('MetaLib'); $pageLimit = isset($config['General']['result_limit']) ? $config['General']['result_limit'] : 2000; $totalPagerItems = $summary['resultTotal'] < $pageLimit ? $summary['resultTotal'] : $pageLimit; // Process Paging $link = $searchObject->renderLinkPageTemplate(); $options = array('totalItems' => $totalPagerItems, 'fileName' => $link, 'perPage' => $summary['perPage']); $pager = new VuFindPager($options); $interface->assign('pageLinks', $pager->getLinks()); $interface->assign('pagesTotal', $totalPagerItems); // Display Listing of Results $template = 'list-list.tpl'; } else { $interface->assign('recordCount', 0); // Was the empty result set due to an error? $error = $searchObject->getIndexError(); if ($error !== false) { // If it's a parse error or the user specified an invalid field, we // should display an appropriate message: if (stristr($error, 'user.entered.query.is.malformed') || stristr($error, 'unknown.field')) { $interface->assign('parseError', true); } else { // Unexpected error -- let's treat this as a fatal condition. PEAR::raiseError(new PEAR_Error('Unable to process query<br />MetaLib Returned: ' . $error)); } } // Show notification if all databases were disallowed. if (count($result['successDatabases']) === 0 && count($result['failedDatabases']) === 0 && count($result['disallowedDatabases']) > 0) { array_unshift($setNotification, '<p>' . translate('metalib_not_authorized_all') . '</p>'); $interface->assign('noSearch', true); } $template = 'list-none.tpl'; } $interface->assign('setNotification', implode('', $setNotification)); } else { $result = false; $interface->assign('noQuery', true); $template = 'list-none.tpl'; } // 'Finish' the search... complete timers and log search history. $searchObject->close(); $interface->assign('time', round($searchObject->getTotalSpeed(), 2)); // Show the save/unsave code on screen // The ID won't exist until after the search has been put in the search // history so this needs to occur after the close() on the searchObject $interface->assign('showSaved', true); $interface->assign('savedSearch', $searchObject->isSavedSearch()); $interface->assign('searchId', $searchObject->getSearchId()); // Save the URL of this search to the session so we can return to it easily: $_SESSION['lastSearchURL'] = $searchObject->renderSearchUrl(); return $interface->fetch("MetaLib/{$template}"); }
/** * Execute a search. * * @param string $irdList Comma-separated list of IRD IDs * @param array $query The search terms from the Search Object * @param array $filterList The fields and values to filter results on * (currently unused) * @param string $start The record to start with * @param string $limit The number of records to return * @param string $sortBy The value to be used by for sorting * @param array $facets The facets to include (null for defaults) (unused) * @param bool $returnErr On fatal error, should we fail outright (false) or * treat it as an empty result set with an error key set (true)? * * @throws object PEAR Error * @return array An array of query results * @access public */ public function query($irdList, $query, $filterList = null, $start = 1, $limit = 20, $sortBy = null, $facets = null, $returnErr = false) { $queryStr = $this->buildQuery($query); if (!$queryStr) { PEAR::raiseError(new PEAR_Error('Search terms are required')); } $successes = array(); $failed = array(); $disallowed = array(); $irdArray = array(); $irdInfoArray = array(); $authorized = UserAccount::isAuthorized(); $irdListArray = explode(',', $irdList); foreach ($irdListArray as $ird) { $irdInfo = $this->getIRDInfo($ird); if (PEAR::isError($irdInfo)) { error_log('MetaLib cannot get info for ird: ' . $ird); $failed[] = $ird; } else { if (strcasecmp($irdInfo['access'], 'guest') != 0 && !$authorized) { $disallowed[] = $irdInfo; } else { $irdArray[] = $ird; $irdInfoArray[$irdInfo['name']] = $irdInfo; } } } if (empty($irdArray)) { return array('recordCount' => 0, 'failedDatabases' => $failed, 'disallowedDatabases' => $disallowed, 'successDatabases' => $successes); } // Put together final list of IRDs to search $irdList = implode(',', $irdArray); // Use a metalib. prefix everywhere so that it's easy to see the record source $queryId = 'metalib.' . md5($irdList . '_' . $queryStr . '_' . $start . '_' . $limit); $findResults = $this->getCachedResults($queryId); if ($findResults !== false && empty($findResults['failedDatabases']) && empty($findResults['disallowedDatabases'])) { return $findResults; } $options = array(); $options['find_base/find_base_001'] = $irdArray; $options['find_request_command'] = $queryStr; // TODO: add configurable authentication mechanisms to identify authorized // users and switch this to use it $options['requester_ip'] = $_SERVER['REMOTE_ADDR']; // TODO: local highlighting? if ($this->debug) { echo '<pre>Query: '; print_r($options); echo "</pre>\n"; } $sessionId = $this->getSession(); // Do the find request $findRequestId = md5($irdList . '_' . $queryStr); if (isset($_SESSION['MetaLibFindResponse']) && $_SESSION['MetaLibFindResponse']['requestId'] == $findRequestId && $disallowed == $_SESSION['MetaLibFindResponse']['disallowed']) { $databases = $_SESSION['MetaLibFindResponse']['databases']; $totalRecords = $_SESSION['MetaLibFindResponse']['totalRecords']; $failed = $_SESSION['MetaLibFindResponse']['failed']; $successes = $_SESSION['MetaLibFindResponse']['successes']; } else { $options['session_id'] = $sessionId; $options['wait_flag'] = 'Y'; $findResults = $this->callXServer('find_request', $options); if (PEAR::isError($findResults)) { PEAR::raiseError($findResults); } // Gather basic information $databases = array(); $totalRecords = 0; foreach ($findResults->find_response->base_info as $baseInfo) { $databaseName = (string) $baseInfo->full_name; $databaseInfo = isset($irdInfoArray[$databaseName]) ? $irdInfoArray[$databaseName] : (string) $baseInfo->full_name; if ($baseInfo->find_status != 'DONE') { error_log('MetaLib search in ' . $baseInfo->base_001 . ' (' . $baseInfo->full_name . ') failed: ' . $baseInfo->find_error_text); $failed[] = $databaseInfo; } $count = ltrim((string) $baseInfo->no_of_documents, ' 0'); if ($count === '') { continue; } $totalRecords += $count; $databases[] = array('ird' => (string) $baseInfo->base_001, 'count' => $count, 'set' => (string) $baseInfo->set_number, 'records' => array()); $successes[] = $databaseInfo; } $_SESSION['MetaLibFindResponse']['requestId'] = $findRequestId; $_SESSION['MetaLibFindResponse']['databases'] = $databases; $_SESSION['MetaLibFindResponse']['totalRecords'] = $totalRecords; $_SESSION['MetaLibFindResponse']['failed'] = $failed; $_SESSION['MetaLibFindResponse']['disallowed'] = $disallowed; $_SESSION['MetaLibFindResponse']['successes'] = $successes; } $documents = array(); $databaseCount = count($databases); if ($databaseCount > 0) { // Sort the array by number of results usort($databases, function ($a, $b) { return $a['count'] - $b['count']; }); // Find cut points where a database is exhausted of results $sum = 0; for ($k = 0; $k < $databaseCount; $k++) { $sum += ($databases[$k]['count'] - ($k > 0 ? $databases[$k - 1]['count'] : 0)) * ($databaseCount - $k); $databases[$k]['cut'] = $sum; } // Find first item for the given page $firstRecord = ($start - 1) * $limit; $i = 0; $iCount = false; for ($k = 0; $k < $databaseCount; $k++) { if ($iCount === false || $databases[$k]['count'] < $iCount) { if ($databases[$k]['cut'] > $firstRecord) { $i = $k; $iCount = $databases[$k]['count']; } } } $l = $databases[$i]['cut'] - $firstRecord - 1; if ($l < 0) { PEAR::raiseError(new PEAR_Error('Invalid page index')); } $m = $l % ($databaseCount - $i); $startDB = $databaseCount - $m - 1; $startRecord = floor($databases[$i]['count'] - ($l + 1) / ($databaseCount - $i) + 1) - 1; // Loop until we have enough record indices or run out of records from any of the databases $currentDB = $startDB; $currentRecord = $startRecord; $haveRecords = true; for ($count = 0; $count < $limit;) { if ($databases[$currentDB]['count'] > $currentRecord) { $databases[$currentDB]['records'][] = $currentRecord + 1; ++$count; $haveRecords = true; } if (++$currentDB >= $databaseCount) { if (!$haveRecords) { break; } $haveRecords = false; $currentDB = 0; ++$currentRecord; } } // Fetch records $baseIndex = 0; for ($i = 0; $i < $databaseCount; $i++) { $database = $databases[($startDB + $i) % $databaseCount]; ++$baseIndex; if (empty($database['records'])) { continue; } $params = array('session_id' => $sessionId, 'present_command' => array('set_number' => $database['set'], 'set_entry' => $database['records'][0] . '-' . end($database['records']), 'view' => 'full', 'format' => 'marc')); $result = $this->callXServer('present_request', $params); if (PEAR::isError($result)) { PEAR::raiseError($result); } // Go through the records one by one. If there is a MOR tag // in the record, it means that a single record present // command is needed to fetch full record. $currentDocs = array(); $recIndex = -1; foreach ($result->present_response->record as $record) { ++$recIndex; $record->registerXPathNamespace('m', 'http://www.loc.gov/MARC21/slim'); if ($record->xpath("./m:controlfield[@tag='MOR']")) { $params = array('session_id' => $sessionId, 'present_command' => array('set_number' => $database['set'], 'set_entry' => $database['records'][$recIndex], 'view' => 'full', 'format' => 'marc')); $singleResult = $this->callXServer('present_request', $params); if (PEAR::isError($singleResult)) { PEAR::raiseError($singleResult); } $currentDocs[] = $this->process($singleResult->present_response->record[0]); } else { $currentDocs[] = $this->process($record); } } $docIndex = 0; foreach ($currentDocs as $doc) { $foundRecords = true; $documents[sprintf('%09d_%09d', $docIndex++, $baseIndex)] = $doc; } } ksort($documents); $documents = array_values($documents); $i = 1; foreach ($documents as $key => $doc) { $documents[$key]['ID'] = array($queryId . '_' . $i); $i++; } } $results = array('recordCount' => $totalRecords, 'documents' => $documents, 'failedDatabases' => $failed, 'disallowedDatabases' => $disallowed, 'successDatabases' => $successes); $this->putCachedResults($queryId, $results); return $results; }
/** * Constructor * * Sets up the PCI API Client * * @access public */ public function __construct() { global $configArray; if ($configArray['System']['debug']) { $this->debug = true; } $this->config = getExtraConfigArray('PCI'); // Store preferred boolean behavior: if (isset($this->config['General']['case_sensitive_bools'])) { $this->caseSensitiveBooleans = $this->config['General']['case_sensitive_bools']; } // Store highlighting/snippet behavior: if (isset($this->config['General']['highlighting'])) { $this->highlight = $this->config['General']['highlighting']; } if (isset($this->config['General']['snippets'])) { $this->snippets = $this->config['General']['snippets']; } $this->params['wsdl'] = $this->config['General']['wsdl']; $this->params['institution'] = $this->config['General']['institution']; $this->params['onCampus'] = UserAccount::isAuthorized(); $this->params['db'] = isset($this->config['General']['db']) ? $this->config['General']['db'] : null; }
/** * Process parameters and display the page. * * @return void * @access public */ public function launch() { global $interface; global $configArray; $config = getExtraConfigArray("PCI"); // Initialise SearchObject. $this->searchObject->init(); $displayQuery = $this->searchObject->displayQuery(); $interface->setPageTitle(translate('Search Results') . (empty($displayQuery) ? '' : ' - ' . htmlspecialchars($displayQuery))); $interface->assign('lookfor', $displayQuery); $interface->assign('searchIndex', $this->searchObject->getSearchIndex()); $interface->assign('searchType', $this->searchObject->getSearchType()); $interface->assign('searchWithoutFilters', $this->searchObject->renderSearchUrlWithoutFilters()); $interface->assign('searchWithFilters', $this->searchObject->renderSearchUrl()); if ($spatialDateRangeType = $this->searchObject->getSpatialDateRangeFilterType()) { $interface->assign('spatialDateRangeType', $spatialDateRangeType); } // Search PCI $result = $this->searchObject->processSearch(false, true); // We'll need recommendations no matter how many results we found: $interface->assign('qtime', round($this->searchObject->getQuerySpeed(), 2)); $interface->assign('spellingSuggestions', $this->searchObject->getSpellingSuggestions()); $interface->assign('topRecommendations', $this->searchObject->getRecommendationsTemplates('top')); $interface->assign('sideRecommendations', $this->searchObject->getRecommendationsTemplates('side')); // Whether embedded openurl autocheck is enabled if (isset($configArray['OpenURL']['autocheck']) && $configArray['OpenURL']['autocheck']) { $interface->assign('openUrlAutoCheck', true); } // We'll need to assign search parameters for removal link $interface->assign('searchParams', $this->searchObject->renderSearchUrlParams()); // We want to guide the user to login for access to licensed material $interface->assign('methodsAvailable', Login::getActiveAuthorizationMethods()); $interface->assign('userAuthorized', UserAccount::isAuthorized()); if ($showGlobalFiltersNote = $interface->getGlobalFiltersNotification('Primo Central')) { $interface->assign('showGlobalFiltersNote', $showGlobalFiltersNote); } if ($result['recordCount'] > 0) { // If the "jumpto" parameter is set, jump to the specified result index: $this->_processJumpto($result); $summary = $this->searchObject->getResultSummary(); $page = $summary['page']; $interface->assign('recordCount', $summary['resultTotal']); $interface->assign('recordStart', $summary['startRecord']); $interface->assign('recordEnd', $summary['endRecord']); $interface->assign('recordSet', $result['response']['docs']); $interface->assign('sortList', $this->searchObject->getSortList()); $pageLimit = isset($config['General']['result_limit']) ? $config['General']['result_limit'] : 2000; $totalPagerItems = $summary['resultTotal'] < $pageLimit ? $summary['resultTotal'] : $pageLimit; // Process Paging $link = $this->searchObject->renderLinkPageTemplate(); $options = array('totalItems' => $totalPagerItems, 'fileName' => $link, 'perPage' => $summary['perPage']); $pager = new VuFindPager($options); $interface->assign('pageLinks', $pager->getLinks()); // Display Listing of Results $interface->setTemplate('list.tpl'); $interface->assign('subpage', 'PCI/list-list.tpl'); } else { // Don't let bots crawl "no results" pages $this->disallowBots(); $interface->assign('recordCount', 0); // Was the empty result set due to an error? $error = $this->searchObject->getIndexError(); if ($error !== false) { // If it's a parse error or the user specified an invalid field, we // should display an appropriate message: if (stristr($error, 'user.entered.query.is.malformed') || stristr($error, 'unknown.field')) { $interface->assign('parseError', true); } else { // Unexpected error -- let's treat this as a fatal condition. PEAR::raiseError(new PEAR_Error('Unable to process query<br />PCI Returned: ' . $error)); } } if (empty($displayQuery)) { $interface->assign('noQuery', true); } $interface->assign('removeAllFilters', $this->searchObject->renderSearchUrlWithoutFilters(array('prefiltered'))); $interface->setTemplate('list-none.tpl'); } // 'Finish' the search... complete timers and log search history. $this->searchObject->close(); $interface->assign('time', round($this->searchObject->getTotalSpeed(), 2)); // Show the save/unsave code on screen // The ID won't exist until after the search has been put in the search // history so this needs to occur after the close() on the searchObject $interface->assign('showSaved', true); $interface->assign('savedSearch', $this->searchObject->isSavedSearch()); $interface->assign('searchId', $this->searchObject->getSearchId()); // Save the URL of this search to the session so we can return to it easily: $_SESSION['lastSearchURL'] = $this->searchObject->renderSearchUrl(); // Save the display query too, so we can use it e.g. in the breadcrumbs $_SESSION['lastSearchDisplayQuery'] = $displayQuery; // Initialize visFacets for PCI Published Timeline $visFacets = array('search_sdaterange_mv' => array(0 => "", 1 => "", 'label' => "Other")); $interface->assign('visFacets', $visFacets); $interface->display('layout.tpl'); }
/** * Get available search sets * * @return string[] Set IDs */ public function getSearchSets() { $access = UserAccount::isAuthorized() ? 'authorized' : 'guest'; $result = array(); foreach ($this->searchSets as $key => $set) { if (isset($set['access']) && $set['access'] != $access) { continue; } $result[$key] = $set['name']; } return $result; }