Subversion Repositories connectors

Rev

Rev 93 | Rev 95 | 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
 * @category Locum Connector
 * @author John Blyberg
 */


/**
 * The Locum connector class for III Mil. 2006.
 * Note the naming convention that is required by Locum: locum _ vendor _ version
 * Also, from a philisophical standpoint, I try to use only public-facing services here,
 * with the exception of the III patron API, which is a product we bought, along with
 * practically every other III customer.
 */

class locum_iii_2007 {

        public $locum_config;

        /**
         * Prep this class
         */

        public function __construct() {
                require_once('patronapi.php');
        }

        /**
         * Grabs bib info from XRECORD and returns it in a Locum-ready array.
         *
         * @param int $bnum Bib number to scrape
         * @param boolean $skip_cover Forget about grabbing cover images.  Default: FALSE
         * @return boolean|array Will either return a Locum-ready array or FALSE
         */

        public function scrape_bib($bnum, $skip_cover = FALSE) {

                $iii_webcat = $this->locum_config[ils_config][ils_server];
                $iii_webcat_port = $this->locum_config[ils_config][ils_harvest_port];

                $bnum = trim($bnum);

                $xrecord = @simplexml_load_file('http://' . $iii_webcat . ':' . $iii_webcat_port . '/xrecord=b' . $bnum);

                // If there is no record, return false (weeded or non-existent)
                if ($xrecord->NULLRECORD) {
                        return FALSE;
                }
                if ($xrecord->VARFLD) {
                        if (!$xrecord->VARFLD[0]->MARCINFO) {
                                return FALSE;
                        }
                } else {
                        return FALSE;
                }

                $bib_info_record = $xrecord->RECORDINFO;
                $bib_info_local = $xrecord->TYPEINFO->BIBLIOGRAPHIC->FIXFLD;
                $bib_info_marc = self::parse_marc_subfields($xrecord->VARFLD);
                unset($xrecord);

                // Process record information
                $bib[bnum] = $bnum;
                $bib[bib_created] = self::fixdate($bib_info_record->CREATEDATE);
                $bib[bib_lastupdate] = self::fixdate($bib_info_record->LASTUPDATEDATE);
                $bib[bib_prevupdate] = self::fixdate($bib_info_record->PREVUPDATEDATE);
                $bib[bib_revs] = (int) $bib_info_record->REVISIONS;

                // Process local record data
                foreach ($bib_info_local as $bil_obj) {
                        switch (trim($bil_obj->FIXLABEL)) {
                                case 'LANG':
                                        $bib[lang] = trim($bil_obj->FIXVALUE);
                                        break;
                                case 'LOCATION':
                                        $bib[loc_code] = trim($bil_obj->FIXVALUE);
                                        break;
                                case 'MAT TYPE':
                                        $bib[mat_code] = trim($bil_obj->FIXVALUE);
                                        break;

                        }
                }

                // Process MARC fields

                // Process Author information
                $bib[author] = '';
                $author_arr = self::prepare_marc_values($bib_info_marc['100'], array('a','b','c','d'));
                $bib[author] = $author_arr[0];

                // In no author info, we'll go for the 110 field
                if (!$bib[author]) {
                        $author_110 = self::prepare_marc_values($bib_info_marc['110'], array('a'));
                        $bib[author] = $author_110[0];
                }

                // Additional author information
                $bib[addl_author] = '';
                $addl_author = self::prepare_marc_values($bib_info_marc['700'], array('a','b','c','d'));
                if (is_array($addl_author)) {
                        $bib[addl_author] = serialize($addl_author);
                }

                // In no additional author info, we'll go for the 710 field
                if (!$bib[addl_author]) {
                        $author_710 = self::prepare_marc_values($bib_info_marc['710'], array('a'));
                        if (is_array($author_710)) {
                                $bib[addl_author] = serialize($author_710);
                        }
                }

                // Title information
                $bib[title] = '';
                $title = self::prepare_marc_values($bib_info_marc['245'], array('a','b'));
                if (substr($title[0], -1) == '/') { $title[0] = trim(substr($title[0], 0, -1)); }
                $bib[title] = trim($title[0]);

                // Title medium information
                $bib[title_medium] = '';
                $title_medium = self::prepare_marc_values($bib_info_marc['245'], array('h'));
                if ($title_medium[0]) {
                        if (preg_match('/\[(.*?)\]/', $title_medium[0], $medium_match)) {
                                $bib[title_medium] = $medium_match[1];
                        }
                }
               
                // Edition information
                $bib[edition] = '';
                $edition = self::prepare_marc_values($bib_info_marc['250'], array('a'));
                $bib[edition] = trim($edition[0]);

                // Series information
                $bib[series] = '';
                $series = self::prepare_marc_values($bib_info_marc['490'], array('a','v'));
                if (!$series[0]) { $series = self::prepare_marc_values($bib_info_marc['440'], array('a','v')); }
                if (!$series[0]) { $series = self::prepare_marc_values($bib_info_marc['400'], array('a','v')); }
                if (!$series[0]) { $series = self::prepare_marc_values($bib_info_marc['410'], array('a','v')); }
                if (!$series[0]) { $series = self::prepare_marc_values($bib_info_marc['800'], array('a','v')); }
                if (!$series[0]) { $series = self::prepare_marc_values($bib_info_marc['810'], array('a','v')); }
                $bib[series] = $series[0];

                // Call number
                $callnum = '';
                $callnum_arr = self::prepare_marc_values($bib_info_marc['099'], array('a'));
                if (is_array($callnum_arr) && count($callnum_arr)) {
                        foreach ($callnum_arr as $cn_sub) {
                                $callnum .= $cn_sub . ' ';
                        }
                }
                $bib[callnum] = trim($callnum);
       
                // Publication information
                $bib[pub_info] = '';
                $pub_info = self::prepare_marc_values($bib_info_marc['260'], array('a','b','c'));
                $bib[pub_info] = $pub_info[0];

                // Publication year
                $bib[pub_year] = '';
                $pub_year = self::prepare_marc_values($bib_info_marc['260'], array('c'));
                $c_arr = explode(',', $pub_year[0]);
                $c_key = count($c_arr) - 1;
                $bib[pub_year] = substr(ereg_replace("[^0-9]", '', $c_arr[$c_key]), -4);

                // ISBN / Std. number
                $bib[stdnum] = '';
                $stdnum = self::prepare_marc_values($bib_info_marc['020'], array('a'));
                $bib[stdnum] = $stdnum[0];

                // Grab the cover image URL if we're doing that
                $bib[cover_img] = '';
                if ($skip_cover != TRUE) {
                        if ($bib[stdnum]) { $bib[cover_img] = locum_server::get_cover_img($bib[stdnum]); }
                }

                // LCCN
                $bib[lccn] = '';
                $lccn = self::prepare_marc_values($bib_info_marc['010'], array('a'));
                $bib[lccn] = $lccn[0];

                // Description
                $bib[descr] = '';
                $descr = self::prepare_marc_values($bib_info_marc['300'], array('a','b','c'));
                $bib[descr] = $descr[0];

                // Notes
                $notes = array();
                $bib[notes] = '';
                $notes_tags = array('500', '520');
                foreach ($notes_tags as $notes_tag) {
                        $notes_arr = self::prepare_marc_values($bib_info_marc[$notes_tag], array('a'));
                        if (is_array($notes_arr)) {
                                foreach ($notes_arr as $notes_arr_val) {
                                        array_push($notes, $notes_arr_val);
                                }
                        }
                }
                if (count($notes)) { $bib[notes] = serialize($notes); }

                // Subject headings
                $subjects = array();
                $subj_tags = array(
                        '600', '610', '611', '630', '650', '651',
                        '653', '654', '655', '656', '657', '658',
                        '690', '691', '692', '693', '694', '695',
                        '696', '697', '698', '699'
                );
                foreach ($subj_tags as $subj_tag) {
                        $subj_arr = self::prepare_marc_values($bib_info_marc[$subj_tag], array('a','b','c','d','e','v','x','y','z'), ' -- ');
                        if (is_array($subj_arr)) {
                                foreach ($subj_arr as $subj_arr_val) {
                                        array_push($subjects, $subj_arr_val);
                                }
                        }
                }
                $bib[subjects] = '';
                if (count($subjects)) { $bib[subjects] = $subjects; }
               
                unset($bib_info_marc);
                return $bib;
        }

