(root)/trunk/locum-client.php - Rev 150
Rev 149 |
Rev 151 |
Go to most recent revision |
Blame |
Compare with Previous |
Last modification |
View Log
| RSS feed
<?php
/**
* Locum is a software library that abstracts ILS functionality into a
* catalog discovery layer for use with such things as bolt-on OPACs like
* SOPAC.
* @package Locum
* @author John Blyberg
*/
require_once('locum.php');
/**
* The Locum Client class represents the "front end" of Locum. IE, the interactive piece.
* This is the class you would use to do searches, place holds, get patron info, etc.
* Ideally, this code should never have to be touched.
*/
class locum_client
extends locum
{
/**
* Does an index search via Sphinx and returns the results
*
* @param string $type Search type. Valid types are: author, title, series, subject, keyword (default)
* @param string $term Search term/phrase
* @param int $limit Number of results to return
* @param int $offset Where to begin result set -- for pagination purposes
* @param array $sort_array Numerically keyed array of sort parameters. Valid options are: newest, oldest
* @param array $location_array Numerically keyed array of location params. NOT IMPLEMENTED YET
* @param array $facet_args String-keyed array of facet parameters. See code below for array structure
* @return array String-keyed result set
*/
public function search
($type, $term, $limit, $offset, $sort_opt = NULL, $format_array = array(), $location_array = array(), $facet_args = array(), $override_search_filter = FALSE, $limit_available = FALSE) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($type, $term, $limit, $offset, $sort_opt, $format_array, $location_array, $facet_args, $override_search_filter, $limit_available);
}
require_once($this->locum_config['sphinx_config']['api_path'] . '/sphinxapi.php');
$db =& MDB2
::connect($this->dsn);
$term_arr = explode('?', trim(preg_replace('/\//', ' ', $term)));
$term = trim($term_arr[0
]);
if ($term == '*' || $term == '**') {
$term = '';
} else {
$term_prestrip = $term;
//$term = preg_replace('/[^A-Za-z0-9*\- ]/iD', '', $term);
$term = preg_replace('/\*\*/','*', $term);
}
$final_result_set['term'] = $term;
$final_result_set['type'] = trim($type);
$cl = new SphinxClient
();
$cl->SetServer($this->locum_config['sphinx_config']['server_addr'], (int
) $this->locum_config['sphinx_config']['server_port']);
// Defaults to 'keyword', non-boolean
$bool = FALSE;
$cl->SetMatchMode(SPH_MATCH_ALL
);
if(!$term) {
// Searches for everything (usually for browsing purposes--Hot/New Items, etc..)
$cl->SetMatchMode(SPH_MATCH_ANY
);
} else {
// Is it a boolean search?
if(preg_match("/ \| /i", $term) || preg_match("/ \-/i", $term) || preg_match("/ \!/i", $term)) {
$cl->SetMatchMode(SPH_MATCH_BOOLEAN
);
$bool = TRUE;
}
if(preg_match("/ OR /i", $term)) {
$cl->SetMatchMode(SPH_MATCH_BOOLEAN
);
$term = preg_replace('/ OR /i',' | ',$term);
$bool = TRUE;
}
// Is it a phrase search?
if(preg_match("/\"/i", $term) || preg_match("/\@/i", $term)) {
$cl->SetMatchMode(SPH_MATCH_EXTENDED2
);
$bool = TRUE;
}
}
// Set up for the various search types
switch ($type) {
case 'author':
$cl->SetFieldWeights(array('author' => 50, 'addl_author' => 30
));
$idx = 'bib_items_author';
break;
case 'title':
$cl->SetFieldWeights(array('title' => 50, 'title_medium' => 50, 'series' => 30
));
$idx = 'bib_items_title';
break;
case 'series':
$cl->SetFieldWeights(array('title' => 5, 'series' => 80
));
$idx = 'bib_items_title';
break;
case 'subject':
$idx = 'bib_items_subject';
break;
case 'callnum':
$cl->SetFieldWeights(array('callnum' => 100
));
$idx = 'bib_items_callnum';
//$cl->SetMatchMode(SPH_MATCH_ANY);
break;
case 'tags':
$cl->SetFieldWeights(array('tag_idx' => 100
));
$idx = 'bib_items_tags';
$cl->SetMatchMode(SPH_MATCH_PHRASE
);
break;
case 'reviews':
$cl->SetFieldWeights(array('review_idx' => 100
));
$idx = 'bib_items_reviews';
break;
case 'keyword':
default:
$cl->SetFieldWeights(array('title' => 50, 'title_medium' => 50, 'author' => 70, 'addl_author' => 40, 'tag_idx' =>35, 'series' => 25, 'review_idx' => 10, 'notes' => 10, 'subjects' => 5
));
$idx = 'bib_items_keyword';
break;
}
// Filter out the records we don't want shown, per locum.ini
if (!$override_search_filter) {
if (trim($this->locum_config['location_limits']['no_search'])) {
$cfg_filter_arr = parent
::csv_parser($this->locum_config['location_limits']['no_search']);
foreach ($cfg_filter_arr as $cfg_filter) {
$cfg_filter_vals[] = parent
::string_poly($cfg_filter);
}
$cl->SetFilter('loc_code', $cfg_filter_vals, TRUE);
}
}
// Valid sort types are 'newest' and 'oldest'. Default is relevance.
switch($sort_opt) {
case 'newest':
$cl->SetSortMode(SPH_SORT_EXTENDED
, 'pub_year DESC, @relevance DESC');
break;
case 'oldest':
$cl->SetSortMode(SPH_SORT_EXTENDED
, 'pub_year ASC, @relevance DESC');
break;
case 'catalog_newest':
$cl->SetSortMode(SPH_SORT_EXTENDED
, 'bib_created DESC, @relevance DESC');
break;
case 'catalog_oldest':
$cl->SetSortMode(SPH_SORT_EXTENDED
, 'bib_created ASC, @relevance DESC');
break;
case 'title':
$cl->SetSortMode(SPH_SORT_ATTR_ASC
, 'title_ord');
break;
case 'author':
$cl->SetSortMode(SPH_SORT_EXTENDED
, 'author_null ASC, author_ord ASC');
break;
case 'top_rated':
$cl->SetSortMode(SPH_SORT_ATTR_DESC
, 'rating_idx');
break;
case 'popular_week':
$cl->SetSortMode(SPH_SORT_ATTR_DESC
, 'hold_count_week');
break;
case 'popular_month':
$cl->SetSortMode(SPH_SORT_ATTR_DESC
, 'hold_count_month');
break;
case 'popular_year':
$cl->SetSortMode(SPH_SORT_ATTR_DESC
, 'hold_count_year');
break;
case 'popular_total':
$cl->SetSortMode(SPH_SORT_ATTR_DESC
, 'hold_count_total');
break;
case 'atoz':
$cl->SetSortMode(SPH_SORT_ATTR_ASC
, 'title_ord');
break;
case 'ztoa':
$cl->SetSortMode(SPH_SORT_ATTR_DESC
, 'title_ord');
break;
default:
if ($type == 'title') {
// We get better results in title matches if we also rank by title length
$cl->SetSortMode(SPH_SORT_EXTENDED
, 'titlelength ASC, @relevance DESC');
} else {
$cl->SetSortMode(SPH_SORT_EXTENDED
, '@relevance DESC, titlelength ASC');
}
break;
}
// Filter by material types
if (is_array($format_array)) {
foreach ($format_array as $format) {
if (strtolower($format) != 'all') {
$filter_arr_mat[] = parent
::string_poly(trim($format));
}
}
if (count($filter_arr_mat)) { $cl->SetFilter('mat_code', $filter_arr_mat); }
}
// Filter by location
if (count($location_array)) {
foreach ($location_array as $location) {
if (strtolower($location) != 'all') {
$filter_arr_loc[] = parent
::string_poly(trim($location));
}
}
if (count($filter_arr_loc)) { $cl->SetFilter('loc_code', $filter_arr_loc); }
}
$cl->SetRankingMode(SPH_RANK_WORDCOUNT
);
$cl->SetLimits(0
, 5000
, 5000
);
$sph_res_all = $cl->Query($term, $idx); // Grab all the data for the facetizer
// If original match didn't return any results, try a proximity search
if(empty($sph_res_all['matches']) && $bool == FALSE && $term != "*" && $type != "tags") {
$term = '"' . $term . '"/1';
$cl->SetMatchMode(SPH_MATCH_EXTENDED2
);
$sph_res_all = $cl->Query($term, $idx);
$forcedchange = 'yes';
}
// Paging/browsing through the result set.
$cl->SetLimits((int
) $offset, (int
) $limit);
// And finally.... we search.
$sph_res = $cl->Query($term, $idx);
// Include descriptors
$final_result_set['num_hits'] = $sph_res['total'];
if ($sph_res['total'] <= $this->locum_config['api_config']['suggestion_threshold']) {
if ($this->locum_config['api_config']['use_yahoo_suggest'] == TRUE) {
$final_result_set['suggestion'] = self::yahoo_suggest($term_prestrip);
}
}
if (is_array($sph_res['matches'])) {
foreach ($sph_res['matches'] as $bnum => $attr) {
$bib_hits[] = $bnum;
}
}
if (is_array($sph_res_all['matches'])) {
foreach ($sph_res_all['matches'] as $bnum => $attr) {
$bib_hits_all[] = $bnum;
}
}
// Limit list to available
if ($limit_available && $final_result_set['num_hits'] && (array_key_exists($limit_available, $this->locum_config['branches']) || $limit_available == 'any')) {
$limit_available = trim(strval($limit_available));
// Remove bibs that we know are not available
$cache_cutoff = date("Y-m-d H:i:00", time() - (60
* $this->locum_config['avail_cache']['cache_cutoff']));
// Remove bibs that are not in this location
$utf = "SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'";
$utfprep = $db->query($utf);
$sql = "SELECT bnum, branch, count_avail FROM locum_avail_branches WHERE bnum IN (" . implode(", ", $bib_hits_all) . ") AND timestamp > '$cache_cutoff'";
$init_result =& $db->query($sql);
if ($init_result) {
$branch_info_cache = $init_result->fetchAll(MDB2_FETCHMODE_ASSOC
);
$bad_bibs = array();
$good_bibs = array();
foreach ($branch_info_cache as $item_binfo) {
if (($item_binfo['branch'] == $limit_available || $limit_available == 'any') && $item_binfo['count_avail'] > 0
) {
if (!in_array($item_binfo['bnum'], $good_bibs)) {
$good_bibs[] = $item_binfo['bnum'];
}
} else {
$bad_bibs[] = $item_binfo['bnum'];
}
}
}
$unavail_bibs = array_values(array_diff($bad_bibs, $good_bibs));
$bib_hits_all = array_values(array_diff($bib_hits_all, $unavail_bibs));
// rebuild from the full list
unset($bib_hits);
$available_count = 0;
foreach ($bib_hits_all as $key => $bib_hit) {
$bib_avail = self::get_item_status($bib_hit);
if ($limit_available == 'any') {
$available = $bib_avail['avail'];
} else {
$available = $bib_avail['branches'][$limit_available]['avail'];
}
if ($available) {
$available_count++;
if ($available_count > $offset) {
$bib_hits[] = $bib_hit;
if (count($bib_hits) == $limit) {
//found as many as we need for this page
break;
}
}
} else {
// remove the bib from the bib_hits_all array
unset($bib_hits_all[$key]);
}
}
// trim out the rest of the array based on *any* cache value
if(!empty($bib_hits_all)) {
$sql = "SELECT bnum FROM locum_avail_branches WHERE bnum IN (" . implode(",", $bib_hits_all) . ") AND count_avail > 0";
$init_result =& $db->query($sql);
if ($init_result) {
$avail_bib_arr = $init_result->fetchCol();
foreach ($bib_hits_all as $bnum_avail_chk) {
if (in_array($bnum_avail_chk, $avail_bib_arr)) {
$new_bib_hits_all[] = $bnum_avail_chk;
}
}
}
$bib_hits_all = $new_bib_hits_all;
unset($new_bib_hits_all);
}
}
// Refine by facets
if (count($facet_args)) {
$where = '';
// Series
if ($facet_args['facet_series']) {
$where .= ' AND (';
$or = '';
foreach ($facet_args['facet_series'] as $series) {
$where .= $or . ' series LIKE \'' . $db->escape($series, 'text') . '%\'';
$or = ' OR';
}
$where .= ')';
}
// Language
if ($facet_args['facet_lang']) {
foreach ($facet_args['facet_lang'] as $lang) {
$lang_arr[] = $db->quote($lang, 'text');
}
$where .= ' AND lang IN (' . implode(', ', $lang_arr) . ')';
}
// Pub. Year
if ($facet_args['facet_year']) {
$where .= ' AND pub_year IN (' . implode(', ', $facet_args['facet_year']) . ')';
}
// Pub. Decade
if ($facet_args['facet_decade']) {
$where .= ' AND pub_decade IN (' . implode(', ', $facet_args['facet_decade']) . ')';
}
// Ages
if (count($facet_args['age'])) {
$age_or = '';
$age_sql_cond = '';
foreach ($facet_args['age'] as $facet_age) {
$age_sql_cond .= $age_or . "age = '$facet_age'";
$age_or = ' OR ';
}
$sql = 'SELECT DISTINCT(bnum) FROM locum_avail_ages WHERE bnum IN (' . implode(', ', $bib_hits_all) . ") AND ($age_sql_cond)";
$init_result =& $db->query($sql);
$age_hits = $init_result->fetchCol();
foreach ($bib_hits_all as $bnum_age_chk) {
if (in_array($bnum_age_chk, $age_hits)) {
$new_bib_hits_all[] = $bnum_age_chk;
}
}
$bib_hits_all = $new_bib_hits_all;
unset($new_bib_hits_all);
}
if(!empty($bib_hits_all)) {
$sql1 = 'SELECT bnum FROM locum_facet_heap WHERE bnum IN (' . implode(', ', $bib_hits_all) . ')' . $where;
$sql2 = 'SELECT bnum FROM locum_facet_heap WHERE bnum IN (' . implode(', ', $bib_hits_all) . ')' . $where . " LIMIT $offset, $limit";
$utf = "SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'";
$utfprep = $db->query($utf);
$init_result =& $db->query($sql1);
$bib_hits_all = $init_result->fetchCol();
$init_result =& $db->query($sql2);
$bib_hits = $init_result->fetchCol();
}
}
// Get the totals
$facet_total = count($bib_hits_all);
$final_result_set['num_hits'] = $facet_total;
// First, we have to get the values back, unsorted against the Sphinx-sorted array
if (count($bib_hits)) {
$sql = 'SELECT * FROM locum_bib_items WHERE bnum IN (' . implode(', ', $bib_hits) . ')';
$utf = "SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'";
$utfprep = $db->query($utf);
$init_result =& $db->query($sql);
$init_bib_arr = $init_result->fetchAll(MDB2_FETCHMODE_ASSOC
);
foreach ($init_bib_arr as $init_bib) {
// Get availability
$init_bib['availability'] = self::get_item_status($init_bib['bnum']);
// Clean up the Stdnum
$init_bib['stdnum'] = preg_replace('/[^\d]/','', $init_bib['stdnum']);
$bib_reference_arr[(string
) $init_bib['bnum']] = $init_bib;
}
// Now we reconcile against the sphinx result
foreach ($sph_res_all['matches'] as $sph_bnum => $sph_binfo) {
if (in_array($sph_bnum, $bib_hits)) {
$final_result_set['results'][] = $bib_reference_arr[$sph_bnum];
}
}
}
$db->disconnect();
$final_result_set['facets'] = self::facetizer($bib_hits_all);
if($forcedchange == 'yes') { $final_result_set['changed'] = 'yes'; }
return $final_result_set;
}
/**
* Formulates the array used to put together the faceted search panel.
* This function is called from the search function.
*
* @param array $bib_hits_all Standard array of bib numbers
* @return array Faceted array of information for bib numbers passed. Keyed by: mat, series, loc, lang, pub_year
*/
public function facetizer
($bib_hits_all) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($bib_hits_all);
}
$db =& MDB2
::connect($this->dsn);
if (count($bib_hits_all)) {
$where_str = 'WHERE bnum IN (' . implode(",", $bib_hits_all) . ')';
$sql['mat'] = 'SELECT DISTINCT mat_code, COUNT(mat_code) AS mat_code_sum FROM locum_facet_heap ' . $where_str . 'GROUP BY mat_code ORDER BY mat_code_sum DESC';
$sql['series'] = 'SELECT DISTINCT series, COUNT(series) AS series_sum FROM locum_facet_heap ' . $where_str . 'GROUP BY series ORDER BY series ASC';
$sql['loc'] = 'SELECT DISTINCT loc_code, COUNT(loc_code) AS loc_code_sum FROM locum_facet_heap ' . $where_str . 'GROUP BY loc_code ORDER BY loc_code_sum DESC';
$sql['lang'] = 'SELECT DISTINCT lang, COUNT(lang) AS lang_sum FROM locum_facet_heap ' . $where_str . 'GROUP BY lang ORDER BY lang_sum DESC';
$sql['pub_year'] = 'SELECT DISTINCT pub_year, COUNT(pub_year) AS pub_year_sum FROM locum_facet_heap ' . $where_str . 'GROUP BY pub_year ORDER BY pub_year DESC';
$sql['pub_decade'] = 'SELECT DISTINCT pub_decade, COUNT(pub_decade) AS pub_decade_sum FROM locum_facet_heap ' . $where_str . ' GROUP BY pub_decade ORDER BY pub_decade DESC';
foreach ($sql AS $fkey => $fquery) {
$tmp_res =& $db->query($fquery);
$tmp_res_arr = $tmp_res->fetchAll();
foreach ($tmp_res_arr as $values) {
if ($values[0
] && $values[1
]) { $result[$fkey][$values[0
]] = $values[1
]; }
}
}
// Create non-distinct facets for age
foreach ($this->locum_config['ages'] as $age_code => $age_name) {
$sql = "SELECT COUNT(bnum) as age_sum FROM locum_avail_ages $where_str AND age = '$age_code'";
$res =& $db->query($sql);
$age_count = $res->fetchOne();
if ($age_count) {
$result['ages'][$age_code] = $age_count;
}
}
// Create facets from availability cache
$result['avail']['any'] = 0;
foreach ($this->locum_config['branches'] as $branch_code => $branch_name) {
$sql = "SELECT COUNT(DISTINCT(bnum)) FROM locum_avail_branches $where_str AND branch = '$branch_code' AND count_avail > 0";
$res =& $db->query($sql);
$avail_count = $res->fetchOne();
if (!$avail_count) { $avail_count = 0; }
$result['avail']['any'] = $result['avail']['any'] + $avail_count;
if ($avail_count) {
$result['avail'][$branch_code] = $avail_count;
}
}
$db->disconnect();
return $result;
}
}
/**
* Returns an array of item status info (availability, location, status, etc).
*
* @param string $bnum Bib number
* @return array Detailed item availability
*/
public function get_item_status
($bnum, $force_refresh = FALSE) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($bnum, $force_refresh);
}
$db = MDB2
::connect($this->dsn);
if (!$force_refresh && $this->locum_config['avail_cache']['cache']) {
$this->locum_config['avail_cache']['cache_cutoff'];
$cache_cutoff = date("Y-m-d H:i:s", (time() - (60
* $this->locum_config['avail_cache']['cache_cutoff'])));
// check the cache table
$sql = "SELECT * FROM locum_availability WHERE bnum = :bnum AND timestamp > '$cache_cutoff'";
$statement = $db->prepare($sql, array('integer'));
$dbr = $statement->execute(array('bnum' => $bnum));
if (PEAR
::isError($dbr) && $this->cli) {
echo "DB connection failed... " . $dbr->getMessage() . "\n";
}
$statement->Free();
$cached = $dbr->NumRows();
}
if ($cached) {
$row = $dbr->fetchRow(MDB2_FETCHMODE_ASSOC
);
$avail_array = unserialize($row['available']);
return $avail_array;
}
$status = $this->locum_cntl->item_status($bnum);
$result['total'] = count($status['items']);
$result['avail'] = 0;
$result['holds'] = $status['holds'];
$result['on_order'] = $status['on_order'];
$result['orders'] = count($status['orders']) ?
$status['orders'] : array();
$result['nextdue'] = 0;
$result['items'] = $status['items'];
$result['locations'] = array();
$result['callnums'] = array();
$result['ages'] = array();
$result['branches'] = array();
$loc_codes = array();
if (count($status['items'])) {
foreach ($status['items'] as $item) {
// Parse Ages
$result['locations'][$item['loc_code']][$item['age']]++;
if ($result['ages'][$item['age']]) {
$result['ages'][$item['age']]['avail'] = $result['ages'][$item['age']]['avail'] + $item['avail'];
$result['ages'][$item['age']]['total']++;
} else {
$result['ages'][$item['age']]['avail'] = $item['avail'];
$result['ages'][$item['age']]['total'] = 1;
}
// Parse Branches
if (count($result['branches'][$item['branch']])) {
$result['branches'][$item['branch']]['avail'] = $result['branches'][$item['branch']]['avail'] + $item['avail'];
$result['branches'][$item['branch']]['total']++;
} else {
$result['branches'][$item['branch']]['avail'] = $item['avail'];
$result['branches'][$item['branch']]['total'] = 1;
}
// Parse Callnums
if (!in_array($item['callnum'], $result['callnums'])) {
$result['callnums'][] = $item['callnum'];
}
// Determine next item due date
if ($result['nextdue'] == 0
|| $result['nextdue'] > $item['due']) {
$result['nextdue'] = $item['due'];
}
// Parse location code
if (!in_array($item['loc_code'], $loc_codes) && trim($item['loc_code'])) {
$loc_codes[] = $item['loc_code'];
}
// Tally availability
if ($item['avail']) {
$result['avail'] = $result['avail'] + $item['avail'];
}
}
}
// Cache the result
$bib_item = self::get_bib_item($bnum);
// Update Cache
$avail_ser = serialize($result);
$ages = count($result['ages']) ?
"'" . implode(',', $result['ages']) . "'" : 'NULL';
$locs = count($loc_codes) ?
"'" . implode(',', $loc_codes) . "'" : 'NULL';
$bib_loc = $bib_item['loc_code'] ?
"'" . $bib_item['loc_code'] . "'" : 'NULL';
$sql = "REPLACE INTO locum_availability (bnum, available) VALUES (:bnum, :available)";
$statement = $db->prepare($sql, array('integer', 'text'));
$dbr = $statement->execute(array('bnum' => $bnum, 'available' => $avail_ser));
if (PEAR
::isError($dbr) && $this->cli) {
echo "DB connection failed... " . $dbr->getMessage() . "\n";
}
$statement->Free();
// Store age cache
$db->query("DELETE FROM locum_avail_ages WHERE bnum = '$bnum'");
if (count($result['ages'])) {
$sql = "INSERT INTO locum_avail_ages (bnum, age, count_avail, count_total, timestamp) VALUES (:bnum, :age, :count_avail, :count_total, NOW())";
$statement = $db->prepare($sql, array('integer', 'text', 'integer', 'integer'));
foreach ($result['ages'] as $age => $age_info) {
$dbr = $statement->execute(array('bnum' => $bnum, 'age' => $age, 'count_avail' => $age_info['avail'], 'count_total' => $age_info['total']));
}
$statement->Free();
}
// Store branch info cache
$db->query("DELETE FROM locum_avail_branches WHERE bnum = '$bnum'");
if (count($result['branches'])) {
$sql = "INSERT INTO locum_avail_branches (bnum, branch, count_avail, count_total, timestamp) VALUES (:bnum, :branch, :count_avail, :count_total, NOW())";
$statement = $db->prepare($sql, array('integer', 'text', 'integer', 'integer'));
foreach ($result['branches'] as $branch => $branch_info) {
$dbr = $statement->execute(array('bnum' => $bnum, 'branch' => $branch, 'count_avail' => $branch_info['avail'], 'count_total' => $branch_info['total']));
}
$statement->Free();
}
return $result;
}
/**
* Returns information about a bib title.
*
* @param string $bnum Bib number
* @param boolean $get_inactive Return records whose active = 0
* @return array Bib item information
*/
public function get_bib_item
($bnum, $get_inactive = FALSE) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($bnum);
}
$db = MDB2
::connect($this->dsn);
$utf = "SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'";
$utfprep = $db->query($utf);
if ($get_inactive) {
$sql = "SELECT * FROM locum_bib_items WHERE bnum = '$bnum' LIMIT 1";
} else {
$sql = "SELECT * FROM locum_bib_items WHERE bnum = '$bnum' AND active = '1' LIMIT 1";
}
$res = $db->query($sql);
$item_arr = $res->fetchAll(MDB2_FETCHMODE_ASSOC
);
$db->disconnect();
$item_arr[0]['stdnum'] = preg_replace('/[^\d]/','', $item_arr[0]['stdnum']);
return $item_arr[0
];
}
/**
* Returns information about an array of bib titles.
*
* @param array $bnum_arr Bib number array
* @return array Bib item information for $bnum_arr
*/
public function get_bib_items_arr
($bnum_arr) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($bnum_arr);
}
if (count($bnum_arr)) {
$db =& MDB2
::connect($this->dsn);
$utf = "SET NAMES 'utf8' COLLATE 'utf8_unicode_ci'";
$utfprep = $db->query($utf);
$sql = 'SELECT * FROM locum_bib_items WHERE bnum IN (' . implode(', ', $bnum_arr) . ')';
$res =& $db->query($sql);
$item_arr = $res->fetchAll(MDB2_FETCHMODE_ASSOC
);
$db->disconnect();
foreach ($item_arr as $item) {
$item['stdnum'] = preg_replace('/[^\d]/','', $item['stdnum']);
$bib[(string
) $item['bnum']] = $item;
}
}
return $bib;
}
/**
* Returns an array of patron information
*
* @param string $pid Patron barcode number or record number
* @return boolean|array Array of patron information or FALSE if login fails
*/
public function get_patron_info
($pid) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($pid);
}
$patron_info = $this->locum_cntl->patron_info($pid);
return $patron_info;
}
/**
* Returns an array of patron checkouts
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @return boolean|array Array of patron checkouts or FALSE if $barcode doesn't exist
*/
public function get_patron_checkouts
($cardnum, $pin = NULL) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $pin);
}
$patron_checkouts = $this->locum_cntl->patron_checkouts($cardnum, $pin);
return $patron_checkouts;
}
/**
* Returns an array of patron checkouts for history
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @param array $last_record Array containing: 'bnum' => Bib num, 'date' => Date of last record harvested.
* It will return everything after that record if this value is passed
* @return boolean|array Array of patron checkouts or FALSE if $barcode doesn't exist
*/
public function get_patron_checkout_history
($cardnum, $pin = NULL, $last_record = NULL) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $pin);
}
return $this->locum_cntl->patron_checkout_history($cardnum, $pin, $action);
}
/**
* Opts patron in or out of checkout history
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @return boolean|array Array of patron checkouts or FALSE if $barcode doesn't exist
*/
public function set_patron_checkout_history
($cardnum, $pin = NULL, $action = NULL) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $pin, $action);
}
return $this->locum_cntl->patron_checkout_history_toggle($cardnum, $pin, $action);
}
/**
* Deletes patron checkout history off the ILS server
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @param string $action NULL = do nothing, 'all' = delete all records, 'selected' = Delete records in $vars array
* @param array $vars array of variables referring to records to delete (optional)
* @param array $last_record Array containing: 'bnum' => Bib num, 'date' => Date of last record harvested
*/
public function delete_patron_checkout_history
($cardnum, $pin = NULL, $action = NULL, $vars = NULL, $last_record = NULL) {
}
/**
* Returns an array of patron holds
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @return boolean|array Array of patron holds or FALSE if login fails
*/
public function get_patron_holds
($cardnum, $pin = NULL) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $pin);
}
$patron_holds = $this->locum_cntl->patron_holds($cardnum, $pin);
return $patron_holds;
}
/**
* Renews items and returns the renewal result
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @param array Array of varname => item numbers to be renewed, or NULL for everything.
* @return boolean|array Array of item renewal statuses or FALSE if it cannot renew for some reason
*/
public function renew_items
($cardnum, $pin = NULL, $items = NULL) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $pin, $items);
}
$renew_status = $this->locum_cntl->renew_items($cardnum, $pin, $items);
return $renew_status;
}
/**
* Updates holds/reserves
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @param array $cancelholds Array of varname => item/bib numbers to be cancelled, or NULL for everything.
* @param array $holdfreezes_to_update Array of updated holds freezes.
* @param array $pickup_locations Array of pickup location changes.
* @return boolean TRUE or FALSE if it cannot cancel for some reason
*/
public function update_holds
($cardnum, $pin = NULL, $cancelholds = array(), $holdfreezes_to_update = array(), $pickup_locations = array()) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $pin, $cancelholds, $holdfreezes_to_update, $pickup_locations);
}
return $this->locum_cntl->update_holds($cardnum, $pin, $cancelholds, $holdfreezes_to_update, $pickup_locations);
}
/**
* Places holds
*
* @param string $cardnum Patron barcode/card number
* @param string $bnum Bib item record number to place a hold on
* @param string $varname additional variable name (such as an item number for item-level holds) to place a hold on
* @param string $pin Patron pin/password
* @param string $pickup_loc Pickup location value
* @return boolean TRUE or FALSE if it cannot place the hold for some reason
*/
public function place_hold
($cardnum, $bnum, $varname = NULL, $pin = NULL, $pickup_loc = NULL) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $bnum, $varname, $pin, $pickup_loc);
}
$request_status = $this->locum_cntl->place_hold($cardnum, $bnum, $varname, $pin, $pickup_loc);
if ($request_status['success']) {
$db =& MDB2
::connect($this->dsn);
$db->query("INSERT INTO locum_holds_placed VALUES ('$bnum', NOW())");
}
return $request_status;
}
/**
* Returns an array of patron fines
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @return boolean|array Array of patron holds or FALSE if login fails
*/
public function get_patron_fines
($cardnum, $pin = NULL) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $pin);
}
$patron_fines = $this->locum_cntl->patron_fines($cardnum, $pin);
return $patron_fines;
}
/**
* Pays patron fines.
* $payment_details structure:
* [varnames] = An array of varnames to id which fines to pay.
* [total] = payment total.
* [name] = Name on the credit card.
* [address1] = Billing address.
* [address2] = Billing address. (opt)
* [city] = Billing address city.
* [state] = Billing address state.
* [zip] = Billing address zip.
* [email] = Cardholder email address.
* [ccnum] = Credit card number.
* [ccexpmonth] = Credit card expiration date.
* [ccexpyear] = Credit card expiration year.
* [ccseccode] = Credit card security code.
*
* @param string $cardnum Patron barcode/card number
* @param string $pin Patron pin/password
* @param array payment_details
* @return array Payment result
*/
public function pay_patron_fines
($cardnum, $pin = NULL, $payment_details) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($cardnum, $pin, $payment_details);
}
$payment_result = $this->locum_cntl->pay_patron_fines($cardnum, $pin, $payment_details);
return $payment_result;
}
/*
* Returns an array of random bibs.
*/
public function get_bib_numbers
($limit = 10
) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($limit);
}
$db =& MDB2
::connect($this->dsn);
$res =& $db->query("SELECT bnum FROM locum_bib_items ORDER BY RAND() LIMIT $limit");
$item_arr = $res->fetchAll(MDB2_FETCHMODE_ASSOC
);
$db->disconnect();
$bnums = array();
foreach ($item_arr as $item) {
$bnums[] = $item['bnum'];
}
return $bnums;
}
/************ External Content Functions ************/
/**
* Formulates "Did you mean?" I may move to the Yahoo API for this..
*
* @param string $str String to check
* @return string|boolean Either returns a string suggestion or FALSE
*/
public function yahoo_suggest
($str) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($str);
}
if (trim($str) && $this->locum_config['api_config']['yahh_app_id']) {
$appid = $this->locum_config['api_config']['yahh_app_id'];
} else {
$appid = 'YahooDemo';
}
$url = 'http://boss.yahooapis.com/ysearch/spelling/v1/'.$str.'?format=xml&appid=' . $appid;
$suggest_obj = @simplexml_load_file($url);
if (trim($suggest_obj->resultset_spell->result->suggestion)) {
return trim($suggest_obj->resultset_spell->result->suggestion);
} else {
return FALSE;
}
}
/*
* Client-side version of get_syndetics(). Does not harvest, only checks the database.
*/
public function get_syndetics
($isbn) {
if (is_callable(array(__CLASS__ . '_hook', __FUNCTION__))) {
eval('$hook = new ' . __CLASS__ . '_hook;');
return $hook->{__FUNCTION__}($isbn);
}
$cust_id = $this->locum_config['api_config']['syndetic_custid'];
if (!$cust_id) {
return NULL;
}
$valid_hits = array(
'TOC' => 'Table of Contents',
'BNATOC' => 'Table of Contents',
'FICTION' => 'Fiction Profile',
'SUMMARY' => 'Summary / Annotation',
'DBCHAPTER' => 'Excerpt',
'LJREVIEW' => 'Library Journal Review',
'PWREVIEW' => 'Publishers Weekly Review',
'SLJREVIEW' => 'School Library Journal Review',
'CHREVIEW' => 'CHOICE Review',
'BLREVIEW' => 'Booklist Review',
'HORNBOOK' => 'Horn Book Review',
'KIRKREVIEW' => 'Kirkus Book Review',
'ANOTES' => 'Author Notes'
);
$db =& MDB2
::connect($this->dsn);
$res = $db->query("SELECT links FROM locum_syndetics_links WHERE isbn = '$isbn' AND updated > DATE_SUB(NOW(), INTERVAL 2 MONTH) LIMIT 1");
$dbres = $res->fetchAll(MDB2_FETCHMODE_ASSOC
);
if ($dbres[0]['links']) {
$links = explode('|', $dbres[0]['links']);
} else {
return FALSE;
}
if ($links) {
foreach ($links as $link) {
$link_result[$valid_hits[$link]] = 'http://www.syndetics.com/index.aspx?isbn=' . $isbn . '/' . $link . '.html&client=' . $cust_id;
}
}
$db->disconnect();
return $link_result;
}
}