Subversion Repositories Web Services

Rev

Rev 119775 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
6822 fvs 1
<?php
2
/**
3
 *
7060 fvs 4
 * This file is part of Open Library System.
6822 fvs 5
 * Copyright © 2009, Dansk Bibliotekscenter a/s,
6
 * Tempovej 7-11, DK-2750 Ballerup, Denmark. CVR: 15149043
7
 *
7060 fvs 8
 * Open Library System is free software: you can redistribute it and/or modify
6822 fvs 9
 * it under the terms of the GNU Affero General Public License as published by
10
 * the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
7060 fvs 13
 * Open Library System is distributed in the hope that it will be useful,
6822 fvs 14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU Affero General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU Affero General Public License
7060 fvs 19
 * along with Open Library System.  If not, see <http://www.gnu.org/licenses/>.
6822 fvs 20
*/
21
 
22
 
47030 fvs 23
require_once('OLS_class_lib/webServiceServer_class.php');
24
require_once('OLS_class_lib/oci_class.php');
25
require_once('OLS_class_lib/z3950_class.php');
119114 fvs 26
require_once 'OLS_class_lib/open_agency_v2_class.php';
6822 fvs 27
 
28
class openHoldings extends webServiceServer {
29
 
82472 fvs 30
  protected $tracking_id;
47460 fvs 31
  protected $curl;
32
  protected $dom;
6822 fvs 33
 
47460 fvs 34
  public function __construct() {
35
    webServiceServer::__construct('openholdingstatus.ini');
36
    $this->curl = new curl();
37
    $this->curl->set_option(CURLOPT_TIMEOUT, 30);
38
    $this->dom = new DomDocument();
39
    $this->dom->preserveWhiteSpace = false;
119114 fvs 40
    $this->openagency = new OpenAgency($this->config->get_value('agency', 'setup'));
47460 fvs 41
  }
42
 
78303 fvs 43
 /* \brief
6879 fvs 44
  * request:
40621 fvs 45
  * - agencyId: Identifier of agency
46
  * - pid: Identifier of Open Search object
47030 fvs 47
  * - mergePids: merge localisations for all pids
40578 fvs 48
  * response:
45899 fvs 49
  * - localisations
40578 fvs 50
  * - - pid: Identifier of Open Search object
50012 fvs 51
  * - - errorMessage
52
  * - or
40578 fvs 53
  * - - agencyId: Identifier of agency
40621 fvs 54
  * - - note: Note from local library
55
  * - - codes: string
50012 fvs 56
  * - - localIdentifier: string
40578 fvs 57
  * - error
58
  * - - pid: Identifier of Open Search object
59
  * - - responderId: librarycode for lookup-library
50012 fvs 60
  * - - errorMessage
79874 fvs 61
  *
62
  * @param $param object - the request
63
  * @retval object - the answer
40578 fvs 64
  */
45899 fvs 65
  public function localisations($param) {
82472 fvs 66
    $this->tracking_id = verbose::set_tracking_id('ohs', $param->trackingId->_value);
45899 fvs 67
    $lr = &$ret->localisationsResponse->_value;
49531 fvs 68
    if (!$this->aaa->has_right('netpunkt.dk', 500)) {
50012 fvs 69
      $error = 'authentication_error';
70
    }
71
    else {
72
      is_array($param->pid) ? $pids = $param->pid : $pids[] = $param->pid;
78303 fvs 73
      $sort_n_merge = (self::xs_boolean($param->mergePids->_value) && count($pids) > 1);
50012 fvs 74
      if ($sort_n_merge) {
119114 fvs 75
        $r_order = $this->openagency->get_request_priority(self::strip_agency($param->agencyId->_value));
76
        if (empty($r_order)) {
50012 fvs 77
          $error = 'error_fetching_request_order';
119114 fvs 78
          verbose::log(ERROR, 'OpenHoldings:: fetch request order returned with empty list for agency: ' . $param->agencyId->_value);
47030 fvs 79
        }
80
      }
81
    }
82
 
83
    if ($error) {
84
      $lr->error->_value->responderId->_value = $param->agencyId->_value;
85
      $lr->error->_value->errorMessage->_value = $error;
86
      return $ret;
87
    }
88
 
40621 fvs 89
// if more than one pid, this could be parallelized
47030 fvs 90
    foreach ($pids as $pid) {
71388 fvs 91
      unset($error);
47030 fvs 92
      $url = sprintf($this->config->get_value('ols_get_holdings','setup'),
78303 fvs 93
                     self::strip_agency($param->agencyId->_value),
115861 fvs 94
                     urlencode($pid->_value));
47460 fvs 95
      $res = $this->curl->get($url);
96
      $curl_status = $this->curl->get_status();
47030 fvs 97
      if ($curl_status['http_code'] == 200) {
47460 fvs 98
        if ($this->dom->loadXML($res)) {
40578 fvs 99
// <holding fedoraPid="870970-basis:28542941">
100
//   <agencyId>715700</agencyId>
101
//   <note></note>
102
//   <codes></codes>
103
// </holding>
47460 fvs 104
          if ($holdings = $this->dom->getElementsByTagName('holding')) {
47030 fvs 105
            foreach ($holdings as $holding) {
106
              foreach ($holding->childNodes as $node) {
107
                $hold[$node->localName] = $node->nodeValue;
40578 fvs 108
              }
47030 fvs 109
              $hold['fedoraPid'] = $holding->getAttribute('fedoraPid');
110
              if ($sort_n_merge) {
111
                if (!isset($r_order[ $hold['agencyId'] ]))
112
                  $r_order[ $hold['agencyId'] ] = count($r_order) + 1000;
113
                $hold['sort'] = sprintf('%06s', $r_order[ $hold['agencyId'] ]);
114
              }
115
              $pid_hold[] = $hold;
116
              unset($hold);
40578 fvs 117
            }
47030 fvs 118
            if ($sort_n_merge) {
119
              $h_arr[0]['pids'][] = $pid->_value;
49291 fvs 120
              if (empty($h_arr[0]['holds'])) {
47030 fvs 121
                $h_arr[0]['holds'] = array();
49291 fvs 122
              }
123
              if (is_array($pid_hold)) {
124
                $h_arr[0]['holds'] = array_merge($h_arr[0]['holds'], $pid_hold);
125
              }
47030 fvs 126
            }
127
            else {
128
              $h_arr[] = array('pids' => array($pid->_value), 'holds' => $pid_hold);
129
            }
130
            unset($pid_hold);
40578 fvs 131
          }
132
        }
133
        else {
71388 fvs 134
          $error = 'error_parsing_holdings_server_answer';
40578 fvs 135
        }
136
      }
47030 fvs 137
      else {
71388 fvs 138
        $error = 'error_contacting_holdings_server';
47030 fvs 139
        verbose::log(ERROR, 'OpenHoldings:: http code: ' . $curl_status['http_code'] .
140
                            ' error: "' . $curl_status['error'] .
141
                            '" for: ' . $curl_status['url']);
142
      }
143
      if ($error) {
53180 fvs 144
        $err->pid[]->_value = $pid->_value;
47030 fvs 145
        $err->errorMessage->_value = $error;
50012 fvs 146
        $lr->localisations[]->_value = $err;
47030 fvs 147
        unset($err);
148
      }
40578 fvs 149
    }
47030 fvs 150
 
71388 fvs 151
    if (empty($h_arr) && $error) {
152
      unset($lr->localisations);
153
      $lr->error->_value->responderId->_value = $param->agencyId->_value;
154
      $lr->error->_value->errorMessage->_value = $error;
155
      return $ret;
156
    }
157
 
49620 fvs 158
    if ($sort_n_merge && is_array($h_arr)) {
47030 fvs 159
      usort($h_arr[0]['holds'], array($this, 'compare'));
160
    }
161
 
119114 fvs 162
    if ($param->role->_value == 'bibdk') {
119874 fvs 163
      $check_exclude_in_solr = array();
119114 fvs 164
      if (is_array($h_arr)) {
119775 fvs 165
        foreach ($h_arr as $hs_idx => $holds) {
119114 fvs 166
          if (isset($holds['holds'])) {
119775 fvs 167
            foreach ($holds['holds'] as $h_idx => $hold) {
119114 fvs 168
              $bib_type = self::bib_type($hold['agencyId']);
169
              switch ($bib_type) {
170
                case 'Folkebibliotek':
171
                  if (self::id_length($hold['fedoraPid']) == 9) {
119775 fvs 172
                    unset($h_arr[$hs_idx]['holds'][$h_idx]);
119114 fvs 173
                  }
174
                  break;
175
                case 'Skolebibliotek':
119775 fvs 176
                  unset($h_arr[$hs_idx]['holds'][$h_idx]);
119114 fvs 177
                  break;
178
                default:
179
                  if (self::library_rule($hold['agencyId'], 'part_of_bibliotek_dk') !== TRUE) {
119775 fvs 180
                    unset($h_arr[$hs_idx]['holds'][$h_idx]);
119114 fvs 181
                  }
182
                  else {
183
                    $ident = $hold['localIdentifier'] . '|' . $hold['agencyId'];
119874 fvs 184
                    $check_exclude_in_solr[$ident] = $ident;
119114 fvs 185
                  }
186
              }
187
            }
188
          }
189
        }
190
      }
191
 
119874 fvs 192
      if ($check_exclude_in_solr) {
193
        $solr_res = self::get_solr('rec.id:(' . implode(' OR ', $check_exclude_in_solr) . ') AND rec.excludeFromUnionCatalogue:1');
119775 fvs 194
        foreach ($h_arr as $hs_idx => $holds) {
119114 fvs 195
          if (isset($holds['holds'])) {
119775 fvs 196
            foreach ($holds['holds'] as $h_idx => $hold) {
119114 fvs 197
              $ident = $hold['localIdentifier'] . '|' . $hold['agencyId'];
198
              if ($solr_res[$ident]) {
119775 fvs 199
                unset($h_arr[$hs_idx]['holds'][$h_idx]);
119114 fvs 200
              }
201
            }
202
          }
203
        }
204
      }
205
    }
206
 
47211 fvs 207
    if (is_array($h_arr)) {
208
      foreach ($h_arr as $holds) {
209
        foreach ($holds['pids'] as $pid)
210
          $one_pid->pid[]->_value = $pid;
211
        if (isset($holds['holds'])) {
119775 fvs 212
          foreach ($holds['holds'] as $hold) {
47211 fvs 213
            $agency->localisationPid ->_value = $hold['fedoraPid'];
214
            $agency->agencyId->_value = $hold['agencyId'];
215
            if ($hold['note']) $agency->note->_value = $hold['note'];
216
            if ($hold['codes']) $agency->codes->_value = $hold['codes'];
217
            if ($hold['callNumber']) $agency->callNumber->_value = $hold['callNumber'];
49692 fvs 218
            if ($hold['localIdentifier']) $agency->localIdentifier->_value = $hold['localIdentifier'];
47211 fvs 219
            $one_pid->agency[]->_value = $agency;
220
            unset($agency);
221
          }
47146 fvs 222
        }
47211 fvs 223
        $lr->localisations[]->_value = $one_pid;
224
        unset($one_pid);
47030 fvs 225
      }
226
    }
227
 
40621 fvs 228
    return $ret;
229
  }
230
 
47030 fvs 231
 
78303 fvs 232
 /* \brief
40621 fvs 233
  * request:
234
  * - lookupRecord
235
  * - - responderId: librarycode for lookup-library
45961 fvs 236
  * - - pid
54591 fvs 237
  * - or next
40621 fvs 238
  * - - bibliographicRecordId: requester record id
239
  * response:
240
  * - responder
241
  * - - localHoldingsId
242
  * - - note:
243
  * - - willLend;
244
  * - - expectedDelivery;
45961 fvs 245
  * - - pid
54591 fvs 246
  * - or next
40621 fvs 247
  * - - bibliographicRecordId: requester record id
248
  * - - responderId: librarycode for lookup-library
249
  * - error
45961 fvs 250
  * - - pid
54591 fvs 251
  * - or next
40621 fvs 252
  * - - bibliographicRecordId: requester record id
253
  * - - responderId: librarycode for lookup-library
254
  * - - errorMessage:
79874 fvs 255
  *
256
  * @param $param object - the request
257
  * @retval object - the answer
40621 fvs 258
  */
45961 fvs 259
  public function holdings($param) {
82472 fvs 260
    $this->tracking_id = verbose::set_tracking_id('ohs', $param->trackingId->_value);
40621 fvs 261
    $hr = &$ret->holdingsResponse->_value;
49531 fvs 262
    if (!$this->aaa->has_right('netpunkt.dk', 500))
47030 fvs 263
      $auth_error = 'authentication_error';
40578 fvs 264
    if (isset($param->lookupRecord)) {
265
      // force to array
266
      if (!is_array($param->lookupRecord)) {
267
        $help = $param->lookupRecord;
268
        unset($param->lookupRecord);
269
        $param->lookupRecord[] = $help;
270
      }
6822 fvs 271
      foreach ($param->lookupRecord as $holding) {
9280 fvs 272
        if (!$fh = $auth_error)
78303 fvs 273
          $fh = self::find_holding($holding->_value);
6879 fvs 274
        if (is_scalar($fh)) {
78303 fvs 275
          self::add_recid($err, $holding);
7002 fvs 276
          $err->responderId->_value = $holding->_value->responderId->_value;
277
          $err->errorMessage->_value = $fh;
40578 fvs 278
          $hr->error[]->_value = $err;
7002 fvs 279
          unset($err);
280
        } else {
78303 fvs 281
          self::add_recid($fh, $holding);
7002 fvs 282
          $fh->responderId->_value = $holding->_value->responderId->_value;
7046 fvs 283
          $hr->responder[]->_value = $fh;
40578 fvs 284
        }
285
      }
286
    }
6822 fvs 287
 
288
    return $ret;
289
  }
290
 
80203 fvs 291
 /* \brief
292
  * request:
293
  * - lookupRecord
294
  * - - responderId: librarycode for lookup-library
295
  * - - pid
296
  * - or next
297
  * - - bibliographicRecordId: requester record id
298
  * response:
299
  * - error
300
  * - - pid
301
  * - or next
302
  * - - bibliographicRecordId: requester record id
303
  * - - responderId: librarycode for lookup-library
304
  * - - errorMessage:
305
  *
306
  * @param $param object - the request
307
  * @retval object - the answer
308
  */
309
  public function detailedHoldings($param) {
82472 fvs 310
    $this->tracking_id = verbose::set_tracking_id('ohs', $param->trackingId->_value);
80203 fvs 311
    $dhr = &$ret->detailedHoldingsResponse->_value;
312
    if (!$this->aaa->has_right('netpunkt.dk', 500)) {
313
      $dhr->error->_value->errorMessage->_value = 'authentication_error';
314
    }
315
    elseif (isset($param->lookupRecord)) {
316
      if (!is_array($param->lookupRecord)) {
317
        $help = $param->lookupRecord;
318
        unset($param->lookupRecord);
319
        $param->lookupRecord[] = $help;
320
      }
321
      foreach ($param->lookupRecord as $holding) {
322
        if (!$fh = $auth_error) {
323
          $fh = self::find_holding($holding->_value, TRUE);
324
        }
325
//var_dump($holding);
326
//var_dump($fh);
327
        self::add_recid($dh, $holding);
328
        $dh->responderId->_value = $holding->_value->responderId->_value;
329
        if (is_scalar($fh)) {
330
          $dh->errorMessage->_value = $fh;
331
        } else {
332
          foreach ($fh as $fhi) {
82880 fvs 333
            foreach (array('id' => 'localItemId', 'policy' => 'policy', 'date' => 'expectedDelivery', 'fee' => 'fee', 'note' => 'note', 'item' => 'itemText', 'target_location_id' => 'localIdentifier', 'level-0' => 'enumLevel0', 'level-1' => 'enumLevel1', 'level-2' => 'enumLevel2', 'level-3' => 'enumLevel3') as $key => $val) {
80203 fvs 334
              if ($help = $fhi[$key]) {
80647 fvs 335
                $item->$val->_value = ($key == 'date' ? substr($help, 0, 10) : trim($help));
80203 fvs 336
              }
337
            }
338
//var_dump($fhi); var_dump($item);
339
            $dh->holdingsItem[]->_value = $item;
340
            unset($item);
341
          }
342
        }
343
        $dhr->responderDetailed[]->_value = $dh;
344
        unset($dh);
345
      }
346
//var_dump($ret); die();
347
    }
348
 
349
    return $ret;
350
  }
351
 
78303 fvs 352
  /* -------------------------------------------------------------------------------- */
353
 
354
  /** \brief
355
   *
79874 fvs 356
   * @param $obj object
357
   * @param $hold object
78303 fvs 358
   */
45961 fvs 359
  private function add_recid(&$obj, &$hold) {
360
    if (isset($hold->_value->pid)) {
361
      $obj->pid->_value = $hold->_value->pid->_value;
362
    }
363
    else {
364
      $obj->bibliographicRecordId->_value = $hold->_value->bibliographicRecordId->_value;
365
    }
366
  }
367
 
78303 fvs 368
  /** \brief
369
   *
79874 fvs 370
   * @param $param object
80203 fvs 371
   * @param $detailed boolean
78303 fvs 372
   * @retval mixed
373
   */
80203 fvs 374
  private function find_holding($param, $detailed = FALSE) {
78303 fvs 375
    $connect_info = self::find_protocol_and_address($param->responderId->_value);
376
    switch ($connect_info['protocol']) {
377
      case 'z3950':
80203 fvs 378
        return self::find_z3950_holding($connect_info, $param, $detailed);
78303 fvs 379
        break;
380
      case 'iso20775':
80203 fvs 381
        return self::find_iso20775_holding($connect_info, $param, $detailed);
78303 fvs 382
        break;
383
      default:
384
        return 'service_not_supported_by_library';
385
    }
386
  }
387
 
79874 fvs 388
 /* /brief
6822 fvs 389
  * struct lookupRecord {
390
  *   string responderId;
45961 fvs 391
  *   string pid;
78303 fvs 392
  * - or next
6822 fvs 393
  *   string bibliographicRecordId;
40578 fvs 394
  *  }
79874 fvs 395
  *
396
  * @param $z_info array
397
  * @param $param object
80203 fvs 398
   * @param $detailed boolean
79874 fvs 399
  * @retval mixed
6822 fvs 400
  */
80203 fvs 401
  private function find_z3950_holding($z_info, $param, $detailed) {
6822 fvs 402
    static $z3950;
78303 fvs 403
    if (empty($z3950)) {
404
      $z3950 = new z3950();
405
    }
406
    list($target, $database) = explode('/', $z_info['url'], 2);
407
    $z3950->set_target($target);
408
    $z3950->set_database($database);
409
    $z3950->set_authentication($z_info['authentication']);
410
    $z3950->set_syntax('xml');
411
    $z3950->set_element('B3');
412
    $z3950->set_schema('1.2.840.10003.13.7.4');
413
    $z3950->set_start(1);
414
    $z3950->set_step(1);
415
    if (isset($param->pid)) {
115821 fvs 416
      list($bibpart, $recid) = explode(':', self::skip_multi_part_pid_info($param->pid->_value));
78303 fvs 417
    }
418
    else {
82554 fvs 419
      $recid = $param->bibliographicRecordId->_value;
78303 fvs 420
    }
82554 fvs 421
    $z3950->set_rpn('@attr 4=103 @attr BIB1 1=12 ' . $recid);
78303 fvs 422
    $this->watch->start('z3950');
423
    $hits = $z3950->z3950_search(5);
424
    $this->watch->stop('z3950');
425
    if ($z3950->get_error()) {
426
      verbose::log(ERROR, 'OpenHoldings:: ' . $z_info['url'] . ' Z3950 error: ' . $z3950->get_error_string());
427
      return 'error_searching_library';
428
    }
429
    if (!$hits) {
82554 fvs 430
      verbose::log(TRACE, 'OpenHoldings:: z3950: ' . $z_info['url'] . ' id: ' . $recid . ' item_not_found');
78303 fvs 431
      return 'item_not_found';
432
    }
433
    $record = $z3950->z3950_record(1);
82554 fvs 434
    verbose::log(TRACE, 'OpenHoldings:: z3950: ' . $z_info['url'] . ' id: ' . $recid . ' record: ' . str_replace("\n", '', $record));
78303 fvs 435
    if (empty($record)) {
436
      return 'no_holding_return_from_library';
437
    }
438
    if ($status = self::parse_z3950_holding($record)) {
80203 fvs 439
      return $detailed ? $status : self::parse_status($status);
78303 fvs 440
    }
441
    else {
442
      return 'cannot_parse_library_answer';
443
    }
444
  }
445
 
446
 /*
447
  * struct lookupRecord {
448
  *   string responderId;
449
  *   string pid;
450
  * - or next
451
  *   string bibliographicRecordId;
452
  *  }
79874 fvs 453
  *
454
  * @param $info array
455
  * @param $param object
80203 fvs 456
  * @param $detailed boolean
79874 fvs 457
  * @retval mixed
78303 fvs 458
  */
80203 fvs 459
  private function find_iso20775_holding($info, $param, $detailed) {
82472 fvs 460
    $rega_url = $this->config->get_value('iso20775_server','setup');
78303 fvs 461
    if (isset($param->pid)) {
115821 fvs 462
      list($bibpart, $recid) = explode(':', self::skip_multi_part_pid_info($param->pid->_value));
78303 fvs 463
    }
464
    else {
465
      $recid = $param->bibliographicRecordId->_value;
466
    }
82472 fvs 467
    $post->trackingId = $this->tracking_id;
468
    $post->timeout = 10000;
469
    $post->records[0]->baseUrl = $info['url'];
470
    $post->records[0]->recordId = $recid;
471
    $this->curl->set_post(json_encode($post), 0);
472
    $this->curl->set_option(CURLOPT_HTTPHEADER, array('Content-Type: application/json; charset=utf-8'), 0);
78303 fvs 473
    if ($this->config->get_value('use_test_iso20775_replies','setup')) {
82472 fvs 474
      $result[0]->record->holding = self::test_iso20775_reply($recid, self::strip_agency($param->responderId->_value));
475
      $result[0]->responseCode = 200;
78303 fvs 476
      $curl_status['http_code'] = 200;
477
    }
478
    else {
479
      $this->watch->start('iso20775');
82472 fvs 480
      $result = @ json_decode($this->curl->get($rega_url, 0));
481
      $curl_status = $this->curl->get_status();
78303 fvs 482
      $this->watch->stop('iso20775');
483
    }
82472 fvs 484
//var_dump(json_encode($post)); var_dump($result); var_dump($curl_status); die();
78303 fvs 485
    if ($curl_status['http_code'] == 200) {
82880 fvs 486
      if ($result[0]->responseCode == 200) {
487
        if ($result[0]->holding) {
488
          verbose::log(TRACE, 'OpenHoldings:: iso20775: ' . $info['url'] .
489
                              ' id: ' . $recid . ' record: ' . str_replace("\n", '', $result[0]->holding));
490
          if ($status = self::parse_iso20775_holding($result[0]->holding)) {
491
            return $detailed ? $status : self::parse_status($status);
492
            //return $status;
493
          }
494
          else {
495
            verbose::log(ERROR, 'OpenHoldings:: Cannot parse: "' . $result[0]->holding .
496
                                '" from: ' . $info['url']);
497
            return 'cannot_parse_library_answer';
498
          }
82472 fvs 499
        }
500
        else {
82880 fvs 501
          verbose::log(TRACE, 'OpenHoldings:: External responseCode: 200 ' .
502
                              ' errorMsg: "' . $result[0]->errorMsg .
503
                              ' holding: "' . $result[0]->holding .
504
                              '" for: ' . $info['url']);
505
          return 'item_not_found';
82472 fvs 506
        }
45961 fvs 507
      }
78303 fvs 508
      else {
82880 fvs 509
        verbose::log(ERROR, 'OpenHoldings:: External responseCode: ' . $result[0]->responseCode .
510
                            ' errorMsg: "' . $result[0]->errorMsg .
82472 fvs 511
                            ' holding: "' . $result[0]->holding .
512
                            '" for: ' . $info['url']);
513
        return 'error_searching_library';
6879 fvs 514
      }
78303 fvs 515
    }
516
    else {
82880 fvs 517
      verbose::log(FATAL, 'OpenHoldings:: http error: ' . $curl_status['http_code'] .
78303 fvs 518
                          ' error: "' . $curl_status['error'] .
519
                          '" for: ' . $curl_status['url']);
520
      return 'error_searching_library';
521
    }
522
 
6822 fvs 523
  }
524
 
78303 fvs 525
  /** \brief Parse a holding record and extract the status
6822 fvs 526
   *
79874 fvs 527
   * @param $holding string - xml document
78303 fvs 528
   * @retval mixed
6822 fvs 529
   */
78303 fvs 530
  private function parse_iso20775_holding($holding) {
531
    if ($this->dom->loadXML($holding)) {
532
      foreach ($this->dom->getElementsByTagName('holding') as $item) {
533
        if (!$h['fee'] = self::first_node_value($item, 'summaryPolicy/feeInformation/feeText')) {
534
          $h['fee'] = self::prefix('Reason: ', self::first_node_value($item, 'summaryPolicy/feeInformation/feeStructured/feeReason'));
535
          $h['fee'] .= self::prefix('Unit: ', self::first_node_value($item, 'summaryPolicy/feeInformation/feeStructured/feeUnit'));
536
          $h['fee'] .= self::prefix('Amount: ', self::first_node_value($item, 'summaryPolicy/feeInformation/feeStructured/feeAmount'));
6879 fvs 537
        }
97406 fvs 538
        $summary_note = '';
539
        if ($summary_note = self::first_node_value($item, 'summaryPolicy/availability/text')) {
540
          $summary_note = ' - ' . $summary_note;
541
        }
78303 fvs 542
        if ($item->getElementsByTagName('holdingSimple')->length) {
543
          $h['id'] = self::first_node_value($this->dom, 'resource/resourceIdentifier/value');
97406 fvs 544
          $h['note'] = self::first_node_value($this->dom, 'copyInformation/note') . $summary_note;
78303 fvs 545
          if (self::first_node_value($item, 'holdingSimple/copiesSummary/status/availableCount')) {
546
            $h['policy'] = 1;
547
            $h['date'] = self::first_node_value($item, 'holdingSimple/copiesSummary/status/earliestDispatchDate');
548
          }
549
          else {
550
            $h['policy'] = 0;
551
          }
552
          $hold[] = $h;
553
        }
554
        else {
555
          foreach ($item->getElementsByTagName('holdingStructured')->item(0)->getElementsByTagName('set') as $hs) {
556
            foreach ($hs->getElementsByTagName('component') as $cpn) {
557
              $h['id'] = self::first_node_value($cpn, 'pieceIdentifier/value');
82472 fvs 558
              $h['level-0'] =
559
              $h['item'] = self::first_node_value($cpn, 'enumerationAndChronology/text');
78303 fvs 560
              $h['date'] = self::first_node_value($cpn, 'availabilityInformation/status/dateTimeAvailable');
561
              $h['policy'] = self::first_node_value($cpn, 'availabilityInformation/status/availabilityStatus');
562
              $hold[] = $h;
563
            }
564
          }
565
        }
566
      }
567
    }
568
    else {
569
      return FALSE;
570
    }
571
    //var_dump($holding); var_dump($hold); die();
572
    return $hold;
40578 fvs 573
  }
6822 fvs 574
 
575
  /** \brief Parse a holding record and extract the status
576
   *
79874 fvs 577
   * @param $holding string - xml document
78303 fvs 578
   * @retval mixed
6822 fvs 579
   */
78303 fvs 580
  private function parse_z3950_holding($holding) {
47460 fvs 581
    if ($this->dom->loadXML($holding)) {
47030 fvs 582
      //echo str_replace('?', '', $holding);
80203 fvs 583
/*
584
         <bibView-11 targetBibPartId-40="09267999ZXZX1991/2006ZX0000ZXZX" numberOfPieces-56="1" >
585
            <bibPartLendingInfo-116 servicePolicy-109="1" >
586
            </bibPartLendingInfo-116>
587
            <bibPartEnumeration-45 enumLevel-93="1" enumCaption-94="År: " specificEnumeration-95="0000" >
588
               <ChildEnumeration enumLevel-93="2" enumCaption-94="Volume: " specificEnumeration-95="1991/2006" >
589
               </ChildEnumeration>
590
            </bibPartEnumeration-45>
591
         </bibView-11>
592
*/
100635 fvs 593
      if (!$this->dom->getElementsByTagName('holdingsSiteLocation-6')->length) {
594
        return array(array('note' => 'No holding'));
595
      }
596
      @ $target_location_id = $this->dom->getElementsByTagName('holdingsSiteLocation-6')->item(0)->getAttribute('targetLocationId-26');
47460 fvs 597
      foreach ($this->dom->getElementsByTagName('bibView-11') as $item) {
40578 fvs 598
        $h = array();
599
        foreach ($item->attributes as $key => $attr)
47030 fvs 600
          if ($key == 'targetBibPartId-40')
601
            $h['id'] = $attr->nodeValue;
602
        foreach ($item->getElementsByTagName('bibPartLendingInfo-116') as $info) {
40578 fvs 603
          foreach ($info->attributes as $key => $attr)
604
            switch ($key) {
47030 fvs 605
              case 'servicePolicy-109' :
606
                $h['policy'] = $attr->nodeValue;
40578 fvs 607
                break;
47030 fvs 608
              case 'servicefee-110' :
609
                $h['fee'] = 'fee'; // $attr->nodeValue; 2do in seperate tag??
40578 fvs 610
                break;
47030 fvs 611
              case 'expectedDispatchDate-111' :
612
                $h['date'] = $attr->nodeValue;
40578 fvs 613
                break;
47030 fvs 614
              case 'serviceNotes-112' :
615
                $h['note'] = $attr->nodeValue;
40578 fvs 616
                break;
617
            }
618
        }
82880 fvs 619
        $h['target_location_id'] = $target_location_id;
80203 fvs 620
        foreach ($item->getElementsByTagName('bibPartEnumeration-45') as $info) {
621
          self::get_enumeration($info, $h);
622
        }
623
        foreach ($item->getElementsByTagName('bibPartChronology-46') as $info) {
624
          self::get_chronology($info, $h);
625
        }
6822 fvs 626
 
627
        $hold[] = $h;
40578 fvs 628
      }
78303 fvs 629
      if (empty($hold)) {
47030 fvs 630
        return array(array('note' => 'No holding'));
78303 fvs 631
      }
632
      else {
40578 fvs 633
        return $hold;
78303 fvs 634
      }
635
    }
636
    else {
40578 fvs 637
      return FALSE;
78303 fvs 638
    }
6822 fvs 639
  }
640
 
80203 fvs 641
  /** \brief parse attributes of bibpartchronology-46
642
   *
643
   *  <bibpartchronology-46 chronLevel-96="1" chroncaption-97="År: " specificchronology-98="0000" >
644
   *  </bibpartchronology-46
645
   *
646
   * @param $node DOMNode
647
   * @param $list array
648
   */
649
  private function get_chronology($node, &$list) {
650
    if ($node) {
651
      foreach ($node->attributes as $key => $attr) {
652
        switch ($key) {
653
          case 'chronLevel-96':
654
            $level = $attr->nodeValue;
655
            break;
656
          case 'chroncaption-97':
657
            $caption = $attr->nodeValue;
658
            break;
659
          case 'specificchronology-98':
660
            $enum = $attr->nodeValue;
661
            break;
662
        }
663
      }
664
      $list['level-0'] = $enum;
665
      $list['item'] .= $caption . $enum . ' ';
666
    }
667
  }
668
 
669
  /** \brief parse attributes of bibPartEnumeration-45 and ChildEnumeration
670
   *
671
   *  <bibPartEnumeration-45 enumLevel-93="1" enumCaption-94="År: " specificEnumeration-95="0000" >
672
   *    <ChildEnumeration enumLevel-93="2" enumCaption-94="Volume: " specificEnumeration-95="1991/2006" >
673
   *    </ChildEnumeration>
674
   *  </bibPartEnumeration-45>
675
   *
676
   * @param $node DOMNode
677
   * @param $list array
678
   */
679
  private function get_enumeration($node, &$list) {
680
    if ($node) {
681
      foreach ($node->attributes as $key => $attr) {
682
        switch ($key) {
683
          case 'enumLevel-93':
684
            $level = $attr->nodeValue;
685
            break;
686
          case 'enumCaption-94':
687
            $caption = $attr->nodeValue;
688
            break;
689
          case 'specificEnumeration-95':
690
            $enum = $attr->nodeValue;
691
            break;
692
        }
693
      }
694
      $list['level-' . $level] = $enum;
82880 fvs 695
      $list['item'] .= trim($caption) . $enum . ' ';
80203 fvs 696
      self::get_enumeration($node->getElementsByTagName('ChildEnumeration')->item(0), $list);
697
    }
698
  }
699
 
78303 fvs 700
  /** \brief Parse status for availability
701
   *
79874 fvs 702
   * @param $status array
78303 fvs 703
   * @retval mixed
704
   */
705
  private function parse_status($status) {
706
    if (count($status) == 1 && $status[0]['policy']) {
707
      $s = &$status[0];
708
      $ret->localHoldingsId->_value = $s['id'];
709
      if ($s['note'])
710
        $ret->note->_value = $s['note'];
711
      $h_date = substr($s['date'],0,10);
712
      if ($s['policy'] == 1) {
713
        $ret->willLend->_value = 'true';
714
        if ($h_date >= date('Y-m-d'))
715
          $ret->expectedDelivery->_value = $h_date;
716
      } elseif (($s['policy'] == 2))
717
          $ret->willLend->_value = 'false';
718
    } elseif (count($status) > 1) {
78363 fvs 719
      $ret->note->_value = 'check_local_library';
78303 fvs 720
      $ret->willLend->_value = 'true';
721
      $pol = 0;
722
      foreach ($status as $s)
723
        if ($s['policy'] <> 1) {
724
          $ret->willLend->_value = 'false';
725
          break ;
726
        }
727
    } else
728
      $ret = 'no_holdings_specified_by_library';
729
 
730
    return $ret;
731
  }
732
 
733
  /** \brief Get the protocol and address for a library from openAgency
734
   *
79874 fvs 735
   * @param $lib string - library number
78303 fvs 736
   * @retval mixed
737
   */
738
  private function find_protocol_and_address($lib) {
54484 fvs 739
    static $z_infos = array();
740
    if (empty($z_infos[$lib])) {
47460 fvs 741
      $url = sprintf($this->config->get_value('agency_server_information','setup'),
78303 fvs 742
                     self::strip_agency($lib));
115821 fvs 743
      $this->curl->set_option(CURLOPT_POST, 0);
47460 fvs 744
      $res = $this->curl->get($url);
745
      $curl_status = $this->curl->get_status();
746
      if ($curl_status['http_code'] == 200) {
747
        if ($this->dom->loadXML($res)) {
78303 fvs 748
          $z_infos[$lib]['url'] = self::first_node_value($this->dom, 'address');
749
          switch ($z_infos[$lib]['protocol'] = self::first_node_value($this->dom, 'protocol')) {
750
            case 'iso20775':
751
              $z_infos[$lib]['password'] = self::first_node_value($this->dom, 'passWord');
752
              break;
753
            case 'z3950':
754
              $auth = self::first_node_value($this->dom, 'userId') . '/' .
755
                      self::first_node_value($this->dom, 'groupId') . '/' .
756
                      self::first_node_value($this->dom, 'passWord');
757
              if ($auth <> '//') {
758
                $z_infos[$lib]['authentication'] = $auth;
759
              }
760
              break;
761
            default:
59663 fvs 762
          }
47460 fvs 763
        }
764
        else {
765
          verbose::log(ERROR, 'OpenHoldings:: Cannot parse serverInformation url ' . $url);
6822 fvs 766
          return FALSE;
767
        }
768
      }
47460 fvs 769
      else {
770
        verbose::log(ERROR, 'OpenHoldings:: fetch serverInformation http code: ' . $curl_status['http_code'] .
771
                            ' error: "' . $curl_status['error'] .
772
                            '" for: ' . $curl_status['url']);
773
        return FALSE;
774
      }
6822 fvs 775
    }
54484 fvs 776
    return $z_infos[$lib];
6822 fvs 777
  }
778
 
78303 fvs 779
  /** \brief Return first nodeValue from a dom node
780
   *
79874 fvs 781
   * @param $domnode DOMNode
782
   * @param $path string - path to node like 'tagA/tagB/tagC'
78303 fvs 783
   * @retval mixed - string or FALSE
784
   */
785
  private function first_node_value($dom_node, $path) {
786
    $tags = explode('/', $path);
787
    foreach ($tags as $tag) {
788
      $dom_list = $dom_node->getElementsByTagName($tag);
789
      if ($dom_list->length) {
790
        $dom_node = $dom_list->item(0);
791
      }
792
      else {
793
        return NULL;
794
      }
795
    }
796
    return $dom_node->nodeValue;
797
  }
798
 
799
  /** \brief Puts $prefix and '. ' around value if set
800
   *
79874 fvs 801
   * @param $prefix string
802
   * @param $value string
78303 fvs 803
   * @retval string
804
   */
805
  private function prefix($prefix, $value) {
806
    return ($value ? $prefix . $value . '. ': $value);
807
  }
808
 
40578 fvs 809
  /** \brief
79874 fvs 810
   * @param $id string - library number or ISIL library id
78303 fvs 811
   * @retval string - return only digits, so something like DK-710100 returns 710100
40578 fvs 812
   */
813
  private function strip_agency($id) {
814
    return preg_replace('/\D/', '', $id);
815
  }
816
 
78303 fvs 817
  /** \brief return true if xs:boolean is so
79874 fvs 818
   * @param $str string
78303 fvs 819
   * @retval boolean
47030 fvs 820
   */
821
  private function xs_boolean($str) {
822
    return (strtolower($str) == 'true' || $str == 1);
823
  }
824
 
78303 fvs 825
  /** \brief Helper function for sorting
79874 fvs 826
   * @param $a array
827
   * @param $b array
78303 fvs 828
   * @retval boolean
47030 fvs 829
   */
830
  private function compare($a, $b) {
831
    return $a['sort'] > $b['sort'];
832
  }
833
 
79874 fvs 834
  /** \brief Helper function for test when no iso20775 host exist
835
   * @param $id string
836
   * @param $lib string
837
   * @retval string - iso20775 xml reply
838
   */
78363 fvs 839
  private function test_iso20775_reply($id, $lib) {
78303 fvs 840
    require_once('examples.php');
78363 fvs 841
    return test_iso_reply($id, $lib);
78303 fvs 842
  }
115821 fvs 843
 
844
  /** \brief Remove multi part information from pid. Multipart infor is placed after two _
845
   * @param $pid string
846
   * @retval string
847
   */
848
  private function skip_multi_part_pid_info($pid) {
849
    list($collection, $id) = explode(':', $pid, 2);
850
    if ($p = strpos($id, '__')) {
851
      return $collection . ':' . substr($id, 0, $p);
852
    }
853
    return $pid;
854
  }
119114 fvs 855
 
856
  /** \brief Return type of library
857
   * @param $agency string
858
   * @retval string
859
   */
860
  private function bib_type($agency) {
861
    $agency_type = $this->openagency->get_agency_type($agency);
862
    return ($agency_type ? $agency_type : 'other');
863
  }
864
 
865
  /** \brief split pid and return length of id
866
   * @param $pid string
867
   * @retval integer
868
   */
869
  private function id_length($pid) {
870
    list($ns, $id) = explode(':', $pid);
871
    return strlen($id);
872
  }
873
 
874
  /** \brief return a given library rule for a given agency
875
   * @param $agency string - the library
876
   * @param $rule string - the library rule
877
   * @retval boolean
878
   */
879
  private function library_rule($agency, $rule) {
880
    static $part_of = array();
881
    if (!isset($part_of[$agency])) {
882
      $part_of[$agency] = $this->openagency->get_library_rules($agency);
883
    }
884
    return $part_of[$agency][$rule];
885
  }
886
 
887
  /** \brief Do a solr search and return id's matching the ones in queries
119874 fvs 888
   * @param $query string
119114 fvs 889
   * @retval array
890
   */
119874 fvs 891
  private function get_solr($query) {
119114 fvs 892
    $ret = array();
119874 fvs 893
    $solr_uri = sprintf($this->config->get_value('solr_uri', 'setup'), urlencode($query));
119114 fvs 894
    $json = $this->curl->get($solr_uri);
895
    $curl_status = $this->curl->get_status();
896
    if ($curl_status['http_code'] !== 200) {
897
      verbose::log(ERROR, 'OpenHoldings:: fetch from solr http code: ' . $curl_status['http_code'] .
898
                          ' error: "' . $curl_status['error'] .
899
                          '" for: ' . $curl_status['url']);
900
      return $ret;
901
    }
902
 
903
    $res = json_decode($json);
904
 
905
    foreach ($res->response->docs as $doc) {
906
      foreach ($doc->{'rec.id'} as $id) {
907
        if ($queries[$id]) {
908
          $ret[$id] = true;
909
        }
910
      }
911
    }
912
    return $ret;
913
  }
6822 fvs 914
}
915
 
916
/*
917
 * MAIN
918
 */
919
 
47460 fvs 920
$ws=new openHoldings();
6822 fvs 921
$ws->handle_request();
922