        /**
         * Parses item status for a particular bib item.
         *
         * @param string $bnum Bib number to query
         * @return array Returns a Locum-ready availability array
         */

        public function item_status($bnum) {
                $iii_webcat = $this->locum_config[ils_config][ils_server];
                $iii_webcat_port = $this->locum_config[ils_config][ils_harvest_port];
                $avail_token = locum::csv_parser($this->locum_config[ils_custom_config][iii_available_token]);

                $bnum = trim($bnum);
                $url = 'http://' . $iii_webcat . '/record=b' . $bnum . '~S3';

                $avail_page_raw = utf8_encode(file_get_contents($url));
               
                // Reserves & totals Regex
                $regex_r1 = '%<span class="bibHolds">(.*?)</span>%s';
                preg_match($regex_r1, $avail_page_raw, $match_r1);

                $regex_r2 = '%(.*?)hold(.*?)of(.*?)cop%s';
                preg_match($regex_r2, trim($match_r1[1]), $match_r2);
                $item_status_result[holds] = (int) trim($match_r2[1]) ? (int) trim($match_r2[1]) : 0;
                $item_status_result[total] = (int) trim($match_r2[3]) ? (int) trim($match_r2[3]) : 0;

                if (preg_match('%View additional copies%s', $avail_page_raw)) {
                        $url = 'http://' . $iii_webcat . '/search/.b' . $bnum . '/.b' . $bnum . '/1,1,1,B/holdings~' . $bnum . '&FF=&1,0,';
                        $avail_page_raw = utf8_encode(file_get_contents($url));
                }

                // Holdings Regex
                $regex_h = '%field 1 -->&nbsp;(.*?)</td>(.*?)browse">(.*?)</a>(.*?)field \% -->&nbsp;(.*?)</td>%s';
                preg_match_all($regex_h, $avail_page_raw, $matches);
                $avail_temp[location] = $matches[1];
                $avail_temp[callnum] = $matches[3];
                $avail_temp[status] = $matches[5];

                // Order Entry Regex
                $regex_o = '%bibOrderEntry(.*?)td(.*?)>(.*?)<%s';
                preg_match($regex_o, $avail_page_raw, $match_o);
                $order_entry_msg = trim($match_o[3]);
                $item_status_result[order] = $order_entry_msg ? $order_entry_msg : '';

                $total_avail = 0;
                foreach ($matches[3] as $num => $cnum) {
                        $cnum = trim($cnum);
                        $item_status = trim($matches[5][$num]);
                        $location = trim($matches[1][$num]);
                        if (in_array($item_status, $avail_token)) {
                                $avail[$cnum][$location][avail]++;
                                $total_avail++;
                        } else if (preg_match('/DUE/i', $item_status)) {
                                $due_arr = explode(' ', trim($item_status));
                                $due_date_arr = explode('-', $due_arr[1]);
                                $due_date = mktime(0, 0, 0, $due_date_arr[0], $due_date_arr[1], (2000 + (int) $due_date_arr[2]));
                                $avail[$cnum][$location][due][] = $due_date;
                                sort($avail[$cnum][$location][due]);
                        }
                }
                $item_status_result[total] = $item_status_result[total] ? $item_status_result[total] : count($matches[3]);
                $item_status_result[copies] = (int) $total_avail;
                $item_status_result[details] = $avail;

                return $item_status_result;
        }
       
