Tag Archives: Holdings

vufind: mostrar información de reservas en los registros

Estos días he estado trasteando con vufind ([1] y [2]).

Mostrar el enlace hacia Holds

En algunas bibliotecas que también tienen vufind y su catálogo es Innovative Millenium había visto que mostraban la posibilidad de colocar un enlace para reservar ejemplares directamente. Sin embargo en mi instalación de vufind no parecía funcionar.

Investigando un poco en la wiki de vufind descubrí que el fichero que me estaba “fastidiando” era el view-holdings.tpl y más en concreto estas líneas:

 {foreach from=$holding item=row}
    {if $row.barcode != ""}

Mis registros no tenían un valor de barcode asignado, por lo cual esa parte del código no se ejecutaba.

¿Cómo lo averigué? Colocando un print_r de la variable $row en smarty (cómo hacer print_r de arrays en smarty templates). Mi código de view-holdings.tpl ha quedado ahora asi:

 {foreach from=$holding item=row}
    <em> Edit view-holdings.tpl by MiguelMartin</em><br />
    {$row|@print_r}
 
    <!-- comento esta linea y añado la siguiente -->
    <!--if $row.barcode != ""-->
    {if $row.id != ""}

Como todos mis registros sí tienen ID, el código se ejecuta y se muestra el link.

Que viva el open source! :)

(Para saber cómo configurar la conexión entre vufind y tu software de catálogo puedes consultar éste link)

Hacer que funcione el enlace a Holds usando Innovative

El link que se genera con lo anterior es del tipo

http://yourvufindserver.com/vufind/Record/.b10002030/Hold

Si usamos el driver de Innovative que viene por defecto (la pareja de archivos Innovative.ini y Innovative.php) obtendremos un error de PEAR que dice algo asi:

Cannot Process Place Hold - ILS Not Supported

Este error viene de $VUFIND_HOME/web/services/Record/Hold.php.

Para solucionarlo cambiad vuestro Innovative.ini por algo del tipo (ojo, cambiad la ‘url’ de la sección [Catalog] y de [PATRONAPI] para que apunte a la url de vuestro catálogo):

[Catalog]
url = http://iii.server.org
 
; The following is a set of fields to look up for
; Change them to match your HTML
[OPAC]
location_column    = "Location"
call_no_column     = "Call No"
status_column      = "Status"
reserves_column    = "Location"
reserves_key_name  = "res"
status_avail       = "AVAILABLE"
status_due         = "DUE "
 
[CONFIG]
; "bib" or "item" level holds
holdtype = "bib"
 
[PATRONAPI]
; Enable III Patron API usage for patron authentication
; and profile information.
enabled = "true"
url = http://iii.server.org:4500/PATRONAPI/

Y vuestro Innovative.php por:

<?php
/**
 *
 * Copyright (C) Villanova University 2007.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2,
 * as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */
require_once 'sys/Proxy_Request.php';
require_once 'Interface.php';
 
/**
 * VuFind Connector for Innovative
 *
 * This class uses screen scraping techniques to gather record holdings written
 * by Adam Bryn of the Tri-College consortium.
 *
 * @author Adam Brin <abrin@brynmawr.com>
 */
class Innovative implements DriverInterface
{
    public $config;
 
    public function __construct()
    {
        // Load Configuration for this Module
        $this->config = parse_ini_file('conf/Innovative.ini', true);
    }
 