        /**
         * 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 patron_info($pid) {
                $papi = new iii_patronapi;
                $papi->iiiserver = $this->locum_config[ils_config][ils_server];
                $papi_data = $papi->get_patronapi_data($pid);

                if (!$papi_data) { return FALSE; }

                $pdata[pnum] = $papi_data[RECORDNUM];
                $pdata[cardnum] = $papi_data[PBARCODE];
                $pdata[checkouts] = $papi_data[CURCHKOUT];
                $pdata[homelib] = $papi_data[HOMELIBR];
                $pdata[balance] = (float) preg_replace('%\$%s', '', $papi_data[MONEYOWED]);
                $pdata[expires] = $papi_data[EXPDATE] ? self::date_to_timestamp($papi_data[EXPDATE], 2000) : NULL;
                $pdata[name] = $papi_data[PATRNNAME];
                $pdata[address] = preg_replace('%\$%s', "\n", $papi_data[ADDRESS]);
                $pdata[tel1] = $papi_data[TELEPHONE];
                if ($papi_data[TELEPHONE2]) { $pdata[tel2] = $papi_data[TELEPHONE2]; }
                $pdata[email] = $papi_data[EMAILADDR];

                return $pdata;
        }

        /**
         * 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 login fails
         */

        public function patron_checkouts($cardnum, $pin = NULL) {
                require_once('iiitools_2007.php');
                $iii = new iiitools;
                $iii->set_iiiserver($this->locum_config[ils_config][ils_server]);
                $iii->set_cardnum($cardnum);
                $iii->set_pin($pin);
                if ($iii->catalog_login() == FALSE) { return FALSE; }
                return $iii->get_patron_items();
        }

        /**
         * Returns an array of patron checkouts for history
         *
         * @param string $cardnum Patron barcode/card number
         * @param string $pin Patron pin/password
         * @return boolean|array Array of patron checkouts or FALSE if login fails
         */

        public function patron_checkout_history($cardnum, $pin = NULL) {
                $iii = $this->get_tools($cardnum, $pin);
                $result = $iii ? $iii->get_patron_history_items() : false;
                return $result;
        }
        /**
         * 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 login fails
         */

        public function patron_checkout_history_toggle($cardnum, $pin = NULL, $action) {
                $iii = $this->get_tools($cardnum, $pin);
                $result = $iii ? $iii->toggle_patron_history($action) : false;
                return $result;
        }
       
        /**
         * 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 patron_holds($cardnum, $pin = NULL) {
                require_once('iiitools_2007.php');
                $iii = new iiitools;
                $iii->set_iiiserver($this->locum_config[ils_config][ils_server]);
                $iii->set_cardnum($cardnum);
                $iii->set_pin($pin);
                if ($iii->catalog_login() == FALSE) { return FALSE; }
                return $iii->get_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) {
                require_once('iiitools_2007.php');
                $iii = new iiitools;
                $iii->set_iiiserver($this->locum_config[ils_config][ils_server]);
                $iii->set_cardnum($cardnum);
                $iii->set_pin($pin);
                if ($iii->catalog_login() == FALSE) { return FALSE; }
                return $iii->renew_material($items);
        }
       
        /**
         * Cancels holds
         *
         * @param string $cardnum Patron barcode/card number
         * @param string $pin Patron pin/password
         * @param array Array of varname => item/bib numbers to be cancelled, or NULL for everything.
         * @return boolean TRUE or FALSE if it cannot cancel for some reason
         */

        public function cancel_holds($cardnum, $pin = NULL, $items = NULL) {
                require_once('iiitools_2007.php');
                $iii = new iiitools;
                $iii->set_iiiserver($this->locum_config[ils_config][ils_server]);
                $iii->set_cardnum($cardnum);
                $iii->set_pin($pin);
                if ($iii->catalog_login() == FALSE) { return FALSE; }
                $iii->cancel_holds($items);
                return TRUE;
        }
        /**
         * Places or removes freezes on holds
         *
         * @param string $cardnum Patron barcode/card number
         * @param string $pin Patron pin/password
         * @param array $holdfreezes_to_update Array of bnum => new status.
         * @return boolean TRUE or FALSE if it cannot cancel for some reason
         */