    public function getStatus($id)
    {
        // Strip ID
        $id_ = substr(str_replace('.b', '', $id), 0, -1);
 
        // Load Record Page
        if (substr($this->config['Catalog']['url'], -1) == '/') {
            $host = substr($this->config['Catalog']['url'], 0, -1);
        } else {
            $host = $this->config['Catalog']['url'];
        }
        //$req = new Proxy_Request($host . '/record=b' . $id_);
 
        //Grab the full item list view
        $req = new Proxy_Request($host . '/search/.b' . $id_ . '/.b' . $id_ .'/1%2C1%2C1%2CB/holdings~' . $id_ . '&FF=&1%2C0%2C');
        if (PEAR::isError($req->sendRequest())) {
            return null;
        }
        $result = $req->getResponseBody();
 
        //strip out html before the first occurance of 'bibItems', should be '<table class="bibItems" '
        $r = substr($result, stripos($result, 'bibItems'));
        //strip out the rest of the first table tag.
        $r = substr($r,strpos($r,">")+1);
        //strip out the next table closing tag and everything after it.
        $r = substr($r,0,stripos($r,"</table"));
 
        //$r should only include the holdings table at this point
 
        //split up into strings that contain each table row, excluding the beginning tr tag.
        $rows = preg_split("/<tr([^>]*)>/",$r);
        $count = 0;
        $keys = array_pad(array(),10,"");
 
        $loc_col_name      = $this->config['OPAC']['location_column'];
        $call_col_name     = $this->config['OPAC']['call_no_column'];
        $status_col_name   = $this->config['OPAC']['status_column'];
        $reserves_col_name = $this->config['OPAC']['location_column'];
        $reserves_key_name = $this->config['OPAC']['reserves_key_name'];
        $stat_avail        = $this->config['OPAC']['status_avail'];
        $stat_due              = $this->config['OPAC']['status_due'];
 
        $ret = array();
        foreach ($rows as $row) {
                // Split up the contents of the row based on the th or td tag, excluding the tags themselves.
                $cols = preg_split("/<t(h|d)([^>]*)>/",$row);
 
                //for each th or td section, do the following.
                for ($i=0; $i < sizeof($cols); $i++) {
 
                        //replace non blocking space encodings with a space.
                        $cols[$i] = str_replace("&nbsp;"," ",$cols[$i]);
                        //remove html comment tags
                        $cols[$i] = ereg_replace("<!--([^(-->)]*)-->","",$cols[$i]);
                        //Remove closing th or td tag, trim whitespace and decode html entities
                        $cols[$i] = html_entity_decode(trim(substr($cols[$i],0,stripos($cols[$i],"</t"))));
 
                        //If this is the first row, it is the header row and has the column names
                        if ($count == 1) {
                                $keys[$i] = $cols[$i];
                        } else if ($count > 1) { //not the first row, has holding info
                                //look for location column
                                if (stripos($keys[$i],$loc_col_name) > -1) {
                                        $ret[$count-2]['location'] = strip_tags($cols[$i]);
                                }
                                // Does column hold reserves information?
                                if (stripos($keys[$i],$reserves_col_name) > -1) {
                                        if (stripos($cols[$i],$reserves_key_name) > -1) {
                                        $ret[$count-2]['reserve'] = 'Y';
                                } else {
                                        $ret[$count-2]['reserve'] = 'N';
                                }
                        }
                    // Does column hold call numbers?
                    if (stripos($keys[$i],$call_col_name) > -1) {
                        $ret[$count-2]['callnumber'] = strip_tags($cols[$i]);
                    }
                    // Look for status information.
                    if (stripos($keys[$i],$status_col_name) > -1) {
                        if (stripos($cols[$i],$stat_avail) > -1) {
                            $ret[$count-2]['status'] = "Available On Shelf";
                            $ret[$count-2]['availability'] = 1;
                        } else {
                            $ret[$count-2]['status'] = "Available to request";
                            $ret[$count-2]['availability'] = 0;
                        }
                        if (stripos($cols[$i],$stat_due) > -1) {
                            $t = trim(substr($cols[$i],stripos($cols[$i],$stat_due)+strlen($stat_due)));
                            $t = substr($t,0,stripos($t," "));
                            $ret[$count-2]['duedate'] = $t;
                        }
                    }
                    //$ret[$count-2][$keys[$i]] = $cols[$i];
                    //$ret[$count-2]['id'] = $bibid;
                    $ret[$count-2]['id'] = $id;
                    $ret[$count-2]['number'] = ($count -1);
                    //Return a fake barcode so hold link is enabled
                    //  Should be dependent on settings variable,  If bib level holds.
                    $ret[$count-2]['barcode'] = '1234567890123';
                }
            }
            $count++;
        }
        return $ret;
    }
 
    public function getStatuses($ids)
    {
        $items = array();
        $count = 0;
        foreach ($ids as $id) {
               $items[$count] = $this->getStatus($id);
               $count++;
        }
        return $items;
    }
 
    public function getHolding($id)
    {
        return $this->getStatus($id);
    }
 
    public function getPurchaseHistory($id)
    {
        return array();
    }
 
    public function getHoldLink($id)
    {
        // Strip ID
        $id_ = substr(str_replace('.b', '', $id), 0, -1);
 
        //Build request link
        $link = $this->config['Catalog']['url'] . '/search?/.b' . $id_ . '/.b' . $id_ . '/1%2C1%2C1%2CB/request~b'. $id_;
        //$link = $this->config['Catalog']['url'] . '/record=b' . $id_;
        return $link;
    }
 