        public function update_holdfreezes($cardnum, $pin, $holdfreezes_to_update) {
                require_once('iiitools_2007.php');
                $iii = new iiitools;
                $iii->set_iiiserver($this->locum_config[ils_config][ils_server]);
                $iii->set_cardnum($cardnum);
                $iii->set_pin($pin);
                if ($iii->catalog_login() == FALSE) { return FALSE; }
                $iii->update_holdfreezes($holdfreezes_to_update);
                return TRUE;
        }

        /**
         * Places holds
         *
         * @param string $cardnum Patron barcode/card number
         * @param string $bnum Bib item record number to place a hold on
         * @param string $inum Item number to place a hold on if required (presented as $varname in locum)
         * @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, $inum = NULL, $pin = NULL, $pickup_loc = NULL) {
                require_once('iiitools_2007.php');
                $iii = new iiitools;
                $iii->set_iiiserver($this->locum_config[ils_config][ils_server]);
                $iii->set_cardnum($cardnum);
                $iii->set_pin($pin);
                if ($iii->catalog_login() == FALSE) { return FALSE; }
                return $iii->place_hold($bnum, $inum, $pickup_loc);
        }
       
        /**
         * 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 fines or FALSE if login fails
         */

        public function patron_fines($cardnum, $pin = NULL) {
                require_once('iiitools_2007.php');
                $iii = new iiitools;
                $iii->set_iiiserver($this->locum_config[ils_config][ils_server]);
                $iii->set_cardnum($cardnum);
                $iii->set_pin($pin);
                if ($iii->catalog_login() == FALSE) { return FALSE; }
                $fines = $iii->get_patron_fines();
                return $fines[items];
        }
       
        /**
         * Pays patron fines.
         * @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) {
                require_once('iiitools_2007.php');
                $iii = new iiitools;
                $iii->set_iiiserver($this->locum_config[ils_config][ils_server]);
                $iii->set_cardnum($cardnum);
                $iii->set_pin($pin);
                if ($iii->catalog_login() == FALSE) { return FALSE; }
                $iii_payment_details[varnames] = $payment_details[varnames];
                $iii_payment_details[amount] = '$' . number_format($payment_details[total], 2);
                $iii_payment_details[name] = $payment_details[name];
                $iii_payment_details[address1] = $payment_details[address1];
                $iii_payment_details[city] = $payment_details[city];
                $iii_payment_details[state] = $payment_details[state];
                $iii_payment_details[zip] = $payment_details[zip];
                $iii_payment_details[email] = $payment_details[email];
                $iii_payment_details[ccnum] = $payment_details[ccnum];
                $iii_payment_details[ccexp_month] = $payment_details[ccexpmonth];
                $iii_payment_details[ccexp_year] = $payment_details[ccexpyear];
                $iii_payment_details[cvv] = $payment_details[ccseccode];

                $payment_result = $iii->pay_fine($iii_payment_details);
                return $payment_result;
        }
       
        /**
         * This is an internal function used to parse MARC values.
         * This function is called by scrape_bib()
         *
         * @param array $value_arr SimpleXML values from XRECORD for that MARC item
         * @param array $subfields An array of MARC subfields to parse
         * @param string $delimiter Delimiter to use for storage and indexing purposes.  A space seems to work fine
         * @return array An array of processed MARC values
         */

        public function prepare_marc_values($value_arr, $subfields, $delimiter = ' ') {

                // Repeatable values can be returned as an array or a serialized value
                foreach ($subfields as $subfield) {
                        if (is_array($value_arr[$subfield])) {

                                foreach ($value_arr[$subfield] as $subkey => $subvalue) {

                                        if (is_array($subvalue)) {
                                                foreach ($subvalue as $sub_subvalue) {
                                                        if ($i[$subkey]) { $pad[$subkey] = $delimiter; }
                                                        $sv_tmp = preg_replace('/\{(.*?)\}/', '', trim($sub_subvalue));
                                                        $sv_tmp = trim(preg_replace('/</i', '"', $sv_tmp));
                                                        if (trim($sub_subvalue)) { $marc_values[$subkey] .= $pad[$subkey] . $sv_tmp; }
                                                        $i[$subkey] = 1;
                                                }
                                        } else {
                                                if ($i[$subkey]) { $pad[$subkey] = $delimiter; }
                                               
                                                // This is a workaround until I can figure out wtf III is doing with encoding.  For now
                                                // there will be no extended characters:
                                                $sv_tmp = preg_replace('/\{(.*?)\}/', '', trim($subvalue));

                                                // Fix odd quote issues.  May be a club method of doing this, but oh well.
                                                $sv_tmp = trim(preg_replace('/</i', '"', $sv_tmp));

                                                if (trim($subvalue)) { $marc_values[$subkey] .= $pad[$subkey] . $sv_tmp; }
                                                $i[$subkey] = 1;
                                        }
                                }      
                        }              
                }

                if (is_array($marc_values)) {
                        foreach ($marc_values as $mv) {
                                $result[] = $mv;
                        }
                }
                return $result;
        }

        /**
         * Does the initial job of creating an array out of the SimpleXML content from XRECORD.
         * This function is called by scrape_bib() and the data is ultimately used by prepare_marc_values()
         *
         * @param array $bib_info_marc VARFLD value tree from XRECORD via SimpleXML
         * @return array A normalized array of marc and subfield info
         */

        public function parse_marc_subfields($bib_info_marc) {
                $bim_item = 0;
                foreach ($bib_info_marc as $bim_obj) {
                        // We need to treat MARC tag numbers as a string, or things would be a mess
                        $marc_num = (string) $bim_obj->MARCINFO->MARCTAG;
                        if (count($bim_obj->MARCSUBFLD) == 1) {
                                // Only one subfield value
                                $subfld = get_object_vars($bim_obj->MARCSUBFLD);
                                $marc_sub[$marc_num][trim($subfld[SUBFIELDINDICATOR])][$bim_item] = trim($subfld[SUBFIELDDATA]);
                        } else if (count($bim_obj->MARCSUBFLD) > 1) {
                                // Multiple subfield values
                                for ($i = 0; $i < count($bim_obj->MARCSUBFLD); $i++) {
                                        $subfld = get_object_vars($bim_obj->MARCSUBFLD[$i]);
                                        $marc_sub[$marc_num][trim($subfld[SUBFIELDINDICATOR])][$bim_item][] = trim($subfld[SUBFIELDDATA]);
                                }
                        }
                        $bim_item++;
                }

                return $marc_sub;
        }

        /**
         * Fixes a non-standard date format.
         *
         * @param string $olddate Date string in MM-DD-YY format
         * @param string Date string in YYYY-MM-DD format
         */

        public function fixdate($olddate) {
                return date('Y-m-d', self::date_to_timestamp($olddate));
        }
       
        /**
         * Converts MM-DD-YY to unix timestamp
         *
         * @param string $date_orig Original date in MM-DD-YY format
         * @param int Optional century to use as a baseline.  Fix for III's Y2K issues.
         * @return timestamp
         */

        public function date_to_timestamp($date_orig, $default_century = NULL) {
                $date_arr = explode('-', trim($date_orig));
                if (strlen(trim($date_arr[2])) == 2) {
                        if ($default_century) {
                                $year = $default_century + (int) trim($date_arr[2]);
                        } else {
                                $year = $default_century + (int) trim($date_arr[2]);
                                if (date('Y') < $year) { $year = 1900 + (int) trim($date_arr[2]); }
                        }
                } else {
                        $year = trim($date_arr[2]);
                }
                $time = mktime(0, 0, 0, $date_arr[0], $date_arr[1], $year);
                return $time;
        }


















}