    public function getMyProfile($userinfo)
    {
        return $userinfo;
 
    }
    public function patronLogin($username,$password)
    {
        //Todo, if username is a barcode, test to make sure it fits proper format
 
        if($this->config['PATRONAPI']['enabled'] == 'true'){
                //use patronAPI to authenticate customer
                $url = $this->config['PATRONAPI']['url'];
 
                //build patronapi pin test request
 
                $req = new Proxy_Request($url . urlencode($username) . '/' . urlencode($password) . '/pintest');
                if (PEAR::isError($req->sendRequest())) {
                    return null;
                }
                $result = $req->getResponseBody();
 
                //search for sucessfull response of "RETCOD=0"
                if(stripos($result,"RETCOD=0") == -1){
                        //pin did not match, can look up specific error to return more usefull info.
                        return null;
 
                }
 
                //Pin did match, get patron information
                $req = new Proxy_Request($url . urlencode($username) . '/dump');
                if (PEAR::isError($req->sendRequest())) {
                    return null;
                }
                $result = $req->getResponseBody();
 
                //The following is taken and modified from patronapi.php by John Blyberg released under the GPL
                $api_contents = trim(strip_tags($result));
                $api_array_lines = explode("\n", $api_contents);
                while (strlen($api_data[PBARCODE]) != 14 && !$api_data[ERRNUM]) {
                        foreach ($api_array_lines as $api_line) {
                                $api_line = str_replace("p=", "peq", $api_line);
                                $api_line_arr = explode("=", $api_line);
                                $regex_match = array("/\[(.*?)\]/","/\s/","/#/");
                                $regex_replace = array('','','NUM');
                                $key = trim(preg_replace($regex_match, $regex_replace, $api_line_arr[0]));
                                $api_data[$key] = trim($api_line_arr[1]);
                        }
                }
 
                if(!$api_data[PBARCODE]){
                        //no barcode found, can look up specific error to return more useful info.
                        //this check needs to be modified to handle using III patron ids also.
                      return null;
 
                }
                //return patron info
                $ret = array();
                $ret['id'] = $api_data[PBARCODE]; //or should I return patron id num?
                $names = explode(',', $api_data[PATRNNAME]);
                $ret['firstname'] = $names[1];
                $ret['lastname'] = $names[0];
                $ret['cat_username'] = urlencode($username);
                $ret['cat_password'] = urlencode($password);
                $ret['email'] = $api_data[EMAILADDR];
                $ret['major'] = null;
                $ret['college'] = $api_data[HOMELIBR];
                $ret['homelib'] = $api_data[HOMELIBR];
                //replace $ seperator in III addresses with newline
                $ret['address1'] = str_replace("$",", ",$api_data[ADDRESS]);
                $ret['address2'] = str_replace("$",", ",$api_data[ADDRESS2]);
                preg_match("/([0-9]{5}|[0-9]{5}-[0-9]{4})[ ]*$/",$api_data[ADDRESS],$zipmatch);
                $ret['zip'] = $zipmatch[1];  //retreive from address
                $ret['phone'] = $api_data[TELEPHONE];
                $ret['phone2'] = $api_data[TELEPHONE2];
                //Should probably have a translation table for patron type
                $ret['group'] = $api_data[PTYPE];
                $ret['expiration'] = $api_data[EXPDATE];
                //Only if agency module is enabled.
                $ret['region'] = $api_data[AGENCY];
                return $ret;
 
        }
        else {
                //use screen scrape
 
        }
 
    }
}
 
?>

De esta forma al hacer click en “Reservar” os llevará a algo del tipo:

https://yourcatalog.com/patroninfo~S1/0/redirect=/search?/.b1000203/.b1000203/1%2C1%2C1%2CB/request~b1000203

Ver Más ayuda.

III Innovative Millenium software: change “holdings” text

In my library we use Innovative Millennium integrated library system.

If you are used to Millenium you’ll know this system uses a kinda-weird way to generate HTML output pages.

My boss told me to change a text which is displayed in some pages (refer to this link for further details).

Here is the *spanish-version* of the HTML output to be modified:
innovative millenium holdings text

Easy, I thought. Well, I was wrong. It took me a while to figure out how to solve this.

The text to be changed is the “Latest received” information displayed in a record page. This page is produced by bibdisplay.html file. More precisely, with the tag which is included in bibdisplay.html. (Note: in my case, the spanish output is produced by bibdisplay_spi.html. SPI means “spanish scope” or something like that).

I downloaded all of the /screens folder to my linux system and grep’d for the Latest received text to change (in spanish: Últimos recibidos). No luck. Then I downloaded the wwwoptions file (this text-messages are usually saved in this file). Still no luck.

After some more research I noticed a webpub_spi.def which contains that string. There is also a webpub.def with the english messages!)
So I just modified it (

vi webpub_spi.def

) and saved it again.

Voilá, it works!