Grima  2018-08
Whispering into Alma's ear
grima-lib.php
Go to the documentation of this file.
1 <?php
8 require_once("grima-util.php");
9 require_once("grima-xmlbag.php");
10 require_once("grima-splats.php");
11 require_once("MARC21.php");
12 
13 // {{{ class Grima
15 class Grima {
16  public $server;
17  public $apikey;
18 
19 // {{{ session management
20 
21  function session_init( $force = false ) {
22  # Session Module
23  $session_module_name = getenvWithFileFallbackAndDefault('SESSION_MODULE','encrypted_cookie');
24  if ($session_module_name=="none") {
25  return;
26  } else if ($session_module_name=="encrypted_cookie") {
27  $session_key = base64_decode( getenvWithFileFallbackAndDefault( 'SESSION_KEY',
28  "base64EncodedGrimaKeyOf32BytesTotallyRandom=" ) );
29  session_set_save_handler(new EncryptedCookieSession($session_key));
30  ini_set("session.use_cookies",false);
31  } else if ($session_module_name) {
32  $new_name = session_module_name($session_module_name);
33  if ($new_name != $session_module_name) {
34  session_name( "grima_failsafe" );
35  session_start();
36  return;
37  }
38  }
39  # Session Name
40  $session_name = getenvWithFileFallbackAndDefault('SESSION_NAME', 'grima');
41  session_name( $session_name );
42  # Session Path
43  $session_path = getenvWithFileFallbackAndDefault('SESSION_PATH', join_paths( sys_get_temp_dir(), 'grima' ) );
44  if (session_module_name()==='files') {
45  @mkdir($session_path, 0700, true);
46  }
47  session_save_path($session_path);
48  # Session lifetime
49  session_set_cookie_params(365*24*60*60); # one year
50  ini_set('session.gc_maxlifetime',525600*60); # of love
51  # Start session
52  session_start();
53  }
54 
55  function session_save($result) {
56  $session_module_name = getenvWithFileFallbackAndDefault('SESSION_MODULE','encrypted_cookie');
57  if ($session_module_name === "none") return;
58  $this->session_init(true);
59  foreach( $result as $key => $value ) {
60  $_SESSION[$key] = $value;
61  }
62  session_write_close();
63  }
64 
65  function session_destroy() {
66  $session_module_name = getenvWithFileFallbackAndDefault('SESSION_MODULE','encrypted_cookie');
67  if ($session_module_name === "none") return;
68  $_SESSION = [];
69  if ( ini_get("session.use_cookies") ) {
70  $params = session_get_cookie_params();
71  $params['expires'] = time() - 42000;
72  unset($params['lifetime']);
73  setcookie(session_name(), '', $params );
74  }
75  $this->session_init(true);
76  $_SESSION = [];
78  }
79 
80 // }}}
81 
82 // {{{ config
83  function __construct() {
84 
85  $this->session_init();
86 
87  $this->apikey =
88  isset($_REQUEST['apikey']) && $_REQUEST['apikey'] ? $_REQUEST['apikey'] : (
89  isset($_SESSION['apikey']) && $_SESSION['apikey'] ? $_SESSION['apikey'] : (
90  getenvWithFileFallbackAndDefault('apikey','') ) );
91 
92  $this->server =
93  isset($_REQUEST['server']) && $_REQUEST['server'] ? $_REQUEST['server'] : (
94  isset($_SESSION['server']) && $_SESSION['server'] ? $_SESSION['server'] : (
95  getenvWithFileFallbackAndDefault('server','') ) );
96 
97  if (strlen($this->server)==2) {
98  $this->server = "https://api-" . $this->server . ".hosted.exlibrisgroup.com";
99  }
100 
101  if (!preg_match('!https://api-[a-z]*\.hosted\.exlibrisgroup\.com!',$this->server)) {
102  $this->server = "";
103  }
104 
105  if ($this->apikey=='EDITME') {
106  $this->apikey = "";
107  }
108 
109  if ($this->apikey && $this->server) {
110  $_SESSION['apikey'] = $this->apikey;
111  $_SESSION['server'] = $this->server;
112  }
113 
114  session_write_close();
115 
116  return false;
117  }
118 
119 // }}}
120 
121 // {{{ REST - get/post/put/delete
122 
123 // {{{ request - general function for API calls
135  function request($method, $url, $URLparams, $QSparams, $body=null) {
136  $headers = array(
137  'Authorization: apikey ' . urlencode($this->apikey),
138  "Accept: application/xml",
139  );
140 
141  // Change "/almaws/v1/users/{user_id}" to "/almaws/v1/users/${URLparams['user_id']}"
142  foreach ($URLparams as $k => $v) {
143  if (!$v) { throw new Exception("URL parameter $k is empty in $method $url"); }
144  $url = str_replace('{'.$k.'}',urlencode($v),$url);
145  }
146 
147  // Include APIKey in url
148  $url = $this->server . $url . '?apikey=' . urlencode($this->apikey) . '&format=xml';
149 
150  // Include query string parameters too
151  foreach ($QSparams as $k => $v) {
152  $url .= "&$k=$v";
153  }
154 
155  // Include XML based body as well
156  // XXX: detect if JSON is more appropriate?
157  if ($body) {
158  $bodyxml = $body->saveXML();
159  $headers[] = 'Content-Type: application/xml';
160  }
161 
162  $ch = curl_init();
163  curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
164  curl_setopt($ch, CURLOPT_URL, $url);
165  curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
166  curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
167 
168  $headerCapture = new HeaderCapture();
169  curl_setopt($ch, CURLOPT_HEADERFUNCTION, [&$headerCapture,"capture"]);
170 
171  # ExLibris / Debian interoperability problem
172  # https://github.com/openssl/openssl/issues/7126
173  # error_log("OPENSSL_VERSION_NUMBER=" .dechex(OPENSSL_VERSION_NUMBER));
174  # 0x01000214f on reclaim
175  if (OPENSSL_VERSION_NUMBER >= 0x010101000) {
176  // debian 10 workaround until Ex Libris patches their servers
177  curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, "DEFAULT@SECLEVEL=1");
178  }
179 
180 
181  if (isset($bodyxml)) {
182  curl_setopt($ch, CURLOPT_POSTFIELDS, $bodyxml);
183  }
184  curl_setopt($ch, CURLOPT_HTTPHEADER, $headers );
185 
186  $response = curl_exec($ch);
187  #error_log( print_r($headerCapture,true) );
188 
189  $code = curl_getinfo($ch,CURLINFO_HTTP_CODE);
190  if (curl_errno($ch)) {
191  throw new Exception("Network error: " . curl_error($ch));
192  }
193  curl_close($ch);
194  if ($code == 204) return; // no body
195 
196  $xml = new DOMDocument();
197  $xml->formatOutput = true;
198  try {
199  if (preg_match(',^application/json,',$headerCapture->headers['content-type'])) {
200  $array = json_decode ($response, true);
201  $xml->loadXML('<?xml version="1.0"?' . '><json/>');
202  arrayToXml($array, $xml->documentElement);
203  } else if (!preg_match('/^</',$response)) {
204  $xml->loadXML(
205  '<?xml version="1.0"?' . '><html><body><h1>Not xml</h1><pre>'
206  . htmlspecialchars($response).
207  '</pre></body></html>'
208  );
209  } else {
210  $xml->loadXML($response);
211  }
212  } catch (Exception $e) {
213  throw new Exception("Malformed XML from Alma:\n$method $url -> $code\n$e");
214  }
215 
216  $xml->rawText = $response;
217  $xml->httpCode = $code;
218  $xml->headers = $headerCapture->headers;
219  $xml->rawHeaders = $headerCapture->rawHeaders;
220 
221  return $xml;
222  }
223 // }}}
224 
225 // {{{ get - general function for GET (retrieve) API calls
234  function get($url,$URLparams,$QSparams) {
235  return $this->request("GET",$url,$URLparams,$QSparams,null);
236  }
237 // }}}
238 
239 // {{{ post - general function for POST (create) API calls
249  function post($url,$URLparams,$QSparams,$body) {
250  return $this->request("POST",$url,$URLparams,$QSparams,$body);
251  }
252 // }}}
253 
254 // {{{ put - general function for PUT (update) API calls
264  function put($url,$URLparams,$QSparams,$body) {
265  return $this->request("PUT",$url,$URLparams,$QSparams,$body);
266  }
267 // }}}
268 
269 // {{{ delete - general function for DELETE API calls
278  function delete($url,$URLparams,$QSparams) {
279  return $this->request("DELETE",$url,$URLparams,$QSparams,null);
280  }
281 // }}}
282 
283 // {{{ checkForErrorMessage - checks for errorMessage tag, throws exceptions
288  function checkForErrorMessage($xml) {
289  if ($xml instanceOf DomDocument) {
290  $xpath = new DomXpath($xml);
291  $xpath->registerNamespace("err","http://com/exlibris/urm/general/xmlbeans");
292  $error = $xpath->query('//err:errorMessage');
293  if ($error->length > 0) {
294  throw new Exception("Alma says: " . $error[0]->nodeValue);
295  }
296  }
297  }
298 // }}}
299 
300 // }}}
301 
302 //{{{Bib APIs
306 // {{{ getBib (Retrieve Bib)
320  function getBib($mms_id, $view = 'full', $expand = 'None') {
321  $ret = $this->get('/almaws/v1/bibs/{mms_id}',
322  array('mms_id' => $mms_id),
323  array('view' => $view, 'expand' => $expand)
324  );
325  $this->checkForErrorMessage($ret);
326  return $ret;
327  }
328 // }}}
329 
330 // {{{ postBib (Create Record)
342  function postBib($bib) {
343  $ret = $this->post('/almaws/v1/bibs',
344  array(),
345  array(),
346  $bib
347  );
348  $this->checkForErrorMessage($ret);
349  return $ret;
350  }
351 // }}}
352 
353 // {{{ putBib (Update Bib Record)
366  function putBib($mms_id,$bib) {
367  $ret = $this->put('/almaws/v1/bibs/{mms_id}',
368  array('mms_id' => $mms_id),
369  array(),
370  $bib
371  );
372  $this->checkForErrorMessage($ret);
373  return $ret;
374  }
375 // }}}
376 
377 // {{{ deleteBib (Delete Bib Record)
389  function deleteBib($mms_id,$override='false') {
390  $ret = $this->delete('/almaws/v1/bibs/{mms_id}',
391  array('mms_id' => $mms_id),
392  array('override' => $override)
393  );
394  $this->checkForErrorMessage($ret);
395  }
396 // }}}
397 
399 //}}}
400 
401 //{{{Holdings List APIs
405 // {{{ grima -> getHoldingsList (Retrieve Holdings list)
418  function getHoldingsList($mms_id) {
419  $ret = $this->get('/almaws/v1/bibs/{mms_id}/holdings',
420  array('mms_id' => $mms_id),
421  array()
422  );
423  $this->checkForErrorMessage($ret);
424  return $ret;
425  }
426 // }}}
427 
429 //}}}
430 
431 //{{{Holding APIs
435 // {{{ getHolding (Retrieve Holdings Record)
448  function getHolding($mms_id,$holding_id) {
449  $ret = $this->get('/almaws/v1/bibs/{mms_id}/holdings/{holding_id}',
450  array(
451  'mms_id' => $mms_id,
452  'holding_id' => $holding_id
453  ),
454  array()
455  );
456  $this->checkForErrorMessage($ret);
457  return $ret;
458  }
459 // }}}
460 
461 // {{{ grima -> postHolding (Create holding record)
474  function postHolding($mms_id,$holding) {
475  $ret = $this->post('/almaws/v1/bibs/{mms_id}/holdings',
476  array('mms_id' => $mms_id),
477  array(),
478  $holding
479  );
480  $this->checkForErrorMessage($ret);
481  return $ret;
482  }
483 // }}}
484 
485 // {{{ putHolding (Update Holdings Record)
499  function putHolding($mms_id,$holding_id,$holding) {
500  $ret = $this->put('/almaws/v1/bibs/{mms_id}/holdings/{holding_id}',
501  array('mms_id' => $mms_id, 'holding_id' => $holding_id),
502  array(),
503  $holding
504  );
505  }
506 // }}}
507 
508 // {{{ grima -> deleteHolding (Delete Holdings Record)
521  function deleteHolding($mms_id,$holding_id,$override='false') {
522  $ret = $this->delete('/almaws/v1/bibs/{mms_id}/holdings/{holding_id}',
523  array(
524  'mms_id' => $mms_id,
525  'holding_id' => $holding_id
526  ),
527  array('override' => $override)
528  );
529  $this->checkForErrorMessage($ret);
530  }
531 // }}}
532 
534 //}}}
535 
536 //{{{Item List APIs
540 // {{{ getItemList (Retrieve Items list)
555  function getItemList($mms_id,$holding_id,$limit,$offset) {
556  $ret = $this->get('/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items', array('mms_id' => $mms_id, 'holding_id' => $holding_id),
557  array('limit' => $limit, 'offset' => $offset)
558  );
559  $this->checkForErrorMessage($ret);
560  return $ret;
561  }
562 // }}}
563 
565 //}}}
566 
567 //{{{Item APIs
571 // {{{ getItem (Retrieve Item and print label information)
584  function getItem($mms_id,$holding_id,$item_pid) {
585  $ret = $this->get('/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items/{item_pid}', array(
586  'mms_id' => $mms_id,
587  'holding_id' => $holding_id,
588  'item_pid' => $item_pid
589  ),
590  array()
591  );
592  $this->checkForErrorMessage($ret);
593  return $ret;
594  }
595 // }}}
596 
597 // {{{ getItemBC (Retrieve Item and print label information (by barcode))
608  function getItemBC($barcode) {
609  $ret = $this->get('/almaws/v1/items',
610  array(),
611  array(
612  'item_barcode' => $barcode,
613  )
614  );
615  $this->checkForErrorMessage($ret);
616  return $ret;
617  }
618 // }}}
619 
620 // {{{ postItem (Create Item)
634  function postItem($mms_id,$holding_id,$item) {
635  $ret = $this->post('/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items',
636  array('mms_id' => $mms_id, 'holding_id' => $holding_id),
637  array(),
638  $item
639  );
640  $this->checkForErrorMessage($ret);
641  return $ret;
642  }
643 // }}}
644 
645 // {{{ putItem (Update Item information)
660  function putItem($mms_id,$holding_id,$item_pid,$item) {
661  $ret = $this->put('/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items/{item_pid}',
662  array('mms_id' => $mms_id, 'holding_id' => $holding_id, 'item_pid' => $item_pid),
663  array(),
664  $item
665  );
666  return $ret;
667  }
668 // }}}
669 
670 // {{{ deleteItem (Withdraw Item)
685  function deleteItem($mms_id,$holding_id,$item_pid,$override = "false",
686  $holdings = "retain") {
687  $ret = $this->delete('/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items/{item_pid}', array(
688  'mms_id' => $mms_id,
689  'holding_id' => $holding_id,
690  'item_pid' => $item_pid
691  ), array(
692  'override' => $override,
693  'holdings' => $holdings
694  )
695  );
696  $this->checkForErrorMessage($ret);
697  }
698 // }}}
699 
701 //}}}
702 
703 //{{{Electronic APIs
707 // {{{ getElectronicPortfolio (Retrieve Portfolio)
721  function getElectronicPortfolio($collection_id,$service_id,$portfolio_id) {
722  $ret = $this->get('/almaws/v1/electronic/e-collections/{collection_id}/e-services/{service_id}/portfolios/{portfolio_id}',
723  array('collection_id' => $collection_id, 'service_id' => $service_id, 'portfolio_id' => $portfolio_id),
724  array()
725  );
726  $this->checkForErrorMessage($ret);
727  return $ret;
728  }
729 // }}}
730 
731 // {{{ getElectronicPortfolioFromBib (Retrieve Portfolio)
744  function getElectronicPortfolioFromBib($mms_id, $portfolio_id) {
745  $ret = $this->get('/almaws/v1/bibs/{mms_id}/portfolios/{portfolio_id}',
746  array('mms_id' => $mms_id, 'portfolio_id' => $portfolio_id),
747  array()
748  );
749  $this->checkForErrorMessage($ret);
750  return $ret;
751  }
752 // }}}
753 
754 // {{{ postElectronicPortfolio (Create Electronic Portfolio) #XXX
768  function postElectronicPortfolio($collection_id,$service_id,$portfolio) {
769  $ret = $this->post('/almaws/v1/electronic/e-collections/{collection_id}/e-services/{service_id}/portfolios',
770  array('collection_id' => $collection_id, 'service_id' => $service_id),
771  array(),
772  $portfolio
773  );
774  $this->checkForErrorMessage($ret);
775  return $ret;
776  }
777 // }}}
778 
779 // {{{ postElectronicPortfolioOnBib (Create Electronic Portfolio on Bib)
793  function postElectronicPortfolioOnBib($mms_id,$portfolio,$update = true) {
794  $ret = $this->post('/almaws/v1/bibs/{mms_id}/portfolios/',
795  array('mms_id' => $mms_id),
796  array(),
797  $portfolio
798  );
799  $this->checkForErrorMessage($ret);
800  if ($update) { $portfolio->loadXML($ret->saveXML()); }
801  return $ret;
802  }
803 // }}}
804 
805 // {{{ putElectronicPortfolioOnBib (Update Portfolio for a Bib)
818  function putElectronicPortfolioOnBib($mms_id,$portfolio_id,$portfolio) {
819  $ret = $this->put('/almaws/v1/bibs/{mms_id}/portfolios/{portfolio_id}',
820  array('mms_id' => $mms_id, 'portfolio_id' => $portfolio_id),
821  array(),
822  $portfolio
823  );
824  $this->checkForErrorMessage($ret);
825  return $ret;
826  }
827 // }}}
828 
829 // {{{ deleteElectronicPortfolio (Delete Electronic Portfolio)
842  function deleteElectronicPortfolio($collection_id,$service_id,$portfolio_id) {
843  $ret = $this->delete('/almaws/v1/electronic/e-collections/{collection_id}/e-services/{service_id}/portfolios/{portfolio_id}',
844  array('collection_id' => $collection_id, 'service_id' => $service_id, 'portfolio_id' => $portfolio_id),
845  array()
846  );
847  $this->checkForErrorMessage($ret);
848  }
849 // }}}
850 
851 // {{{ getElectronicPortfoliosForService (Retrieve Portfolios)
866  function getElectronicPortfoliosForService($collection_id, $service_id, $limit, $offset) {
867  $ret = $this->get('/almaws/v1/electronic/e-collections/{collection_id}/e-services/{service_id}/portfolios',
868  array('collection_id' => $collection_id, 'service_id' => $service_id),
869  array('limit' => $limit, $offset = $offset)
870  );
871  $this->checkForErrorMessage($ret);
872  return $ret;
873  }
874 // }}}
875 
876 // {{{ grima -> getElectronicPortfoliosForBib (Retrieve Portfolios)
890  function getElectronicPortfoliosForBib($mms_id, $limit, $offset) {
891  $ret = $this->get('/almaws/v1/bibs/{mms_id}/portfolios',
892  array('mms_id' => $mms_id),
893  array('limit' => $limit, $offset = $offset)
894  );
895  $this->checkForErrorMessage($ret);
896  return $ret;
897  }
898 // }}}
899 
900 // {{{ getElectronicCollection (Retrieve Electronic Collection)
912  function getElectronicCollection($collection_id) {
913  $ret = $this->get('/almaws/v1/electronic/e-collections/{collection_id}',
914  array('collection_id' => $collection_id),
915  array()
916  );
917  $this->checkForErrorMessage($ret);
918  return $ret;
919  }
920 // }}}
921 
922 // {{{ putElectronicCollection (Update Electronic Collection)
935  function putElectronicCollection($collection_id,$collection) {
936  $ret = $this->put('/almaws/v1/electronic/e-collections/{collection_id}',
937  array('collection_id' => $collection_id),
938  array(),
939  $collection
940  );
941  $this->checkForErrorMessage($ret);
942  return $ret;
943  }
944 // }}}
945 
946 // {{{ getElectronicServices (Retrieve Electronic Services)
959  function getElectronicServices($collection_id) {
960  $ret = $this->get('/almaws/v1/electronic/e-collections/{collection_id}/e-services',
961  array('collection_id' => $collection_id),
962  array()
963  );
964  $this->checkForErrorMessage($ret);
965  return $ret;
966  }
967 // }}}
968 
970 //}}}
971 
972 //{{{Library APIs
976 // {{{ grima -> getLibrary (Retrieve a Library)
988  function getLibrary($libraryCode) {
989  $ret = $this->get('/almaws/v1/conf/libraries/{libraryCode}',
990  array('libraryCode' => $libraryCode),
991  array()
992  );
993  $this->checkForErrorMessage($ret);
994  return $ret;
995  }
996 // }}}
997 
998 // {{{ grima -> getAllLibraries (Retrieve Libraries)
1009  function getAllLibraries() {
1010  $ret = $this->get('/almaws/v1/conf/libraries',
1011  array(),
1012  array()
1013  );
1014  $this->checkForErrorMessage($ret);
1015  return $ret;
1016  }
1017 // }}}
1018 
1019 // {{{ getAllLocations (Retrieve Locations)
1030  function getAllLocations($libraryCode) {
1031  $ret = $this->get('/almaws/v1/conf/libraries/{libraryCode}/locations',
1032  array('libraryCode' => $libraryCode),
1033  array()
1034  );
1035  $this->checkForErrorMessage($ret);
1036  return $ret;
1037  }
1038 // }}}
1039 
1041 //}}}
1042 
1043 //{{{Location APIs
1047 // {{{ getLocation (Retrieve Location)
1059  function getLocation($libraryCode,$locationCode) {
1060  $ret = $this->get('/almaws/v1/conf/libraries/{libraryCode}/locations/{locationCode}',
1061  array(
1062  'libraryCode' => $libraryCode,
1063  'locationCode' => $locationCode,
1064  ),
1065  array()
1066  );
1067  $this->checkForErrorMessage($ret);
1068  return $ret;
1069  }
1070 // }}}
1071 
1073 //}}}
1074 
1075 //{{{Set APIs
1079 // {{{ grima -> getSet (Retrieve a Set)
1091  function getSet($set_id) {
1092  $ret = $this->get('/almaws/v1/conf/sets/{set_id}',
1093  array('set_id' => $set_id),
1094  array()
1095  );
1096  $this->checkForErrorMessage($ret);
1097  return $ret;
1098  }
1099 // }}}
1100 
1101 // {{{ grima -> postSet (Create a Set)
1112  function postSet($set) {
1113  $ret = $this->post('/almaws/v1/conf/sets/',
1114  array(),
1115  array(),
1116  $set
1117  );
1118  $this->checkForErrorMessage($ret);
1119  return $ret;
1120  }
1121 // }}}
1122 
1123 // {{{ grima -> postSetManageMembers (Manage Members)
1138  function postSetManageMembers($set_id,$id_type,$op,$set) {
1139  $ret = $this->post('/almaws/v1/conf/sets/{set_id}',
1140  array('set_id' => $set_id),
1141  array('id_type' => $id_type, 'op' => $op),
1142  $set
1143  );
1144  $this->checkForErrorMessage($ret);
1145  return $ret;
1146  }
1147 
1148 // {{{ Set -> createSetFromImport (Create a Set)
1161  function createSetFromImport($job_instance_id, $population) {
1162  # create blank set
1163 
1164  $body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?' . '>
1165 <set>
1166  <name>Grima set from ' . $job_instance_id . '</name>
1167  <description>members of ' . $job_instance_id . '</description>
1168  <type desc="Itemized">ITEMIZED</type>
1169  <content desc="All Titles">BIB_MMS</content>
1170  <private desc="No">false</private>
1171 </set>';
1172 
1173 /*
1174  Content:
1175  BIB_MMS -- all titles
1176  ITEM
1177  PORTFOLIO
1178  IEPA
1179  FILE
1180  AUTHORITY_MMS
1181  IEP
1182  IEE
1183  IED
1184  IEC
1185 */
1186 
1187 /*
1188  Population:
1189 */
1190 
1191  $bodyxml = new DomDocument();
1192  $bodyxml->loadXML($body);
1193 
1194  $ret = $this->post('/almaws/v1/conf/sets', array(), array('job_instance_id' => $job_instance_id, 'population' => $population),$bodyxml);
1195  $this->checkForErrorMessage($ret);
1196  return $ret;
1197 
1198  }
1199 // }}}
1200 
1201 // {{{ grima -> deleteSet (Delete a Set)
1212  function deleteSet($set_id) {
1213  $ret = $this->delete('/almaws/v1/conf/sets/{set_id}',
1214  array(
1215  'set_id' => $set_id,
1216  ),
1217  array()
1218  );
1219  $this->checkForErrorMessage($ret);
1220  }
1221 // }}}
1222 
1223 // {{{ grima -> getSetMembers (Does it work????)
1236  function getSetMembers($set_id,$limit = 10,$offset = 0) {
1237  $ret = $this->get('/almaws/v1/conf/sets/{set_id}/members',
1238  array('set_id' => $set_id),
1239  array('limit' => $limit, 'offset' => $offset)
1240  );
1241  $this->checkForErrorMessage($ret);
1242  return $ret;
1243  }
1244 // }}}
1245 
1247 //}}}
1248 
1249 //{{{Analytics APIs
1253  # XXX check if blank filter is ok
1254  function getAnalytics($path,$filter,$limit=25,$token=null) {
1255  return $this->get('/almaws/v1/analytics/reports',
1256  array(),
1257  array('path' => urlencode($path), 'filter' => urlencode($filter),
1258  'limit' => $limit, 'token' => $token)
1259  );
1260  }
1261 
1263 //}}}
1264 
1265 //{{{Job APIs
1269 // {{{ postJob (Submit a manual or scheduled job)
1283  function postJob($job_id, $op, $job) {
1284  $ret = $this->post('/almaws/v1/conf/jobs/{job_id}',
1285  array('job_id' => $job_id),
1286  array('op' => $op),
1287  $job
1288  );
1289  $this->checkForErrorMessage($ret);
1290  return $ret;
1291  }
1292 // }}}
1293 
1295 //}}}
1296 
1297 
1298 }
1299 
1300 // }}}
1301 
1302 // {{{ class GrimaTask
1306 abstract class GrimaTask implements ArrayAccess {
1307 
1308  public $error = false;
1309  public $args = array();
1310  public $el_override = array();
1311 
1312  public $auto_args = array();
1313 
1314  public $messages = array();
1315 
1316  function offsetExists($offset) {
1317  return isset($this->args[$offset]);
1318  }
1319 
1320  function offsetGet($offset) {
1321  return $this->args[$offset];
1322  }
1323 
1324  function offsetSet($offset,$value) {
1325  $this->args[$offset] = $value;
1326  }
1327 
1328  function offsetUnset($offset) {
1329  unset($this->args[$offset]);
1330  }
1331 
1332  function __construct() {
1333  $webroot = __DIR__;
1334  $base = get_class($this); //basename($_SERVER['PHP_SELF'],'.php');
1335  if (file_exists(join_paths($webroot,$base,"$base.xml")) and (!isset($this->formxml))) {
1336  $this->formxml = file_get_contents(join_paths($webroot,$base,"$base.xml"));
1337  }
1338  if (isset($this->formxml)) {
1339  $this->form = new GrimaForm();
1340  $this->form->fromXML($this->formxml);
1341  }
1342  }
1343 
1344  function setup_splat() {
1345  $webroot = __DIR__;
1346  $basename = get_class($this); //basename($_SERVER['PHP_SELF'],'.php');
1347 
1348  $this->splat = new zemowsplat\Splat();
1349 
1350  $this->splat->addBases(array(
1351  "$webroot/$basename/splats", // per-task overrides
1352  "$webroot/splats", // default
1353  ));
1354 
1355  $this->splatVars = array(
1356  'title' => $this->form->title,
1357  'basename' => $basename,
1358  'webroot' => $webroot,
1359  'local_stylesheets' => array('Grima.css'),
1360  'form' => &$this->form,
1361  'messages' => &$this->messages,
1362  'width' => 9,
1363  );
1364  if (isset($this['redirect_url'])) {
1365  $this->splatVars['redirect_url'] = $this['redirect_url'];
1366  }
1367  }
1368 
1369  function print_form() {
1370  $this->form->loadValues($this);
1371  $this->splat->splat('print_form', $this->splatVars );
1372  }
1373 
1374  function print_success() {
1375  $this->form->loadPersistentValues($this);
1376  $this->splat->splat('print_success', $this->splatVars );
1377  }
1378 
1379  function print_failure() {
1380  $this->form->loadValues($this);
1381  $this->splat->splat('print_failure', $this->splatVars );
1382  }
1383 
1384  function check_login() {
1385  global $grima;
1386  if (isset($grima->apikey) and isset($grima->server) and
1387  ($grima->apikey) and ($grima->server)) {
1388  return true;
1389  } else {
1390  GrimaTask::call('Login',array("redirect_url"=>$_SERVER['PHP_SELF']));
1391  exit;
1392  return false;
1393  }
1394  }
1395 
1396  public function addMessage($type,$message) {
1397  $this->messages[] = new GrimaTaskMessage($type,$message);
1398  }
1399 
1400  public function run() {
1401  $this->check_login(); # if not logged in, print login form
1402  $this->error = false;
1403  $this->get_input();
1404  $this->setup_splat(); # should happen after form xml read in get_input
1405  if ($this->check_input()) {
1406  try {
1407  $this->do_task();
1408  } catch (Exception $e) {
1409  $this->addMessage('error',$e->getMessage());
1410  $this->error = true;
1411  }
1412  } else {
1413  $this->print_form();
1414  exit;
1415  }
1416  if ($this->error) {
1417  $this->print_failure();
1418  } else {
1419  $this->print_success();
1420  }
1421  }
1422 
1423  public static function RunIt() {
1424  $task = new static();
1425  $task->run();
1426  }
1427 
1428  function get_input() {
1429  if (isset($this->form)) {
1430  if (php_sapi_name() == "cli") { # command line
1431  /*
1432  if ($options = getopt(implode(array_keys($param)))) {
1433  foreach ($param as $k => $v) {
1434  $this->args[$v] = $options[$k[0]];
1435  }
1436  if (!$this->check_input()) {
1437  $this->usage(); exit;
1438  }
1439  } else {
1440  $this->usage(); exit;
1441  }
1442  */
1443  } else { # web
1444  foreach ($this->form->fields as $field) {
1445  if (isset($_REQUEST[$field->name])) {
1446  $this[$field->name] = $_REQUEST[$field->name];
1447  /* sanitize */
1448  }
1449  }
1450  }
1451 
1452  } else {
1453  $this->get_input_param($this->auto_args);
1454  }
1455  }
1456 
1457  function check_input() {
1458  if (isset($this->form)) {
1459  $input_good = true;
1460  foreach ($this->form->fields as $field) {
1461  if ($field->required) {
1462  if (!isset($this[$field->name]) or
1463  !($this[$field->name])) {
1464  $field->error_condition = "error";
1465  $field->error_message = "Field is required\n";
1466  $input_good = false;
1467  }
1468  }
1469  }
1470  return $input_good;
1471  } else {
1472 
1473  foreach ($this->auto_args as $k => $v) {
1474  if (preg_match('/[^:]:$/',$k)) {
1475  if (!isset($this->args[$v]) or !($this->args[$v])) {
1476  return false;
1477  }
1478  }
1479  }
1480  return true;
1481  }
1482  }
1483 
1484  function get_input_param($param) {
1485  if (php_sapi_name() == "cli") { # command line
1486  if ($options = getopt(implode(array_keys($param)))) {
1487  foreach ($param as $k => $v) {
1488  $this->args[$v] = $options[$k[0]];
1489  }
1490  if (!$this->check_input()) {
1491  $this->usage(); exit;
1492  }
1493  } else {
1494  $this->usage(); exit;
1495  }
1496  } else { # web
1497  foreach ($param as $k => $v) {
1498  if (isset($_REQUEST[$v])) {
1499  $this->args[$v] = $_REQUEST[$v];
1500  }
1501  }
1502  if (!$this->check_input()) {
1503  $this->print_form(); exit;
1504  }
1505  }
1506  }
1507 
1508  abstract function do_task();
1509 
1510  function usage() { # XXX rewrite for grima form
1511  global $argv;
1512  print "Usage: php ${argv[0]} ";
1513  foreach ($this->auto_args as $k => $v) {
1514  if (preg_match('/^(.):$/',$k,$m)) {
1515  print "-${m[1]} <$v> ";
1516  } else {
1517  if (preg_match('/^(.)::$/',$k,$m)) {
1518  print "[ -${m[1]} <$v> ] ";
1519  } else {
1520  if (preg_match('/^.$/',$k)) {
1521  print "[ -$k ] ";
1522  }
1523  }
1524  }
1525  }
1526  print "\n";
1527  exit;
1528  }
1529 
1530  public static function call($grimaname,$args = array()) {
1531  $url = rtrim("../$grimaname/$grimaname.php?" . http_build_query($args),"?");
1532  do_redirect($url);
1533  }
1534 
1535 }
1536 
1537 // }}}
1538 
1539 // {{{ class GrimaTaskMessage
1544  public $type;
1545  /* bootstrap type: debug, info, success, warning, error */
1546  public $message;
1547 
1548  function __construct($type,$message) {
1549  $this->type = $type;
1550  $this->message = $message;
1551  }
1552 }
1553 
1554 // }}}
1555 
1556 //{{{ class GrimaForm
1560 class GrimaForm {
1561  public $fields = array();
1562  public $title;
1563  protected $action;
1564 
1565 // {{{ loadValues
1571  function loadValues($obj) {
1572  foreach ($this->fields as $field) {
1573  if (isset($obj[$field->name])) {
1574  $field->value = $obj[$field->name];
1575  }
1576  }
1577  }
1578 // }}}
1579 
1580 // {{{ loadPersistentValues
1586  function loadPersistentValues($obj) {
1587  foreach ($this->fields as $field) {
1588  if (($field->persistent) and isset($obj[$field->name])) {
1589  $field->value = $obj[$field->name];
1590  }
1591  }
1592  }
1593 // }}}
1594 
1595 // {{{ fromXML
1600  function fromXML($xml) {
1601  $doc = new DomDocument();
1602  $doc->loadXML($xml);
1603  $xpath = new DomXpath($doc);
1604  $this->title = $xpath->query('//Title')[0]->nodeValue;
1605  $this->action = basename($_SERVER['PHP_SELF']); # allow set?
1606 
1607  $nodes = $xpath->query('//Field');
1608  foreach ($nodes as $node) {
1609  $this->fields[$node->getAttribute('name')] = new GrimaFormField($node);
1610  }
1611  }
1612 // }}}
1613 
1614 }
1615 
1616 // }}}
1617 
1618 //{{{ class GrimaDataStore
1619 
1620 $ds_barcode = 'GRIMABYZEMKAT';
1621 
1625 class GrimaDataStore implements ArrayAccess {
1626 
1627  public $marc;
1628  public $bib;
1629  public $holding;
1630  public $item;
1631 
1632 // {{{ __construction
1637  function __construct() {
1638  global $ds_barcode;
1639  $this->item = new Item();
1640  try {
1641  $this->item->loadFromAlmaBarcode($ds_barcode);
1642  } catch (Exception $e) {
1643  $this->setup();
1644  }
1645  $this->bib = new Bib();
1646  $this->bib->loadFromAlma($this->item['mms_id']);
1647  $this->bib->useNormac();
1648  }
1649 // }}}
1650 
1651 // {{{ array access functions
1652  function offsetExists($offset) {
1653  $fields = $this->bib->marc->getFields('ZEM');
1654  foreach($fields as $field) {
1655  $keys = $field->getSubfields('k');
1656  if ($keys[0]->data == $offset) {
1657  return true;
1658  }
1659  }
1660  return false;
1661  }
1662 
1663  function offsetGet($offset) {
1664  $fields = $this->bib->marc->getFields('ZEM');
1665  foreach($fields as $field) {
1666  $keys = $field->getSubfields('k');
1667  if ($keys[0]->data == $offset) {
1668  $values = $field->getSubfields('v');
1669  return $values[0]->data;
1670  }
1671  }
1672  return false;
1673  }
1674 
1675  function offsetSet($offset,$value) {
1676  $fields = $this->bib->marc->getFields('ZEM');
1677  foreach($fields as $field) {
1678  $keys = $field->getSubfields('k');
1679  if ($keys[0]->data == $offset) {
1680  $values = $field->getSubfields('v');
1681  $values[0]->data = $value;
1682  $this->bib->updateAlma();
1683  return true;
1684  }
1685  }
1686  $subarray = array(
1687  new ISO2709Subfield('k',$offset),
1688  new ISO2709Subfield('v',$value),
1689  );
1690  $field = new ISO2709Field('ZEM',null," ",$subarray);
1691  $this->bib->marc->appendField($field);
1692  $this->bib->updateAlma();
1693  return false;
1694  }
1695 
1696  function offsetUnset($offset) {
1697  $fields = $this->bib->marc->getFields('ZEM');
1698  foreach($fields as $field) {
1699  $keys = $field->getSubfields('k');
1700  if ($keys[0]->data == $offset) {
1701  $field->delete();
1702  $this->bib->updateAlma();
1703  return true;
1704  }
1705  }
1706  return false;
1707  }
1708 // }}}
1709 
1710 // {{{ setup
1715  function setup() {
1716  global $ds_barcode;
1717  $bib = new Bib();
1718  $bib['title'] = "GRIMA'S DATA STORE"; # XXX indicators 00?
1719  $bib['suppress_from_publishing'] = "true";
1720  $bib->addToAlma();
1721  $this->bib = $bib;
1722  $this->bib->useNormac();
1723 
1724  $library = Library::getOneLibrary();
1725  $location = $library->getOneLocation();
1726 
1727  $holding = new Holding();
1728  $holding['library_code'] = $library['code'];
1729  $holding['location_code'] = $location['code'];
1730  $holding->addToAlmaBib($bib['mms_id']);
1731  $this->holding = $holding;
1732 
1733  $item = new Item();
1734  $item['barcode'] = $ds_barcode;
1735  $item->addToAlmaHolding($bib['mms_id'],$holding['holding_id']);
1736  $this->item = $item;
1737 
1738  preg_match('/\d{5}$/',$bib['mms_id'],$m);
1739  $this['alma institution code'] = $m[0];
1740  }
1741 // }}}
1742 
1743 // {{{ exists
1748  public static function exists() {
1749  global $ds_barcode;
1750  $item = new Item();
1751  try {
1752  $item->loadFromAlmaBarcode($ds_barcode);
1753  return true;
1754  } catch (Exception $e) {
1755  return false;
1756  }
1757  }
1758 
1759 // }}}
1760 
1761 // {{{ destroy
1766  public function destroy() {
1767  global $ds_barcode;
1768  $item = new Item();
1769  $item->loadFromAlmaBarcode($ds_barcode);
1770  $bib = new Bib();
1771  $bib->loadFromAlma($item['mms_id']);
1772  $bib->deleteTreeFromAlma();
1773  }
1774 
1775 // }}}
1776 
1777 }
1778 
1779 // }}}
1780 
1781 //{{{ class GrimaFormField
1782 
1787 
1788  public $value;
1789 
1790  public $name;
1791  public $label;
1793  public $required;
1794  public $persistent;
1795  public $visible;
1796  public $rows;
1797  public $dataset;
1798  protected $autocomplete;
1799  protected $highlight;
1800  public $error_condition = ""; /* can be warning or error */
1801  public $error_message = "";
1802 
1803 // {{{ booly - is it true or false
1811  function booly($str, $default = 'undefined') {
1812  switch(strtolower($str)) {
1813  case 'true':
1814  case 't':
1815  case 'on':
1816  case 'yes':
1817  case '1':
1818  return true;
1819  case 'false':
1820  case 'f':
1821  case 'off':
1822  case 'no':
1823  case '0':
1824  return false;
1825  default:
1826  return $default;
1827  }
1828  }
1829 // }}}
1830 
1831 // {{{ __construct
1837  function __construct($field) {
1838  $this->name = $field->getAttribute('name');
1839  $this->label = $field->getAttribute('label');
1840  $this->placeholder = $field->getAttribute('placeholder');
1841  $this->rows = $field->getAttribute('rows');
1842  $this->type = $field->getAttribute('type');
1843  if (!$this->type) {
1844  $this->type = 'input';
1845  }
1846  $this->required = $this->booly($field->getAttribute('required'),true);
1847  $this->persistent = $this->booly($field->getAttribute('persistent'),false);
1848  $this->autocomplete = $this->booly($field->getAttribute('autocomplete'),false);
1849  $this->visible = $this->booly($field->getAttribute('visible'),true);
1850  $this->dataset = $field->getAttribute('dataset');
1851  $this->options = array();
1852  if ($this->dataset) {
1853  if ($this->dataset == "institutions") {
1854  $institutions = GrimaDB::getInstitutions();
1855  $currentUser = GrimaUser::GetCurrentUser();
1856  foreach ($institutions as $institution) {
1857  $i = htmlspecialchars($institution);
1858  $s = ($institution === $currentUser['institution']) ? ' selected="selected"' : '';
1859  $this->options[] = "<option value=\"$i\"$s>$i</option>";
1860  }
1861  }
1862  } else {
1863  foreach ($field->getElementsByTagName("option") as $option) {
1864  $this->options[] = $option->ownerDocument->saveXML( $option );
1865  }
1866  }
1867  }
1868 // }}}
1869 
1870 }
1871 
1872 // }}}
1873 
1874 //{{{ class AlmaObject
1878 class AlmaObject implements ArrayAccess {
1879  public $el_access = array();
1880  public $xml;
1881  public $templateDir = __DIR__ . "/templates";
1882 
1883 // {{{ __construct
1887  function __construct() {
1888  $this->xml = new DomDocument();
1889  $blankRecord = get_class($this);
1890  $this->xml->loadXML(file_get_contents("{$this->templateDir}/{$blankRecord}.xml"));
1891  }
1892 // }}}
1893 
1894  function offsetExists($offset) {
1895  if (isset($this->el_override)) {
1896  return array_key_exists($offset, $this->el_override);
1897  }
1898  return array_key_exists($offset, $this->el_access);
1899  }
1900 
1901  function offsetGet($offset) {
1902  if ((isset($this->el_override)) and
1903  (isset($this->el_override[$offset]))) {
1904  return $this->el_override[$offset];
1905  }
1906  $xpath = new DomXpath($this->xml);
1907  $node = $xpath->query($this->el_address[$offset]);
1908  if (sizeof($node) >= 1) {
1909  return $node[0]->nodeValue;
1910  }
1911  return null;
1912  }
1913 
1914  # XXX el_override ?
1915  function offsetSet($offset, $value) {
1916  $xpath = new DomXpath($this->xml);
1917  $node = $xpath->query($this->el_address[$offset]);
1918  $node[0]->nodeValue = $value;
1919  }
1920 
1921  function offsetUnset($offset) {
1922  $xpath = new DomXpath($this->xml);
1923  $node = $xpath->query($this->el_address[$offset]);
1924  $node[0]->nodeValue = null;
1925  }
1926 
1927 }
1928 
1929 // }}}
1930 
1931 // {{{ class AlmaObjectWithMARC
1936 
1937  public $marc; # Normac MARC21Record
1938 
1939  function useNormac() {
1940  if (isset($this->marc)) { return; }
1941  $this->marc = new MARC21Record();
1942  #error_log("LOADING:");
1943  #error_log($this->xml->saveXML());
1944  $this->marc->loadFromString($this->xml->saveXML());
1945  }
1946 
1947 // {{{ AlmaObjectWithMARC -> addControlField
1954  function addControlField($tag,$data) {
1955  $this->useNormac(); # this function uses normac
1956  $subarray = array();
1957  $field = new ISO2709Field($tag, data);
1958  $this->marc->AppendField($field,true);
1959  }
1960 // }}}
1961 
1962 // {{{ AlmaObjectWithMARC -> addDataField
1970  function addDataField($tag,$indicators,$subfields) {
1971  $this->useNormac(); # this function uses normac
1972  $subarray = array();
1973  foreach ($subfields as $k => $v) {
1974  $subarray[] = new ISO2709Subfield($k,$v);
1975  }
1976  $field = new ISO2709Field($tag, null, $indicators, $subarray);
1977  $this->marc->AppendField($field,true);
1978  }
1979 // }}}
1980 
1981 // {{{ AlmaObjectWithMARC -> normacToXML
1985  function normacToXML() {
1986  if (isset($this->marc)) {
1987  $newRecord = new DOMDocument;
1988  $newRecord->loadXML($this->marc->asXMLString());
1989 
1990  $xpath = new DOMXpath($this->xml);
1991  $record = $xpath->query('//record');
1992  $node = $record[0];
1993  $node->parentNode->removeChild($node);
1994 
1995  $node = $this->xml->importNode($newRecord->documentElement, true);
1996  $this->xml->documentElement->appendChild($node);
1997  }
1998  }
1999 
2000 // }}}
2001 
2002 // {{{ AlmaObjectWithMARC -> appendField
2011  function appendField($tag,$ind1,$ind2,$subfields) {
2012  $frag = "<datafield ind1=\"$ind1\" ind2=\"$ind2\" tag=\"$tag\">";
2013  foreach ($subfields as $k => $v) {
2014  $frag .= "<subfield code=\"$k\">$v</subfield>";
2015  }
2016  $frag .= "</datafield>";
2017  $xpath = new DomXpath($this->xml);
2018  $record = $xpath->query("//record");
2019  appendInnerXML($record[0],$frag);
2020  }
2021 // }}}
2022 
2023 // {{{ AlmaObjectWithMARC -> getFields
2030  function getFields($tag) {
2031  $this->useNormac(); # this function uses normac
2032  return $this->marc->getFields($tag);
2033  }
2034 // }}}
2035 
2036 // {{{ AlmaObjectWithMARC -> getMarcFields # XXX IN PROGRESS
2043  function getMarcFields($tag) {
2044  $xpath = new DomXpath($this->xml);
2045  $tag = preg_replace('/X*$/','',$tag);
2046  $tag = preg_replace('/\.*$/','',$tag);
2047  $fields = $xpath->query("//record/datafield[starts-with(@tag,'$tag')]");
2048  $fieldarr = array();
2049  foreach ($fields as $field) {
2050  $marcfield = new MARCField();
2051  # indicators?
2052  $subfieldarr = array();
2053  foreach ($field->childNodes as $child) {
2054  $subfieldarr[] = array(
2055  $child->attributes[0]->value,
2056  $child->nodeValue
2057  );
2058  }
2059  $fieldarr[] = $subfieldarr;
2060  }
2061  return $fieldarr;
2062  }
2063 // }}}
2064 
2065 // {{{ AlmaObjectWithMARC -> getSubfieldValues
2073  function getSubfieldValues($tag,$code) {
2074  $xpath = new DomXpath($this->xml);
2075  $subfields = $xpath->query("//record/datafield[@tag='$tag']/subfield[@code='$code']");
2076  $arr = array();
2077  foreach ($subfields as $subfield) {
2078  $arr[] = $subfield->nodeValue;
2079  }
2080  return $arr;
2081  }
2082 // }}}
2083 
2084 // {{{ AlmaObjectWithMARC -> deleteField
2090  function deleteField($tag) {
2091  $xpath = new DomXpath($this->xml);
2092  $fields = $xpath->query("//record/datafield[@tag='$tag']");
2093  foreach( $fields as $field ) {
2094  $field->parentNode->removeChild( $field );
2095  }
2096  }
2097 // }}}
2098 
2099 // {{{ AlmaObjectWithMARC -> deleteSubfieldMatching
2105  function deleteSubfieldMatching($tag,$code,$regex) {
2106  $xpath = new DomXPath($this->xml);
2107  $subfs = $xpath->query("//datafield[@tag='$tag']/subfield[@code='$code']");
2108  foreach ($subfs as $subf) {
2109  if (preg_match($regex,$subf->nodeValue)) {
2110  $subf->parentNode->removeChild($subf);
2111  }
2112  }
2113  }
2114 // }}}
2115 
2116 // {{{ AlmaObjectWithMARC -> replaceOrAddSubfield
2124  function replaceOrAddSubfield($tag,$code,$value) {
2125  # very shady but sometimes needed
2126  $xpath = new DomXpath($this->xml);
2127  $fields = $xpath->query("//record/datafield[@tag='$tag']");
2128  if (sizeof($fields) == 0) {
2129  $this->appendField($tag,' ',' ',array($code => $value));
2130  } else {
2131  $done = false;
2132  foreach ($fields[0]->childNodes as $subfield) {
2133  if($subfield->nodeType !== 1) {
2134  continue;
2135  }
2136  if ($subfield->getAttribute("code") == $code) {
2137  $subfield->nodeValue = $value;
2138  $done = true;
2139  break;
2140  }
2141  }
2142  if (!$done) {
2143  $subfield = $this->xml->createElement("subfield");
2144  $subfield->setAttribute("code",$code);
2145  $subfield->appendChild($this->xml->createTextNode($value));
2146  $fields[0]->appendChild($subfield);
2147  }
2148  }
2149  }
2150 // }}}
2151 
2152 }
2153 // }}}
2154 
2155 // {{{ class Bib
2159 class Bib extends AlmaObjectWithMARC {
2160  public $holdingsList; # HoldingsList object
2161  public $holdings = array();
2162 
2163  public $itemList;
2164  public $items = array();
2165 
2166  public $portfolioList = array(); # just an array for now
2167 
2168  /* public $networknums = array(); */
2169 
2170  protected $el_address = array(
2171  'mms_id' => '//mms_id',
2172  'leader' => '//leader',
2173  'record_format' => '//record_format',
2174  'title' => '//title',
2175  'author' => '//author',
2176  'place_of_publication' => '//place_of_publication',
2177  'publisher_const' => '//publisher_const',
2178  'publisher' => '//publisher_const',
2179  'suppress_from_publishing' => '//suppress_from_publishing',
2180  );
2181 
2182  function offsetGet($offset) {
2183  if ($offset == 'Type') {
2184  $leader = $this['leader'];
2185  return $leader[6];
2186  }
2187  if ($offset == 'BLvl') {
2188  $leader = $this['leader'];
2189  return $leader[7];
2190  }
2191  if ($offset == 'ELvl') {
2192  $leader = $this['leader'];
2193  return $leader[17];
2194  }
2195  if ($offset == 'Desc') {
2196  $leader = $this['leader'];
2197  return $leader[18];
2198  }
2199  return parent::offsetGet($offset);
2200  }
2201 
2202  # override because these go multiple places
2203  function offsetSet($offset,$value) {
2204  parent::offsetSet($offset,$value);
2205  if ($offset == 'author') {
2206  $this->replaceOrAddSubfield('100','a',$value);
2207  }
2208  if ($offset == 'title') {
2209  $this->replaceOrAddSubfield('245','a',$value);
2210  }
2211  if (($offset == 'publisher_const') or ($offset == 'publisher')) {
2212  $this->replaceOrAddSubfield('264','b',$value);
2213  }
2214  if ($offset == 'place_of_publication') {
2215  $this->replaceOrAddSubfield('264','a',$value);
2216  }
2217  }
2218 
2219 // {{{ Bib -> loadFromAlma (get) - gets Bib from Alma
2225  function loadFromAlma($mms_id) {
2226  global $grima;
2227  $this->xml = $grima->getBib($mms_id);
2228  }
2229 // }}}
2230 
2231 // {{{ Bib -> addToAlma (post) - adds the Bib to Alma
2236  function addToAlma() {
2237  global $grima;
2238  $this->xml = $grima->postBib($this->xml);
2239  }
2240 // }}}
2241 
2242 // {{{ Bib -> updateAlma (put) - replaces the Bib in Alma
2246  function updateAlma() {
2247  global $grima;
2248  # XXX parent here?
2249  $this->normacToXML();
2250  $this->xml = $grima->putBib($this['mms_id'],$this->xml);
2251  }
2252 // }}}
2253 
2254 // {{{ Bib -> deleteFromAlma (delete) - deletes the Bib from Alma
2258  function deleteFromAlma() {
2259  global $grima;
2260  $grima->deleteBib($this['mms_id']);
2261  }
2262 // }}}
2263 
2264 // {{{ Bib -> deleteTreeFromAlma (delete) - deletes Bib and inventory from Alma
2268  function deleteTreeFromAlma() {
2269  global $grima;
2270  $this->getHoldings();
2271  foreach ($this->holdings as $holding) {
2272  $holding->deleteTreeFromAlma(); #XXX
2273  }
2274  $this->deleteAllPortfolios($this['mms_id']); #XXX
2275  $grima->deleteBib($this['mms_id']);
2276  }
2277 // }}}
2278 
2279 // {{{ Bib -> hasInventory - does the bib have holdings or portfolios in Alma?
2283  function hasInventory() {
2284  $this->getHoldingsList();
2285  if (count($this->holdingsList->entries) > 0) {
2286  return true;
2287  }
2288  $this->getPortfolioList();
2289  if (count($this->portfolioList) > 0) {
2290  print_r($this->portfolioList);
2291  return true;
2292  } else {
2293  return false;
2294  }
2295  }
2296 // }}}
2297 
2298 // {{{ Bib -> linkedToCZ - is the bib linked to community zone?
2302  function linkedToCZ() {
2303  $xpath = new DomXPath($this->xml);
2304  $nodes = $xpath->query("//linked_record_id[@type='CZ']");
2305  return (count($nodes) > 0);
2306  }
2307 // }}}
2308 
2309 // {{{ Bib -> unlinkFromCZ - does this work
2314  function unlinkFromCZ() {
2315  $xpath = new DomXPath($this->xml);
2316  $nodes = $xpath->query("//linked_record_id[@type='CZ']");
2317  foreach ($nodes as $node) {
2318  $node->parentNode->removeChild($node);
2319  }
2320  $this->updateAlma();
2321  }
2322 // }}}
2323 
2324 // {{{ Bib -> getHoldings - get holdings objects
2328  function getHoldings() {
2329  $this->getHoldingsList();
2330  foreach ($this->holdingsList->entries as $entry) {
2331  $holding = new Holding();
2332  $holding->loadFromAlma($this['mms_id'],$entry['holding_id']);
2333  $holding['mms_id'] = $this['mms_id'];
2334  $this->holdings[] = $holding;
2335  }
2336  return $this->holdings;
2337  }
2338 // }}}
2339 
2340 // {{{ Bib -> deleteAllPortfolios - delete all portfolios from the bib
2344  function deleteAllPortfolios() {
2345  global $grima;
2346  while ($this->getPortfolioList()) {
2347  foreach($this->portfolioList as $portfolio) {
2348  error_log("deleting " . $portfolio['pid'] . "\n");
2349  $portfolio->deleteFromAlma();
2350  }
2351  $this->portfolioList = array();
2352  sleep(3);
2353  }
2354  }
2355 // }}}
2356 
2357 // {{{ Bib -> getHoldingsList
2361  function getHoldingsList() {
2362  $this->holdingsList = new HoldingsList($this['mms_id']);
2363  }
2364 // }}}
2365 
2366 // {{{ Bib -> getPortfolioList
2370  function getPortfolioList() { # maybe rename
2371  global $grima;
2372  $limit = 10; $offset = 0; # where to allow passing
2373  $ret = $grima->getElectronicPortfoliosForBib($this['mms_id'],$limit,$offset);
2374  $xpath = new DOMXpath($ret);
2375  $ports = $xpath->query('//portfolio');
2376  foreach ($ports as $portnode) {
2377  $newport = new ElectronicPortfolio();
2378  $newport->loadFromPortfolioListNode($portnode);
2379  $this->portfolioList[] = $newport;
2380  }
2381  return count($ports); # XXX is this the right thing to return?
2382  }
2383 // }}}
2384 
2385 // {{{ Bib -> getPortfolios
2389  function getPortfolios() {
2390  $this->getPortfolioList();
2391  foreach($this->portfolioList as $port) {
2392  $port->loadFromAlma($port['portfolio_id']);
2393  }
2394  return $this->portfolioList;
2395  }
2396 // }}}
2397 
2398 // {{{ getItems - get items objects
2402  function getItems() {
2403  $this->getItemList();
2404  $this->items =& $this->itemList->items;
2405  return $this->items;
2406  }
2407 // }}}
2408 
2409 // {{{ getItemList - populates itemList property from Alma
2413  function getItemList() { # XXX
2414  global $grima;
2415  $this->itemList = new ItemList($this['mms_id'],'ALL');
2416  }
2417 // }}}
2418 
2419 // {{{ Bib -> get_title_proper
2423  function get_title_proper() {
2424  $xpath = new DomXpath($this->xml);
2425  $title = $xpath->query("//record/datafield[@tag='245']/subfield[@code='a']");
2426  return preg_replace("/[ \/=:,;\.]*$/","",$title[0]->nodeValue);
2427  }
2428 // }}}
2429 
2430  /*
2431  function get_networkNumbers() {
2432  $xpath = new DomXpath($this->xml);
2433  $netNums = $xpath->query("//bib/network_numbers/network_number");
2434  $ret = array();
2435  for ($j=0;$j<$netNums->length;$j++) {
2436  $ret[] = $netNums[$j]->nodeValue;
2437  }
2438  return $ret;
2439  }
2440  */
2441 
2442 // {{{ Bib -> getLCCallNumber #XXX
2448  function getLCCallNumber() {
2449  $xpath = new DomXPath($this->xml);
2450  $calls = array();
2451  foreach (array('050','090') as $tag) {
2452  foreach ($xpath->query("//datafield[@tag='$tag']") as $call) {
2453  $calls[] = $call;
2454  }
2455  }
2456  $ret = array();
2457 
2458  foreach ($calls as $node) {
2459  $classs = $xpath->query("subfield[@code='a']",$node);
2460  $items = $xpath->query("subfield[@code='b']",$node);
2461  if ((count($classs) > 0) and (count($items) > 0)) {
2462  $ret = array($classs[0]->nodeValue,$items[0]->nodeValue);
2463  }
2464  }
2465  return $ret;
2466  }
2467 // }}}
2468 
2469 }
2470 
2471 // }}}
2472 
2473 // {{{ class HoldingsList
2475 class HoldingsList extends AlmaObject {
2476  public $el_address = array(
2477  'mms_id' => '//mms_id',
2478  'title' => '//title',
2479  'author' => '//author',
2480  );
2481  public $xml;
2482  #public $holdings = array(); # should this go in BIB?
2483  public $entries = array(); # array of HoldingsListEntry
2484 
2485  function __construct($mms_id = null) {
2486  if (!is_null($mms_id)) {
2487  $this->loadFromAlma($mms_id);
2488  }
2489  }
2490 
2491  function loadFromAlma($mms_id) {
2492  global $grima;
2493  $this->xml = $grima->getHoldingsList($mms_id);
2494  $xpath = new DomXpath($this->xml);
2495  $hs = $xpath->query('//holding');
2496  $this->entries = array(); # clear
2497  #$this->holdings = array(); # clear
2498  foreach ($hs as $h) {
2499  #$this->holdings[] = new HoldingsListEntry($h,$mms_id);
2500  $this->entries[] = new HoldingsListEntry($h,$mms_id);
2501  }
2502  }
2503 }
2504 
2505 // }}}
2506 
2507 // {{{ class HoldingsListEntry
2510  protected $el_address = array(
2511  'holding_id' => '//holding_id',
2512  'call_number' => '//holding/call_number',
2513  'library_code' => '//holding/library',
2514  'library' => '//holding/library/@desc',
2515  'location_code' => '//holding/location',
2516  'location' => '//holding/location/@desc'
2517  );
2518  public $xml;
2519 
2520  function __construct($node,$mms_id) {
2521  $this->xml = new DomDocument();
2522  $this->xml->appendChild($this->xml->importNode($node,true));
2523  $this->el_override['mms_id'] = $mms_id;
2524  }
2525 
2526  function getItemList($limit = -1) {
2527  global $grima;
2528  $this->getMmsIfNeeded();
2529  $this->itemList = new ItemList($this['mms_id'], $this['holding_id'], $limit);
2530  }
2531 }
2532 
2533 // }}}
2534 
2535 // {{{ class ItemList
2537 class ItemList extends AlmaObject {
2538  public $items = array(); #maybe ok as is
2539 
2540  function __construct($mms_id,$holding_id,$limit =-1) {
2541 
2542  global $grima;
2543  $curr_offset = 0;
2544  $req_limit = ($limit == -1)?100:$limit;
2545 
2546  do {
2547  if ($curr_offset > 0) {
2548  if (($curr_offset+1)*100 > $limit) {
2549  $req_limit = $limit - $curr_offset*100;
2550  } else {
2551  $req_limit = 100;
2552  }
2553  }
2554  $xml = $grima->getItemList($mms_id,$holding_id,$req_limit,$curr_offset*100);
2555  $xpath = new DomXpath($xml);
2556  $is = $xpath->query('//item');
2557  foreach ($is as $i) {
2558  $new_item = new Item();
2559  $new_item->loadFromItemListNode($i);
2560  $this->items[] = $new_item;
2561  }
2562  $xpath = new DomXPath($xml);
2563  if (!$curr_offset) {
2564  $length = $xpath->query('//items/@total_record_count')[0]->nodeValue;
2565  if ($limit == -1) { $limit = $length; }
2566  }
2567  $curr_offset++;
2568 
2569  } while (($curr_offset*100 < $length) and ($curr_offset*100 < $limit));
2570 
2571  }
2572 
2573 }
2574 
2575 // }}}
2576 
2577 // {{{ class Holding
2580  public $itemList; # object
2581  public $items = array();
2582  public $xml;
2583 
2584  function offsetSet($offset,$value) {
2585  if ($offset == "mms_id") {
2586  $this->el_override['mms_id'] = $value;
2587  } else {
2588  parent::offsetSet($offset,$value);
2589  }
2590  }
2591 
2592  function offsetGet($offset) { #XXX TEST
2593  if ($offset == "library") {
2594  $lib = new Library();
2595  $lib->loadFromAlma($this['library_code']);
2596  return $lib['name'];
2597  }
2598  if ($offset == "location") {
2599  $loc = new Location();
2600  $loc->loadFromAlma($this['library_code'],$this['location_code']);
2601  return $loc['name'];
2602  }
2603  if ($offset == "call_number") {
2604  $Hs = $this->getSubfieldValues("852","h");
2605  $Is = $this->getSubfieldValues("852","i");
2606  $acc = "";
2607  foreach ($Hs as $h) {
2608  $acc .= "$h ";
2609  }
2610  foreach ($Is as $i) {
2611  $acc .= "$i ";
2612  }
2613  return rtrim($acc);
2614  }
2615  return parent::offsetGet($offset);
2616  }
2617 
2618 // {{{ $el_address
2619  public $el_address = array(
2620  'holding_id' => '//holding_id',
2621  'inst_code' => "/holding/record/datafield[@tag='852']/subfield[@code='a']",
2622  'library_code' => "/holding/record/datafield[@tag='852']/subfield[@code='b']",
2623  'location_code' => "/holding/record/datafield[@tag='852']/subfield[@code='c']",
2624 
2625  'classification_part' => "/holding/record/datafield[@tag='852']/subfield[@code='h']",
2626  '852h' => "/holding/record/datafield[@tag='852']/subfield[@code='h']",
2627 
2628  'item_part' => "/holding/record/datafield[@tag='852']/subfield[@code='i']",
2629  '852i' => "/holding/record/datafield[@tag='852']/subfield[@code='i']",
2630  'shelving_scheme' => "/holding/record/datafield[@tag='852']/@ind1",
2631  );
2632 // }}}
2633 
2634 // {{{ loadFromAlma (get) - populates record from Alma
2641  function loadFromAlma($mms_id,$holding_id) {
2642  global $grima;
2643  $this->xml = $grima->getHolding($mms_id,$holding_id);
2644  $this['mms_id'] = $mms_id;
2645  }
2646 // }}}
2647 
2648 // {{{ getMmsFromHoldingID (get) - gets the MMS for a holding ID
2653  public static function getMmsFromHoldingID($holding_id) {
2654  global $grima;
2655 
2656  $report = new AnalyticsReport();
2657  # $report->path = "/shared/UK Libraries- University of Kentucky (UKY)/Reports/Kathryn/HoldingToMMS";
2658  $report->path = "/shared/Community/Reports/Institutions/University of Kentucky/Kathryn/HoldingToMMS";
2659  $report->filter = '
2660 <sawx:expr xsi:type="sawx:comparison" op="equal" xmlns:saw="com.siebel.analytics.web/report/v1.1"
2661 xmlns:sawx="com.siebel.analytics.web/expression/v1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2662 xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2663  <sawx:expr xsi:type="sawx:sqlExpression">"Holding Details"."Holding Id"</sawx:expr><sawx:expr xsi:type="xsd:string">{holding_id}</sawx:expr>
2664 </sawx:expr>';
2665 
2666  $report->runReport(array('holding_id' => $holding_id), 1);
2667  if (count($report->rows) == 1) {
2668  return $report->rows[0][1];
2669  } else {
2670  throw new Exception("No MMS for Holding ID $holding_id");
2671  }
2672  }
2673 // }}}
2674 
2675 // {{{ getMmsIfNeeded (get) - populates MMS if needed
2680  function getMmsIfNeeded() {
2681  global $grima;
2682 
2683  if (!isset($this['mms_id']) or (!$this['mms_id'])) {
2684  $this['mms_id'] = Holding::getMmsFromHoldingID($this['holding_id']);
2685  }
2686  }
2687 // }}}
2688 
2689 // {{{ loadFromAlmaX (get) - populates record from Alma using holding_id
2695  function loadFromAlmaX($holding_id) {
2696  global $grima;
2697 
2698  $mms_id = Holding::getMmsFromHoldingID($holding_id);
2699  $this->xml = $grima->getHolding($mms_id,$holding_id);
2700  $this['mms_id'] = $mms_id;
2701  }
2702 // }}}
2703 
2704 // {{{ addToAlmaBib (post) - adds new holding record to specified bib
2710  function addToAlmaBib($mms_id) {
2711  global $grima;
2712  $this->xml = $grima->postHolding($mms_id,$this->xml);
2713  $this['mms_id'] = $mms_id;
2714  }
2715 // }}}
2716 
2717 // {{{ hasItems - checks whether a holding record has any items
2722  function hasItems() {
2723  $this->getItemList();
2724  return (sizeof($this->itemList->items) > 0); # XXX test
2725  }
2726 // }}}
2727 
2728 // {{{ updateAlma (put) - update record in Alma
2732  function updateAlma() {
2733  global $grima;
2734  $grima->putHolding($this['mms_id'],$this['holding_id'],$this->xml);
2735  }
2736 // }}}
2737 
2738 // {{{ deleteFromAlma (delete) - delete record in Alma
2742  function deleteFromAlma() {
2743  global $grima;
2744  $grima->deleteHolding($this['mms_id'],$this['holding_id']);
2745  }
2746 // }}}
2747 
2748  # call number object?
2749 
2751  # must have mms_id set? confirm
2752  # must have shelving set? confirm
2753 
2754  $bib = new Bib();
2755  $bib->loadFromAlma(); # link?
2756 
2757  switch($this["shelving_scheme"]) {
2758  case '0': # Library of Congress
2759  $fields = $bib->getFields("090");
2760  foreach ($fields as $field) {
2761  $subfields = $field->getSubfields("a");
2762  print $field["ind1"];
2763  $field["ind1"] = "4";
2764  foreach ($subfields as $subfield) {
2765  print $subfield["value"];
2766  $subfield["value"] = "joe";
2767  }
2768  }
2769  break;
2770  }
2771 
2772  }
2773 
2774  function setCallNumber($h,$i,$ind1) {
2775  $xpath = new DomXpath($this->xml);
2776  $xpath->query("//record/datafield[@tag='852']")->item(0)->setAttribute("ind1",$ind1);
2777 
2778  $field852 = $xpath->query("//record/datafield[@tag='852']")->item(0);
2779  $subfieldHs = $xpath->query("subfield[@code='h']",$field852);
2780  foreach ($subfieldHs as $subfieldH) {
2781  $subfieldH->parentNode->removeChild($subfieldH);
2782  }
2783  $subfieldIs = $xpath->query("subfield[@code='i']",$field852);
2784  foreach ($subfieldIs as $subfieldI) {
2785  $subfieldI->parentNode->removeChild($subfieldI);
2786  }
2787 
2788  appendInnerXML($field852,"<subfield code=\"h\">$h</subfield>");
2789  if ($i) {
2790  appendInnerXML($field852,"<subfield code=\"i\">$i</subfield>");
2791  }
2792  }
2793 
2794 // {{{ moveToBib - moves a holding from one bib to another
2798  function moveToBib($mms_id) {
2799  $this->deleteFromAlma();
2800  $this->addToAlmaBib($mms_id);
2801  }
2802 // }}}
2803 
2804 // {{{ getItems - get items objects
2808  function getItems() {
2809  $this->getItemList();
2810  $this->items =& $this->itemList->items;
2811  return $this->items;
2812  }
2813 // }}}
2814 
2815 // {{{ getItemList - populates itemList property from Alma
2819  function getItemList() {
2820  global $grima;
2821  $this->getMmsIfNeeded();
2822  $this->itemList = new ItemList($this['mms_id'],$this['holding_id']);
2823  }
2824 // }}}
2825 
2826 // {{{ Holding -> deleteTreeFromAlma - delete the holding and all items
2830  function deleteTreeFromAlma() {
2831  global $grima;
2832  $this->getItemList();
2833  foreach ($this->itemList->items as $item) {
2834  $item->deleteFromAlma("true");
2835  }
2836  $this->deleteFromAlma();
2837  }
2838 // }}}
2839 
2840 // {{{ Holding -> deleteSubfieldMatching # XXX TEST. UM. MARC INTERFACE? OMG
2849 /*
2850  function deleteSubfieldMatching($tag,$code,$regex) {
2851  $xpath = new DomXPath($this->xml);
2852  $subfs = $xpath->query("datafield[@tag='$tag']/subfield[@code='$code']");
2853  foreach ($subfs as $subf) {
2854  print "HERE";
2855  if (preg_match($regex,$subf->nodeValue)) {
2856  $subf->parentNode->removeChild($subf);
2857  }
2858  }
2859  }
2860 */
2861 // }}}
2862 
2863 }
2864 
2865 // }}}
2866 
2867 // {{{ class Item
2869 class Item extends AlmaObject {
2870 
2871  public $el_address = array(
2872  'item_pid' => '//pid',
2873  'barcode' => '//barcode',
2874  'creation_date' => '//creation_date',
2875  'modification_date' => '//modification_date',
2876  'base_status' => '//base_status',
2877  'physical_material_type_code' => '//physical_material_type',
2878  'physical_material_type' => '//physical_material_type/@desc',
2879  'location' => '//location/@desc',
2880  'location_code' => '//location',
2881  'library' => '//location/@desc',
2882  'library_code' => '//library',
2883  'policy' => '//policy',
2884  'item_policy' => '//policy',
2885  'provenance' => '//provenance',
2886  'po_line' => '//po_line',
2887  'is_magnetic' => '//is_magnetic',
2888  'arrival_date' => '//arrival_date',
2889  'year_of_issue' => '//year_of_issue',
2890  'enumeration_a' => '//enumeration_a',
2891  'enumeration_b' => '//enumeration_b',
2892  'enumeration_c' => '//enumeration_c',
2893  'enumeration_d' => '//enumeration_d',
2894  'enumeration_e' => '//enumeration_e',
2895  'enumeration_f' => '//enumeration_f',
2896  'enumeration_g' => '//enumeration_g',
2897  'enumeration_h' => '//enumeration_h',
2898  'chronology_i' => '//chronology_i',
2899  'chronology_j' => '//chronology_j',
2900  'chronology_k' => '//chronology_k',
2901  'chronology_l' => '//chronology_l',
2902  'chronology_m' => '//chronology_m',
2903  'description' => '//description',
2904  'alternative_call_number' => '//alternative_call_number',
2905  'alternative_call_number_type' => '//alternative_call_number_type',
2906  'storage_location_id' => '//storage_location_id',
2907  'receiving_operator' => '//receiving_operator',
2908  'process_type' => '//process_type',
2909  'in_temp_location' => '//in_temp_location',
2910  'mms_id' => '//mms_id',
2911  'holding_id' => '//holding_id',
2912  'title' => '//title',
2913  'author' => '//author',
2914  'call_number' => '//call_number',
2915  'pages' => '//pages',
2916  'pieces' => '//pieces',
2917  'public_note' => '//public_note',
2918  'fulfillment_note' => '//fulfillment_note',
2919  'internal_note_1' => '//internal_note_1',
2920  'internal_note_2' => '//internal_note_2',
2921  'internal_note_3' => '//internal_note_3',
2922  'statistics_note_1' => '//statistics_note_1',
2923  'statistics_note_2' => '//statistics_note_2',
2924  'statistics_note_3' => '//statistics_note_3',
2925  'requested' => '//requested',
2926  'physical_condition' => '//physical_condition',
2927  'temp_library' => '//temp_library',
2928  'temp_location' => '//temp_location',
2929  );
2930 
2931 // {{{ loadFromAlma (get)
2939  function loadFromAlma($mms_id,$holding_id,$item_pid) {
2940  global $grima;
2941  $this->xml = $grima->getItem($mms_id,$holding_id,$item_pid);
2942  }
2943 // }}}
2944 
2945 // {{{ loadFromAlmaX (get)
2950  function loadFromAlmaX($item_pid) {
2951  global $grima;
2952  $this->xml = $grima->getItem('X','X',$item_pid);
2953  }
2954 // }}}
2955 
2956 // {{{ loadFromAlmaBarcode (get)
2961  function loadFromAlmaBarcode($barcode) {
2962  global $grima;
2963  $this->xml = $grima->getItemBC($barcode);
2964  }
2965 // }}}
2966 
2967 // {{{ loadFromAlmaBCorX (get)
2974  function loadFromAlmaBCorX($id) {
2975  global $grima;
2976  if (preg_match("/^23.*/",$id)) { # item_pid
2977  # probably should know about suffix too
2978  $this->loadFromAlmaX($id);
2979  } else {
2980  $this->loadFromAlmaBarcode($id);
2981  }
2982  }
2983 // }}}
2984 
2985 // {{{ loadFromItemListNode
2991  function loadFromItemListNode($node) {
2992  $this->xml = new DomDocument();
2993  $this->xml->appendChild($this->xml->importNode($node,true));
2994  }
2995 // }}}
2996 
2997 // {{{ addToAlmaHolding (post)
3004  function addToAlmaHolding($mms_id,$holding_id) {
3005  global $grima;
3006  $this->mms_id = $mms_id;
3007  $this->holding_id = $holding_id;
3008  $this->xml = $grima->postItem($mms_id,$holding_id,$this->xml);
3009  return $this->xml;
3010  }
3011 // }}}
3012 
3013 // {{{ addToAlmaHoldingX (post)
3019  # XXX check this
3020  function addToAlmaHoldingX($holding_id) {
3021  $this->addToAlmaHolding('X',$holding_id);
3022  return $this->xml;
3023  }
3024 // }}}
3025 
3026 // {{{ updateAlma (put)
3031  function updateAlma() {
3032  global $grima;
3033  return $grima->putItem(
3034  $this['mms_id'],
3035  $this['holding_id'],
3036  $this['item_pid'],
3037  $this->xml
3038  );
3039  }
3040 // }}}
3041 
3042 // {{{ deleteFromAlma (delete)
3049  function deleteFromAlma($override = "false", $holdings = "retain") {
3050  global $grima;
3051  $grima->deleteItem($this['mms_id'],$this['holding_id'],$this['item_pid'],$override,$holdings);
3052  }
3053 // }}}
3054 
3055 }
3056 
3057 // }}}
3058 
3059 // {{{ class ElectronicCollection
3062 
3063  public $xml;
3064  public $services = array();
3065 
3066 
3067 // {{{ ElectronicCollection -> el_address
3068  public $el_address = array( # XXX many more fields here
3069  'collection_id' => '//id',
3070  'id' => '//id',
3071  'public_name' => '//public_name',
3072  );
3073 // }}}
3074 
3075 // {{{ ElectronicCollection -> loadFromAlma (get)
3080  function loadFromAlma($collection_id) {
3081  global $grima;
3082  $this->xml = $grima->getElectronicCollection($collection_id);
3083  #$this['collection_id'] = $collection_id;
3084  }
3085 // }}}
3086 
3087 // {{{ ElectronicCollection -> updateAlma (put)
3092  function updateAlma() {
3093  global $grima;
3094  $this->xml = $grima->putElectronicCollection($this['collection_id'], $this->xml);
3095  }
3096 // }}}
3097 
3098 // {{{ ElectronicCollection -> getServices (get) XXX
3103  function getServices() {
3104  global $grima;
3105  $ret = $grima->getElectronicServices($this['collection_id']);
3106  $xpath = new DomXpath($ret);
3107  $eservices = $xpath->query('//electronic_services/electronic_service');
3108  foreach ($eservices as $service) {
3109  $service_id = $service->firstChild->nodeValue; # XXX really?
3110  $ser = new ElectronicService();
3111  $ser->loadFromServiceListNode($service);
3112  $this->services[] = $ser;
3113  }
3114  }
3115 // }}}
3116 
3117 }
3118 
3119 // }}}
3120 
3121 // {{{ class ElectronicService
3125 
3126  public $portfolios = array();
3127 
3128  public $el_address = array(
3129  'id' => '//id',
3130  'service_id' => '//id',
3131  );
3132 
3133  public $el_override = array();
3134 
3135  function __construct() {
3136  # load from template
3137  }
3138 
3139  function offsetSet($offset,$value) {
3140  if ($offset == "collection_id") {
3141  $this->el_override['collection_id'] = $value;
3142  } else {
3143  parent::offsetSet($offset,$value);
3144  }
3145  }
3146 
3147 // {{{ ElectronicService -> loadFromAlma (get)
3152  function loadFromAlma($collection_id,$service_id) {
3153  global $grima;
3154  $this->xml = $grima->getElectronicService($collection_id,$service_id);
3155  $this['collection_id'] = $collection_id;
3156  }
3157 // }}}
3158 
3159 // {{{ ElectronicService -> loadFromServiceListNode
3165  function loadFromServiceListNode($node) {
3166  $this->xml = new DomDocument();
3167  $this->xml->appendChild($this->xml->importNode($node,true));
3168  $xpath = new DomXPath($this->xml);
3169  $service_link = $xpath->query('//electronic_service')->item(0)->attributes['link']->nodeValue;
3170  preg_match('!/e-collections/(.*)/e-services/!',$service_link,$m);
3171  $this['collection_id'] = $m[1];
3172  }
3173 
3175  $curr_offset = 0;
3176  do {
3177  $retrieved = $this->getPortfolios(100, $curr_offset);
3178  $curr_offset += 100;
3179  } while ($retrieved == 100);
3180  }
3181 
3182 // {{{ ElectronicService -> getPortfolios
3190  /* mark full or brief? */
3191  #function retrievePortfolios($limit = 10, $offset = 0) { # RENAME
3192  function getPortfolios($limit = 10, $offset = 0) {
3193  global $grima;
3194  $ret = $grima->getElectronicPortfoliosForService($this['collection_id'],$this['service_id'],$limit,$offset);
3195  $xpath = new DOMXpath($ret);
3196  $ports = $xpath->query('//portfolio');
3197  foreach ($ports as $portnode) {
3198  $newport = new ElectronicPortfolio();
3199  $newport->loadFromPortfolioListNode($portnode);
3200  $this->portfolios[] = $newport;
3201  }
3202  return count($ports);
3203  }
3204 
3205 // }}}
3206 
3207 // {{{ ElectronicService -> deleteAllPortfolios - delete all portfolios from the bib
3213  function deleteAllPortfolios($bib_treat) {
3214  global $grima;
3215  while ($this->getPortfolios(100,0)) {
3216  foreach($this->portfolios as $portfolio) {
3217  $portfolio->deleteFromAlma($bib_treat);
3218  }
3219  $this->portfolios = array();
3220  sleep(2);
3221  }
3222  }
3223 // }}}
3224 
3225 /*
3226  function deleteAllPortfolios() { #XXX
3227  global $grima;
3228  # get portfolio list? or just ten at a time?
3229  $this->retrievePortfolios();
3230  while (sizeof($this->portfolios) > 0) {
3231  foreach ($this->portfolios as $portfolio) {
3232  print $portfolio['mms_id']->nodeValue; exit;
3233  #$portfolio->delete();
3234  }
3235  }
3236  }
3237 */
3238 
3239 }
3240 
3241 //}}}
3243 //}}}
3244 
3245 //{{{ class ElectronicPortfolio
3249  public $xml;
3250 
3251 // {{{ ElectronicPortfolio -> el_address
3252  public $el_address = array(
3253  'portfolio_id' => '//portfolio/id',
3254  'is_local' => '//is_local',
3255  'is_standalone' => '//is_standalone',
3256  'mms_id' => '//mms_id',
3257  'title' => '//title',
3258  'service' => '//service',
3259  'url' => '//url',
3260  'static_url' => '//static_url',
3261  'availability' => '//availability',
3262  'collection_id' => '//electronic_collection/id',
3263  'service_id' => '//electronic_collection/service',
3264  'material_type' => '//material_type',
3265  'url_type' => '//url_type',
3266  'public_note' => '//public_note',
3267  'proxy_enabled' => '//proxy_enabled',
3268  'library' => '//library'
3269  );
3270 // }}}
3271 
3272 // {{{ ElectronicPortfolio -> addToAlmaService
3279  function addToAlmaService($collection_id,$service_id) {
3280  global $grima;
3281  $ret = $grima->postElectronicPortfolio($collection_id, $service_id, $this->xml);
3282  return $ret;
3283  }
3284 // }}}
3285 
3286 // {{{ ElectronicPortfolio -> addToAlmaBib
3294  function addToAlmaBib($mms_id) {
3295  global $grima;
3296  $ret = $grima->postElectronicPortfolioOnBib($mms_id, $this->xml);
3297  return $ret;
3298  }
3299 // }}}
3300 
3301 // {{{ ElectronicPortfolio -> loadFromAlma
3307  function loadFromAlma($portfolio_id) {
3308  global $grima;
3309  $this->xml = $grima->getElectronicPortfolio('X','X',$portfolio_id);
3310  }
3311 
3312  function loadFromAlmaX($portfolio_id) {
3313  global $grima;
3314  $this->xml = $grima->getElectronicPortfolio('X','X',$portfolio_id);
3315  }
3316 // }}}
3317 
3318 // {{{ ElectronicPortfolio -> loadFromPortfolioListNode
3324  function loadFromPortfolioListNode($node) {
3325  $this->xml = new DomDocument();
3326  $this->xml->appendChild($this->xml->importNode($node,true));
3327  }
3328 // }}}
3329 
3330 // {{{ ElectronicPortfolio -> updateAlma
3334  function updateAlma() {
3335  global $grima;
3336  $this->xml = $grima->putElectronicPortfolioOnBib($this['mms_id'],$this['portfolio_id'],$this->xml);
3337  }
3338 // }}}
3339 
3340 // {{{ ElectronicPortfolio -> deleteFromAlma
3346  function deleteFromAlma($bib_treat = "retain") { # accept a variable?
3347  global $grima;
3348  $mms_id = $this['mms_id'];
3349  $grima->deleteElectronicPortfolio('X','X',
3350  $this['portfolio_id']);
3351  if ($bib_treat == "delete") {
3352  $bib = new Bib();
3353  $bib->loadFromAlma($mms_id);
3354  sleep(2);
3355  if (! $bib->hasInventory()) {
3356  if ($bib->linkedToCZ()) {
3357  print "LINKED TO CZ";
3358  $bib->unlinkFromCZ();
3359  exit;
3360  } else {
3361  $bib->deleteFromAlma();
3362  }
3363  }
3364  }
3365  }
3366 // }}}
3367 
3368 }
3369 
3370 // }}}
3371 
3372 // {{{ class Set
3374 class Set extends AlmaObject {
3375  public $xml;
3376  public $members = array();
3377 
3378  /*
3379  public $id;
3380  public $type; # itemized or logical
3381  public $name;
3382  public $private;
3383  public $active;
3384  public $size; # number of members
3385  */
3386 
3387 // {{{ Set -> el_address
3388  public $el_address = array(
3389  'set_id' => '//set/id',
3390  'id' => '//set/id',
3391  'name' => '//set/name',
3392  'description' => '//set/description',
3393  'type' => '//set/type',
3394  'content' => '//content',
3395  'private' => '//private',
3396  'status' => '//status',
3397  'status_date' => '//status_date',
3398  'note' => '//note',
3399  'created_by' => '//created_by',
3400  'created_date' => '//created_date',
3401  'query' => '//query',
3402  'number_of_members' => '//number_of_members',
3403  'additional_info' => '//additional_info',
3404  );
3405 // }}}
3406 
3407 // {{{ Set -> addToAlma (post) - adds the Set to Alma
3411  function addToAlma() {
3412  global $grima;
3413  $this->xml = $grima->postSet($this->xml);
3414  }
3415 // }}}
3416 
3417 // {{{ Set -> loadFromAlma
3423  function loadFromAlma($set_id) {
3424  global $grima;
3425  $this->xml = $grima->getSet($set_id);
3426  }
3427 // }}}
3428 
3429 // {{{ Set -> createFromImport
3436  function createFromImport($job_id,$population) {
3437  global $grima;
3438  $this->xml = $grima->createSetFromImport($job_id,$population);
3439  }
3440 // }}}
3441 
3442 // {{{ Set -> runOnElements
3450  function runOnElements($function, $args = array(), $filter = null, $filter_args = array()) {
3451  global $grima;
3452  $xml = $grima->getSetMembers($this['set_id'],0);
3453  $xpath = new DomXpath($xml);
3454  $this->size = $xpath->query("//members")->item(0)->getAttribute("total_record_count");
3455  $limit = $this->size;
3456  for ($j = 0; $j < ceil($limit/100); $j++) { # how many queries
3457  $xml = $grima->getSetMembers($this['id'],100,$j*100);
3458  $xpath = new DomXpath($xml);
3459  foreach ($xpath->query("//member") as $member) {
3460  $id = $member->childNodes[0]->nodeValue;
3461  if (!isset($filter) or ($filter($id,$filter_args))) {
3462  $function($id, $args);
3463  }
3464  }
3465  }
3466 
3467  }
3468 // }}}
3469 
3470  function getMembers($limit = -1) { # put in $members
3471  # limit -1 means all
3472  global $grima;
3473  if ($limit == -1) { # get them all
3474  $xml = $grima->getSetMembers($this['set_id'],0);
3475  $xpath = new DomXpath($xml);
3476  $this->size = $xpath->query("//members")->item(0)->getAttribute("total_record_count");
3477  $limit = $this->size;
3478  }
3479 
3480  for ($j = 0; $j < ceil($limit/100); $j++) { # how many queries
3481  $xml = $grima->getSetMembers($this['id'],100,$j*100);
3482  $xpath = new DomXpath($xml);
3483  foreach ($xpath->query("//member") as $member) {
3484  $this->members[] = new SetMember(
3485  $member->childNodes[0]->nodeValue,
3486  $member->childNodes[1]->nodeValue
3487  );
3488  }
3489  }
3490  }
3491 
3492 // {{{ Set -> deleteFromAlma (delete)
3497  function deleteFromAlma() {
3498  global $grima;
3499  $grima->deleteSet($this['set_id']);
3500  }
3501 // }}}
3502 
3503 // {{{ Set -> addMember (delete)
3510  function addMember($id) {
3511  $frag = "<member><id>$id</id></member>";
3512  $source = new DOMDocument();
3513  $source->loadXML($frag);
3514 
3515  $xpath = new DomXpath($this->xml);
3516  $members = $xpath->query('//members')->item(0);
3517  $members->appendChild($this->xml->importNode($source->documentElement, true));
3518 
3519  }
3520 // }}}
3521 
3522 // {{{ Set -> appendInAlma ()
3528  function appendInAlma($setToAppend) {
3529  global $grima;
3530  $grima->postSetManageMembers($this['set_id'],null,'add_members',
3531  $setToAppend->xml);
3532  }
3533 // }}}
3534 
3535 // {{{ Set -> removeAllMembersInAlma ()
3541  global $grima;
3542  $emptyset = new Set();
3543  $emptyset->addMember('9941266686802636'); /* hack */
3544  $grima->postSetManageMembers($this['set_id'],null,'replace_members',
3545  $emptyset->xml);
3546  $grima->postSetManageMembers($this['set_id'],null,'delete_members',
3547  $emptyset->xml);
3548  }
3549 
3550 }
3551 
3552 // }}}
3553 
3554 // }}}
3555 
3556 // {{{ class SetMember
3558 class SetMember {
3559  public $id;
3561 
3562  function __construct($id,$description) {
3563  $this->id = $id;
3564  $this->description = $description;
3565  }
3566 }
3567 
3568 // }}}
3569 
3570 // {{{ class Library
3572 class Library extends AlmaObject {
3573  public $el_address = array(
3574  'code' => '//code',
3575  'path' => '//path',
3576  'name' => '//name',
3577  'resource_sharing' => '//resource_sharing',
3578  );
3579 
3580 // {{{ Library -> loadFromAlma
3586  function loadFromAlma($code) {
3587  global $grima;
3588  $this->xml = $grima->getLibrary($code);
3589  }
3590 // }}}
3591 
3592 // {{{ Library -> getAllLibraries
3598  public static function getAllLibraries() {
3599  global $grima;
3600  $xml = $grima->getAllLibraries();
3601  $xpath = new DomXpath($xml);
3602  $codes = $xpath->query('//library/code');
3603  $libarray = array();
3604  foreach ($codes as $code) {
3605  $lib = new Library();
3606  $lib->loadFromAlma($code->nodeValue);
3607  $libarray[] = $lib;
3608  }
3609  return $libarray;
3610  }
3611 // }}}
3612 
3613 // {{{ Library -> getOneLibrary
3620  public static function getOneLibrary($otherthan = array()) {
3621  global $grima;
3622  $xml = $grima->getAllLibraries();
3623  $xpath = new DomXpath($xml);
3624  $codes = $xpath->query('//library/code');
3625 
3626  foreach ($codes as $code) {
3627  if (! in_array($code,$otherthan)) {
3628  $lib = new Library();
3629  $lib->loadFromAlma($code);
3630  return $lib;
3631  }
3632  }
3633  throw new Exception("No libraries found.");
3634  }
3635 // }}}
3636 
3637 // {{{ Library -> getAllLocations
3643  public function getAllLocations() {
3644  global $grima;
3645  $xml = $grima->getAllLocations($this['code']);
3646  $xpath = new DomXpath($xml);
3647  $codes = $xpath->query('//location/code');
3648  $locarray = array();
3649  foreach ($codes as $code) {
3650  $loc = new Location();
3651  $loc->loadFromAlma($this['code'], $code->nodeValue);
3652  $locarray[] = $loc;
3653  }
3654  return $locarray;
3655  }
3656 // }}}
3657 
3658 // {{{ Library -> getOneLocation
3665  public function getOneLocation($otherthan = array()) {
3666  global $grima;
3667  $xml = $grima->getAllLocations($this['code']);
3668  $xpath = new DomXpath($xml);
3669  $codes = $xpath->query('//location/code');
3670  foreach ($codes as $code) {
3671  if (! in_array($code->nodeValue,$otherthan)) {
3672  $loc = new Location();
3673  $loc->loadFromAlma($this['code'],$code->nodeValue);
3674  return $loc;
3675  }
3676  }
3677  throw new Exception("No locations found.");
3678  }
3679 // }}}
3680 
3681 // {{{ Library -> getOneLibraryLocation
3689  public static function getOneLibraryLocation($otherthan = array()) {
3690  global $grima;
3691 
3692  $xml = $grima->getAllLibraries();
3693  $xpath = new DomXpath($xml);
3694  $library_codes = $xpath->query('//library/code');
3695 
3696  foreach ($library_codes as $library_code) {
3697  $lib = new Library();
3698  $lib->loadFromAlma($library_code->nodeValue);
3699  $arr = array();
3700  foreach ($otherthan as $other) {
3701  if ($other[0] == $library_code->nodeValue) {
3702  $arr[] = $other[1];
3703  }
3704  }
3705  try {
3706  $loc = $lib->getOneLocation($arr);
3707  return $loc;
3708  } catch (Exception $e) {
3709  continue;
3710  }
3711  }
3712  throw new Exception("No suitable library/location found.");
3713  }
3714 // }}}
3715 
3716 }
3717 
3718 // }}}
3719 
3720 // {{{ class Location
3722 class Location extends AlmaObject {
3723 
3724  public $el_override_fields = array('library_code','flag');
3725 
3726  public $el_address = array(
3727  'code' => '//code',
3728  'name' => '//name',
3729  'external_name' => '//external_name',
3730  'type' => '//type',
3731  'remote_storage' => '//remote_storage',
3732  'map' => '//map',
3733  'suppress_from_publishing' => '//suppress_from_publishing',
3734  'fulfillment_unit' => '//fulfillment_unit',
3735  'accession_placement' => '//accession_placement',
3736  'call_number_type' => '//call_number_type',
3737  );
3738 
3739  function offsetSet($offset,$value) {
3740  if (in_array($offset,$this->el_override_fields)) {
3741  $this->el_override[$offset] = $value;
3742  } else {
3743  parent::offsetSet($offset,$value);
3744  }
3745  /*
3746  if ($offset == "library_code") {
3747  $this->el_override['library_code'] = $value;
3748  } else {
3749  parent::offsetSet($offset,$value);
3750  }
3751  */
3752  }
3753 
3754 // {{{ Location -> loadFromAlma
3760  function loadFromAlma($libraryCode,$locationCode) {
3761  global $grima;
3762  $this->xml = $grima->getLocation($libraryCode, $locationCode);
3763  $this['library_code'] = $libraryCode;
3764  }
3765 // }}}
3766 
3767 }
3768 
3769 // }}}
3770 
3771 // {{{ class Job
3773 class Job extends AlmaObject {
3774  public $el_address = array(
3775  'id' => '//id',
3776  'name' => '//name',
3777  'description' => '//description',
3778  'type' => '//type',
3779  'category' => '//category',
3780  'content' => '//content',
3781  'schedule' => '//schedule',
3782  'creator' => '//creator',
3783  'next_run' => '//next_run',
3784  'related_profile' => '//related_profile',
3785  'additional_info' => '//additional_info',
3786  );
3787 
3788 // {{{ Job -> addParameter
3795  function addParameter($name,$value) {
3796  $frag = "<parameter><name>$name</name><value>$value</value></parameter>";
3797  $source = new DOMDocument();
3798  $source->loadXML($frag);
3799 
3800  $xpath = new DomXpath($this->xml);
3801  $members = $xpath->query('//parameters')->item(0);
3802  $members->appendChild($this->xml->importNode($source->documentElement, true));
3803  }
3804 // }}}
3805 
3806 // {{{ Job -> runInAlma
3812  function runInAlma() {
3813  global $grima;
3814  $grima->postJob($this['id'],'run',$this->xml);
3815  }
3816 // }}}
3817 
3818 }
3819 
3820 // }}}
3821 
3822 // {{{ class AnalyticsReport
3825 
3826 public $path;
3827 public $filter;
3828 public $reportXml;
3829 public $rows = array();
3830 
3831 // {{{ AnalyticsReport -> runReport
3837  function runReport($filter_params=array(), $limit = -1, $token = "") {
3838  global $grima;
3839  if (isset($this->filter)) {
3840  $passfilter = $this->filter;
3841  foreach ($filter_params as $k => $v) {
3842  $passfilter = str_replace('{'.$k.'}',urlencode($v),$passfilter);
3843  }
3844  } else {
3845  $passfilter = null;
3846  }
3847 
3848  if ($limit == -1) { $limit = 1000; } # no limit
3849  if ($limit < 25) { $limit = 25; } # must be in chunks of 25
3850  $this->reportXml = $grima->getAnalytics($this->path, $passfilter, $limit, $token);
3851 
3852  $xpath = new DomXpath($this->reportXml);
3853  $xpath->registerNamespace("x", "urn:schemas-microsoft-com:xml-analysis:rowset");
3854 
3855  $rows = $xpath->query('//x:Row');
3856  foreach ($rows as $row) {
3857  $newrow = array();
3858  $cols = $xpath->query("./*[contains(name(),'Column')]", $row);
3859  foreach ($cols as $col) {
3860  $newrow[] = $col->nodeValue;
3861  }
3862  $this->rows[] = $newrow;
3863  }
3864 
3865  }
3866 
3867 // }}}
3868 
3869 
3870 }
3871 
3872 // }}}
3873 
3874 // {{{ class GrimaDB
3875 
3876 // {{{ shared
3877 function tableExists($pdo, $table) {
3878  $table_esc = "\"" . preg_replace( "/\"/", "\"\"", $table ) . "\"";
3879  try {
3880  $result = $pdo->query("SELECT 1 FROM $table_esc LIMIT 1");
3881  } catch (Exception $e) {
3882  // We got an exception == table not found
3883  return FALSE;
3884  }
3885  // Result is either boolean FALSE (no table found) or PDOStatement Object (table found)
3886  return $result !== FALSE;
3887 }
3888 
3889 // }}}
3890 
3891 // {{{ class GrimaDB
3896 class GrimaDB implements ArrayAccess, IteratorAggregate {
3897 
3898  private static $db = FALSE;
3899  private $info; // array
3900 
3901  static function init() {
3902  return self::getDb();
3903  }
3904 
3905  static function isEmpty() {
3906  $db = self::getDb();
3907  $result = $db->query( 'SELECT COUNT(*) as c FROM institutions' );
3908  foreach( $result as $row ) {
3909  if ($row['c']>0) return false;
3910  }
3911  return true;
3912  }
3913 
3914  static function isStateless() {
3915  $db = self::getDb();
3916  return $db === 'stateless';
3917  }
3918 
3919  protected static function getDb() {
3920  if (!self::$db) {
3921  try {
3922  $db_url = getenvWithFileFallbackAndDefault('DATABASE_URL','');
3923  $db_user = getenvWithFileFallbackAndDefault('DATABASE_USER','');
3924  $db_pass = getenvWithFileFallbackAndDefault('DATABASE_PASS','');
3925  if ($db_user && $db_pass) {
3926  self::$db = new PDO($db_url, $db_user, $db_pass);
3927  } else {
3928  self::$db = new PDO($db_url);
3929  }
3930  if (!tableExists(self::$db,"institutions")) {
3931  self::$db->exec("CREATE TABLE institutions ( institution VARCHAR(100) PRIMARY KEY, apikey VARCHAR(100), server VARCHAR(100) )");
3932  }
3933  if (!tableExists(self::$db,"users")) {
3934  self::$db->exec("CREATE TABLE users ( institution VARCHAR(100), username VARCHAR(100), password VARCHAR(255), isAdmin INTEGER )");
3935  }
3936  } catch (Exception $e) {
3937  self::$db = 'stateless';
3938  }
3939  }
3940  return self::$db;
3941  }
3942 
3943  protected function getPasswordAlgorithm() {
3944  return defined("PASSWORD_ARGON2I") ? constant("PASSWORD_ARGON2I") : PASSWORD_DEFAULT;
3945  }
3946 
3947  static function getInstitutions() {
3948  $ret = array();
3949  try {
3950  $db = self::getDb();
3951  $result = $db->query( 'SELECT institution FROM institutions' );
3952  foreach( $result as $row ) {
3953  $ret[] = $row['institution'];
3954  }
3955  } catch (Exception $e) {
3956  }
3957  return $ret;
3958  }
3959 
3960  function offsetExists($offset) {
3961  return isset($this->info[$offset]);
3962  }
3963 
3964  function offsetGet($offset) {
3965  return isset($this->info[$offset]) ? $this->info[$offset] : '';
3966  }
3967 
3968  function offsetSet($offset,$value) {
3969  $this->info[$offset] = $value;
3970  }
3971 
3972  function offsetUnset($offset) {
3973  unset($this->info[$offset]);
3974  }
3975 
3976  function getIterator() {
3977  return new ArrayIterator($this->info);
3978  }
3979 
3980 }
3981 
3982 // }}}
3983 
3984 // {{{ class GrimaInstitution
3988 class GrimaInstitution extends GrimaDB {
3989  public function addToDB() {
3990  $db = $this->getDb();
3991  $query = $db->prepare( 'INSERT INTO institutions(institution,apikey,server) VALUES (:institution,:apikey,:server)' );
3992  if (!$query) {
3993  $errorCode = $db->errorCode();
3994  $errorInfo = $db->errorInfo();
3995  throw new Exception(
3996  "Could not even prepare to insert into institution database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
3997  );
3998  }
3999  $success = $query->execute( array(
4000  'institution' => $this['institution'],
4001  'apikey' => $this['apikey'],
4002  'server' => $this['server'],
4003  ) );
4004  if (!$success) {
4005  $errorCode = $query->errorCode();
4006  $errorInfo = $query->errorInfo();
4007  throw new Exception(
4008  "Could not insert into user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4009  );
4010  }
4011  }
4012 }
4013 
4014 // }}}
4015 
4016 // {{{ class GrimaUser
4020 class GrimaUser extends GrimaDB {
4021  public static function FromArray( $data ) {
4022  $user = new GrimaUser();
4023  foreach ( $data as $key => $val ) {
4024  $user[$key] = $val;
4025  }
4026  return $user;
4027  }
4028 
4029  public static function LookupUser($username, $institution = '', $password = FALSE) {
4030  $db = self::getDb();
4031  $query = $db->prepare(
4032  'SELECT * ' .
4033  'FROM institutions NATURAL JOIN users '.
4034  'WHERE institution=:institution '.
4035  'AND username=:username'
4036  );
4037  $success = $query->execute( array(
4038  'institution' => $institution,
4039  'username' => $username,
4040  ) );
4041  if ($success) {
4042  $row = $query->fetch(PDO::FETCH_ASSOC);
4043  if ($row===false) return false;
4044  $user = GrimaUser::FromArray( $row );
4045  if ( ($password !== FALSE) && ($user['password']!='') ) {
4046  if ( !password_verify( $password, $user['password'] ) ) return false;
4047  if ( password_needs_rehash( $user['password'], $user->getPasswordAlgorithm() ) ) {
4048  $user['password'] = $password;
4049  $user->updateDB();
4050  }
4051  }
4052  unset( $user['password'] );
4053  return $user;
4054  } else {
4055  $errorCode = $query->errorCode();
4056  $errorInfo = $query->errorInfo();
4057  throw new Exception(
4058  "Could not select from user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4059  );
4060  }
4061  }
4062 
4063  public static function RenameUser( $username, $institution, $newusername ) {
4064  $db = self::getDb();
4065  $query = $db->prepare(
4066  'UPDATE users ' .
4067  'SET username=:newusername ' .
4068  'WHERE institution=:institution '.
4069  'AND username=:username'
4070  );
4071  $success = $query->execute( array(
4072  'institution' => $institution,
4073  'username' => $username,
4074  'newusername' => $newusername,
4075  ) );
4076  if ($success) {
4077  return true;
4078  } else {
4079  $errorCode = $query->errorCode();
4080  $errorInfo = $query->errorInfo();
4081  throw new Exception(
4082  "Could not update user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4083  );
4084  }
4085  }
4086 
4087  public static function ResetPassword( $username, $institution, $password ) {
4088  $db = self::getDb();
4089  $query = $db->prepare(
4090  'UPDATE users ' .
4091  'SET password=:password ' .
4092  'WHERE institution=:institution '.
4093  'AND username=:username'
4094  );
4095  $passwordHash = password_hash( $password, self::getPasswordAlgorithm() );
4096  $success = $query->execute( array(
4097  'institution' => $institution,
4098  'username' => $username,
4099  'password' => $passwordHash,
4100  ) );
4101  if ($success) {
4102  return true;
4103  } else {
4104  $errorCode = $query->errorCode();
4105  $errorInfo = $query->errorInfo();
4106  throw new Exception(
4107  "Could not update user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4108  );
4109  }
4110  }
4111 
4112  public static function GetCurrentUser() {
4113  if (!isset($_SESSION)) return false;
4114  return GrimaUser::FromArray( $_SESSION );
4115  }
4116 
4117  public static function SetCurrentUser( $username, $password, $institution = '' ) {
4118  global $grima;
4119  $user = GrimaUser::LookupUser( $username, $institution, $password );
4120  if ( $user !== false ) {
4121  $grima->session_save($user);
4122  } else {
4123  return false;
4124  }
4125  }
4126 
4127  public static function LogoutCurrentUser() {
4128  global $grima;
4129  $grima->session_destroy();
4130  }
4131 
4132  function addToDB() {
4133  $db = $this->getDb();
4134  $query = $db->prepare( 'INSERT INTO users( username, password, institution, isAdmin ) VALUES (:username, :password, :institution, :isAdmin)' );
4135  if (!$query) {
4136  $errorCode = $db->errorCode();
4137  $errorInfo = $db->errorInfo();
4138  throw new Exception(
4139  "Could not even prepare to insert into user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4140  );
4141  }
4142  $success = $query->execute( array(
4143  'username' => $this['username'],
4144  'password' => password_hash( $this['password'], $this->getPasswordAlgorithm() ),
4145  'institution' => $this['institution'],
4146  'isAdmin' => $this['isAdmin'],
4147  ) );
4148  if (!$success) {
4149  $errorCode = $query->errorCode();
4150  $errorInfo = $query->errorInfo();
4151  throw new Exception(
4152  "Could not insert into user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4153  );
4154  }
4155  }
4156 
4157  function updateDB() {
4158  $db = $this->getDb();
4159  $query = $db->prepare( 'UPDATE users SET isAdmin=:isAdmin, password=:password WHERE username=:username AND institution=:institution' );
4160  if (!$query) {
4161  $errorCode = $db->errorCode();
4162  $errorInfo = $db->errorInfo();
4163  throw new Exception(
4164  "Could not even prepare to update user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4165  );
4166  }
4167  $success = $query->execute( array(
4168  'username' => $this['username'],
4169  'password' => password_hash( $this['password'], $this->getPasswordAlgorithm() ),
4170  'institution' => $this['institution'],
4171  'isAdmin' => $this['isAdmin'],
4172  ) );
4173  if (!$success) {
4174  $errorCode = $query->errorCode();
4175  $errorInfo = $query->errorInfo();
4176  throw new Exception(
4177  "Could not update user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4178  );
4179  }
4180  }
4181 
4182  function deleteFromDB() {
4183  $db = $this->getDb();
4184  $query = $db->prepare( 'DELETE FROM users WHERE username=:username AND institution=:institution' );
4185  if (!$query) {
4186  $errorCode = $db->errorCode();
4187  $errorInfo = $db->errorInfo();
4188  throw new Exception(
4189  "Could not even prepare to delete from user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4190  );
4191  }
4192  $success = $query->execute( array(
4193  'username' => $this['username'],
4194  'institution' => $this['institution'],
4195  ) );
4196  if (!$success) {
4197  $errorCode = $query->errorCode();
4198  $errorInfo = $query->errorInfo();
4199  throw new Exception(
4200  "Could not delete from user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4201  );
4202  }
4203  }
4204 }
4205 
4206 // }}}
4207 
4208 // }}}
4209 
4210 // {{{ class EncryptedCookieSession
4211 
4213 implements SessionHandlerInterface {
4214 
4215  private $save_path;
4216  private $session_name;
4217  private $session_key;
4218  private $last_read;
4219 
4220  public function __construct( $key ) {
4221  $this->session_key = $key;
4222  $this->last_read = "Not This";
4223  }
4224 
4225  public function open ( $save_path, $session_name ) {
4226  $this->save_path = $save_path;
4227  $this->session_name = $session_name;
4228  #error_log("session_open($save_path,$session_name)");
4229  return true;
4230  }
4231 
4232  public function close () {
4233  #error_log("session_close()");
4234  return true;
4235  }
4236 
4237  public function destroy ($session_id) {
4238  #error_log("session_destroy($session_id)");
4239  $params = session_get_cookie_params();
4240  $params['expires'] = time() - 42000;
4241  unset($params['lifetime']);
4242  setcookie( $this->session_name, '', $params );
4243  return true;
4244  }
4245 
4246  public function gc ($maxlifetime) {
4247  #error_log("session_gc($maxlifetime)");
4248  return true;
4249  }
4250 
4251  public function read ($session_id) {
4252  #error_log("session_read($session_id)");
4253  $nonce_ciphertext_b64 = isset( $_COOKIE[$this->session_name] ) ? $_COOKIE[$this->session_name] : "";
4254  if ($nonce_ciphertext_b64) {
4255  $nonce_ciphertext = base64_decode( $nonce_ciphertext_b64 );
4256  if (strlen($nonce_ciphertext)>24+16) {
4257  $nonce = substr( $nonce_ciphertext, 0, 24 );
4258  $ciphertext = substr( $nonce_ciphertext, 24 );
4259  $plaintext = sodium_crypto_secretbox_open( $ciphertext, $nonce, $this->session_key );
4260  #error_log("session_read -> $plaintext");
4261  $this->last_read = $plaintext;
4262  return $plaintext ?: "";
4263  } else {
4264  error_log("cookie too short: $nonce_ciphertext_b64");
4265  }
4266  } else {
4267  #error_log("cookie not set or empty");
4268  }
4269  return "";
4270  }
4271 
4272  public function write ($session_id, $session_data) {
4273  #error_log("session_write($session_id,$session_data)");
4274  if (!$session_data) return true;
4275  if ($this->last_read == $session_data) return true;
4276  $nonce = random_bytes(24);
4277  $ciphertext = sodium_crypto_secretbox( $session_data, $nonce, $this->session_key );
4278  $nonce_ciphertext = $nonce.$ciphertext;
4279  $nonce_ciphertext_b64 = base64_encode( $nonce_ciphertext );
4280  $options = session_get_cookie_params();
4281  if (isset($options['expires']) && $options['expires']) {
4282  $options['expires'] += time();
4283  }
4284  unset($options['lifetime']);
4285  setcookie( $this->session_name, $nonce_ciphertext_b64, $options );
4286  return true;
4287  }
4288 
4289 }
4290 
4291 // }}}
4292 
4293 $grima = new Grima();
4294 
4295 /* vim: set foldmethod=marker noexpandtab shiftwidth=4 tabstop=4: */
putBib($mms_id, $bib)
Update Bib Record - updates the copy of the bib in Alma.
Definition: grima-lib.php:366
get_input()
Definition: grima-lib.php:1428
getPortfolios($limit=10, $offset=0)
get list of portfolios from Alma
Definition: grima-lib.php:3192
Definition: grima-lib.php:2159
static exists()
checks whether a grima data store has been set up in Alma
Definition: grima-lib.php:1748
offsetExists($offset)
Definition: grima-lib.php:1316
request($method, $url, $URLparams, $QSparams, $body=null)
general function for REST API calls
Definition: grima-lib.php:135
postSetManageMembers($set_id, $id_type, $op, $set)
Manage Members - modify sets in Alma.
Definition: grima-lib.php:1138
hasItems()
checks whether a holding record has any items
Definition: grima-lib.php:2722
runReport($filter_params=array(), $limit=-1, $token="")
pull Analytics report from Alma
Definition: grima-lib.php:3837
offsetSet($offset, $value)
Definition: grima-lib.php:1675
unlinkFromCZ()
does this work : Not supported for CZ
Definition: grima-lib.php:2314
$key
Definition: encrypt.php:4
$info
Definition: grima-lib.php:3899
offsetSet($offset, $value)
Definition: grima-lib.php:3739
deleteAllPortfolios()
delete all portfolios from the bib
Definition: grima-lib.php:2344
offsetSet($offset, $value)
Definition: grima-lib.php:2584
loadFromAlma($mms_id, $holding_id, $item_pid)
populates item record from Alma
Definition: grima-lib.php:2939
write($session_id, $session_data)
Definition: grima-lib.php:4272
removeAllMembersInAlma()
remove all members of the set
Definition: grima-lib.php:3540
getBib($mms_id, $view='full', $expand='None')
Retrieve Bib - retrieve a bib record from Alma.
Definition: grima-lib.php:320
deleteTreeFromAlma()
deletes the Bib and its inventory #XXX
Definition: grima-lib.php:2268
addToAlmaHolding($mms_id, $holding_id)
add new item record to holding in Alma
Definition: grima-lib.php:3004
getAllLibraries()
Retrieve Libraries - retrieve all libraries.
Definition: grima-lib.php:1009
$apikey
Definition: grima-lib.php:17
offsetUnset($offset)
Definition: grima-lib.php:3972
class Library
Definition: grima-lib.php:3572
static getInstitutions()
Definition: grima-lib.php:3947
deleteHolding($mms_id, $holding_id, $override='false')
Delete Holdings Record - delete the holdings record from Alma.
Definition: grima-lib.php:521
deleteBib($mms_id, $override='false')
Delete Bib Record - deletes the bib record from Alma.
Definition: grima-lib.php:389
putItem($mms_id, $holding_id, $item_pid, $item)
Update Item information - replace item record in Alma.
Definition: grima-lib.php:660
static ResetPassword( $username, $institution, $password)
Definition: grima-lib.php:4087
A holder for the user inputs to a grima, base on the XML file.
Definition: grima-lib.php:1560
runInAlma()
run a job in Alma
Definition: grima-lib.php:3812
setCallNumberFromBib()
Definition: grima-lib.php:2750
getHolding($mms_id, $holding_id)
Retrieve Holdings Record - retrieve holdings record from Alma.
Definition: grima-lib.php:448
getPasswordAlgorithm()
Definition: grima-lib.php:3943
loadFromAlma($mms_id, $holding_id)
populates the record from Alma
Definition: grima-lib.php:2641
loadFromAlma($set_id)
populate set with info from Alma
Definition: grima-lib.php:3423
offsetUnset($offset)
Definition: grima-lib.php:1328
static RenameUser( $username, $institution, $newusername)
Definition: grima-lib.php:4063
deleteFromAlma($override="false", $holdings="retain")
delete record from Alma
Definition: grima-lib.php:3049
loadFromServiceListNode($node)
populate item record from the information in an ServiceList node
Definition: grima-lib.php:3165
session_save($result)
Definition: grima-lib.php:55
deleteAllPortfolios($bib_treat)
delete all portfolios from the service
Definition: grima-lib.php:3213
putHolding($mms_id, $holding_id, $holding)
Update Holdings Record - replace the holdings record in Alma.
Definition: grima-lib.php:499
static isStateless()
Definition: grima-lib.php:3914
updateAlma()
update holding record in Alma
Definition: grima-lib.php:2732
getSetMembers($set_id, $limit=10, $offset=0)
get the members of a set, IF IT WORKS?!?!
Definition: grima-lib.php:1236
getElectronicCollection($collection_id)
Retrieve Electronic Collection - retrieve a collection record from Alma.
Definition: grima-lib.php:912
open( $save_path, $session_name)
Definition: grima-lib.php:4225
$plaintext
Definition: encrypt.php:2
loadPersistentValues($obj)
load persistent values into the form
Definition: grima-lib.php:1586
offsetExists($offset)
Definition: grima-lib.php:1652
updateAlma()
replaces the Portfolio in Alma
Definition: grima-lib.php:3334
class Job
Definition: grima-lib.php:3773
getLCCallNumber()
get LC call number, giving pref to 090 and later fields
Definition: grima-lib.php:2448
addToAlmaBib($mms_id)
adds a new holding record to the specified bib
Definition: grima-lib.php:2710
check_input()
Definition: grima-lib.php:1457
offsetGet($offset)
Definition: grima-lib.php:1663
loadFromAlmaX($portfolio_id)
Definition: grima-lib.php:3312
offsetSet($offset, $value)
Definition: grima-lib.php:1324
class ElectronicPortfolio
Definition: grima-lib.php:3248
postElectronicPortfolioOnBib($mms_id, $portfolio, $update=true)
Create Electronic Portfolio - add a new portfolio to Alma Bib.
Definition: grima-lib.php:793
usage()
Definition: grima-lib.php:1510
updateDB()
Definition: grima-lib.php:4157
getElectronicPortfoliosForService($collection_id, $service_id, $limit, $offset)
Retrieve Portfolios - retrieve a list of portfolios from Alma.
Definition: grima-lib.php:866
loadFromAlma($code)
populate library with info from Alma
Definition: grima-lib.php:3586
postJob($job_id, $op, $job)
Submit a manual or scheduled job.
Definition: grima-lib.php:1283
static SetCurrentUser( $username, $password, $institution='')
Definition: grima-lib.php:4117
loadFromItemListNode($node)
populate item record from the information in an ItemList node
Definition: grima-lib.php:2991
class Holding
Definition: grima-lib.php:2579
offsetExists($offset)
Definition: grima-lib.php:1894
getItem($mms_id, $holding_id, $item_pid)
Retrieve Item and print label information.
Definition: grima-lib.php:584
getElectronicPortfolio($collection_id, $service_id, $portfolio_id)
Retrieve Portfolio - retrieve a portfolio record from Alma.
Definition: grima-lib.php:721
updateAlma()
replace item record in Alma
Definition: grima-lib.php:3031
setCallNumber($h, $i, $ind1)
Definition: grima-lib.php:2774
getHoldings()
populate holdings property with Holdings items
Definition: grima-lib.php:2328
static LogoutCurrentUser()
Definition: grima-lib.php:4127
getIterator()
Definition: grima-lib.php:3976
getHoldingsList($mms_id)
Retrieve Holdings list - download brief descriptions of holdings for the bib.
Definition: grima-lib.php:418
deleteElectronicPortfolio($collection_id, $service_id, $portfolio_id)
Delete Electronic Portfolio - delete portfolio from Alma.
Definition: grima-lib.php:842
getMarcFields($tag)
get fields for the given MARC tag
Definition: grima-lib.php:2043
gc($maxlifetime)
Definition: grima-lib.php:4246
offsetSet($offset, $value)
Definition: grima-lib.php:3139
static isEmpty()
Definition: grima-lib.php:3905
$server
Definition: grima-lib.php:16
offsetGet($offset)
Definition: grima-lib.php:2182
__construct()
Definition: grima-lib.php:83
getFields($tag)
get fields for the given MARC tag
Definition: grima-lib.php:2030
loadFromAlmaBCorX($id)
populates item record from Alma using either identifier
Definition: grima-lib.php:2974
deleteFromAlma($bib_treat="retain")
delete portfolio from Alma
Definition: grima-lib.php:3346
session_init( $force=false)
Definition: grima-lib.php:21
tableExists($pdo, $table)
Definition: grima-lib.php:3877
hasInventory()
populate holdings property with Holdings items
Definition: grima-lib.php:2283
fromXML($xml)
interpret XML to determine form fields and behavior
Definition: grima-lib.php:1600
deleteFromDB()
Definition: grima-lib.php:4182
appendInnerXML( $elt, $xmlString)
Definition: grima-util.php:20
static getOneLibraryLocation($otherthan=array())
get one library configured by the institution
Definition: grima-lib.php:3689
addParameter($name, $value)
add a parameter to the job
Definition: grima-lib.php:3795
loadFromPortfolioListNode($node)
populate item record from the information in an PortfolioList node
Definition: grima-lib.php:3324
setup_splat()
Definition: grima-lib.php:1344
deleteFromAlma()
delete set from Alma
Definition: grima-lib.php:3497
addToAlmaHoldingX($holding_id)
add new item record to holding in Alma
Definition: grima-lib.php:3020
getItemList()
populates itemList property from Alma
Definition: grima-lib.php:2819
deleteFromAlma()
delete the holding record from Alma
Definition: grima-lib.php:2742
loadFromAlmaBarcode($barcode)
populates item record from Alma, using barcode
Definition: grima-lib.php:2961
getPortfolios()
get full portfolios for the bib
Definition: grima-lib.php:2389
session_destroy()
Definition: grima-lib.php:65
putElectronicPortfolioOnBib($mms_id, $portfolio_id, $portfolio)
Update Electronic Portfolio - update portfolio in Alma.
Definition: grima-lib.php:818
offsetGet($offset)
Definition: grima-lib.php:3964
check_login()
Definition: grima-lib.php:1384
get_input_param($param)
Definition: grima-lib.php:1484
Shared access to the database for GrimaUser and GrimaInstitution.
Definition: grima-lib.php:3896
offsetSet($offset, $value)
Definition: grima-lib.php:1915
grima-lib.php - a library for running API calls in Alma
Definition: grima-lib.php:15
__construct($type, $message)
Definition: grima-lib.php:1548
getenvWithFileFallbackAndDefault( $env_name, $default=false)
Definition: grima-util.php:52
do_redirect($url)
Definition: grima-util.php:45
print_success()
Definition: grima-lib.php:1374
getElectronicPortfoliosForBib($mms_id, $limit, $offset)
Retrieve Portfolios list (Bib) - retrieve a list of portfolios from Alma.
Definition: grima-lib.php:890
loadFromAlma($portfolio_id)
populate portfolio with info from Alma
Definition: grima-lib.php:3307
The base class for most "grimas".
Definition: grima-lib.php:1306
$grima
Definition: grima-lib.php:4293
test()
Definition: grima-xmlbag.php:192
static FromArray( $data)
Definition: grima-lib.php:4021
Interface to the GrimaDB database to check password and get institution&#39;s apikey. ...
Definition: grima-lib.php:4020
static getOneLibrary($otherthan=array())
get one library configured by the institution
Definition: grima-lib.php:3620
offsetSet($offset, $value)
Definition: grima-lib.php:2203
static GetCurrentUser()
Definition: grima-lib.php:4112
appendInAlma($setToAppend)
add elements to a set
Definition: grima-lib.php:3528
postElectronicPortfolio($collection_id, $service_id, $portfolio)
Create Electronic Portfolio - add a new portfolio to Alma.
Definition: grima-lib.php:768
getAnalytics($path, $filter, $limit=25, $token=null)
Definition: grima-lib.php:1254
print_form()
Definition: grima-lib.php:1369
class ElectronicService
Definition: grima-lib.php:3123
class Item
Definition: grima-lib.php:2869
addDataField($tag, $indicators, $subfields)
add a data field to the MARC record using Normac
Definition: grima-lib.php:1970
offsetGet($offset)
Definition: grima-lib.php:1901
__construct($field)
create a new GrimaFormField
Definition: grima-lib.php:1837
getHoldingsList()
populate holdingsList property with info from Alma
Definition: grima-lib.php:2361
offsetSet($offset, $value)
Definition: grima-lib.php:3968
__construct()
Definition: grima-lib.php:1332
deleteFromAlma()
deletes the Bib from Alma
Definition: grima-lib.php:2258
Simple template engine.
Definition: grima-splats.php:12
__construct()
create new blank Alma Object
Definition: grima-lib.php:1887
offsetExists($offset)
Definition: grima-lib.php:3960
postHolding($mms_id, $holding)
Create holding record - add a new holdings record to a bib.
Definition: grima-lib.php:474
__construct($id, $description)
Definition: grima-lib.php:3562
loadFromAlma($mms_id)
Definition: grima-lib.php:2491
Methods for fields and subfields in Alma&#39;s MARCXML.
Definition: grima-lib.php:1935
getMmsIfNeeded()
populates the MMS ID if not already known
Definition: grima-lib.php:2680
addToAlmaService($collection_id, $service_id)
populate item record from the information in an PortfolioList node : Insufficient permission ...
Definition: grima-lib.php:3279
$xml
Definition: grima-lib.php:3375
getLibrary($libraryCode)
Retrieve a Library - retrieve a Library from Alma.
Definition: grima-lib.php:988
Interface to the GrimaDB database to retrieve API keys.
Definition: grima-lib.php:3988
getSubfieldValues($tag, $code)
get subfield value
Definition: grima-lib.php:2073
addToAlma()
adds Set to Alma, updates object with current Alma version
Definition: grima-lib.php:3411
booly($str, $default='undefined')
return boolean meaning of common terms ("true","on","1","yes")
Definition: grima-lib.php:1811
postBib($bib)
Create Record - adds a new bib record to Alma.
Definition: grima-lib.php:342
getSet($set_id)
Retrieve a Set - retrieve a Set from Alma.
Definition: grima-lib.php:1091
Base class for objects returned from alma APIs (mostly just array access)
Definition: grima-lib.php:1878
loadFromAlma($mms_id)
populates the bib with a record from Alma
Definition: grima-lib.php:2225
static call($grimaname, $args=array())
Definition: grima-lib.php:1530
static getAllLibraries()
get all libraries configured for the institution
Definition: grima-lib.php:3598
$holdingsList
Definition: grima-lib.php:2160
$itemList
Definition: grima-lib.php:2580
checkForErrorMessage($xml)
checks for errorMessage tag, throws exceptions
Definition: grima-lib.php:288
loadFromAlmaX($item_pid)
populates item record from Alma, only needs item_pid
Definition: grima-lib.php:2950
put($url, $URLparams, $QSparams, $body)
general function for PUT (update) API calls
Definition: grima-lib.php:264
__construct()
make sure it exists
Definition: grima-lib.php:1637
retrieveAllPortfolios()
Definition: grima-lib.php:3174
addToDB()
Definition: grima-lib.php:4132
class HoldingsListEntry
Definition: grima-lib.php:2509
__construct($node, $mms_id)
Definition: grima-lib.php:2520
addMessage($type, $message)
Definition: grima-lib.php:1396
getElectronicPortfolioFromBib($mms_id, $portfolio_id)
Retrieve Portfolio - retrieve a portfolio record from Alma.
Definition: grima-lib.php:744
linkedToCZ()
is the bib linked to the community zone?
Definition: grima-lib.php:2302
static RunIt()
Definition: grima-lib.php:1423
postSet($set)
Create a Set - add a new set to Alma.
Definition: grima-lib.php:1112
read($session_id)
Definition: grima-lib.php:4251
loadFromAlma($collection_id)
load record from Alma
Definition: grima-lib.php:3080
$ciphertext
Definition: encrypt.php:11
A thin wrapper around a message and urgency level.
Definition: grima-lib.php:1543
getItemList()
populates itemList property from Alma
Definition: grima-lib.php:2413
getPortfolioList()
populate portfolioList property with brief info from Alma
Definition: grima-lib.php:2370
deleteField($tag)
delete all $tag fields from the MARC record
Definition: grima-lib.php:2090
join_paths(... $paths)
Definition: grima-util.php:156
normacToXML()
convert normac fields back to XML
Definition: grima-lib.php:1985
offsetUnset($offset)
Definition: grima-lib.php:1696
class SetMember
Definition: grima-lib.php:3558
createFromImport($job_id, $population)
create a new set in Alma based on a job that just ran
Definition: grima-lib.php:3436
class ElectronicCollection
Definition: grima-lib.php:3061
replaceOrAddSubfield($tag, $code, $value)
replace or add subfield value in marc
Definition: grima-lib.php:2124
bib records returned from alma
addToAlmaBib($mms_id)
add portfolio to Bib in Alma : Insufficient permission
Definition: grima-lib.php:3294
addControlField($tag, $data)
add a data field to the MARC record using Normac
Definition: grima-lib.php:1954
static getDb()
Definition: grima-lib.php:3919
__construct($mms_id, $holding_id, $limit=-1)
Definition: grima-lib.php:2540
loadFromAlma($libraryCode, $locationCode)
populate location with info from Alma
Definition: grima-lib.php:3760
destroy($session_id)
Definition: grima-lib.php:4237
getItemBC($barcode)
Retrieve Item and print label information (by barcode))
Definition: grima-lib.php:608
get_title_proper()
a tidy title proper
Definition: grima-lib.php:2423
getMembers($limit=-1)
Definition: grima-lib.php:3470
$description
Definition: grima-lib.php:3560
getItems()
populate items property with Items objects ## XXX TEST
Definition: grima-lib.php:2808
destroy()
remove the grima data store from Alma
Definition: grima-lib.php:1766
class ItemList
Definition: grima-lib.php:2537
class AnalyticsReport
Definition: grima-lib.php:3824
getOneLocation($otherthan=array())
get one location configured for the library
Definition: grima-lib.php:3665
addMember($id)
add member to a local set
Definition: grima-lib.php:3510
offsetUnset($offset)
Definition: grima-lib.php:1921
arrayToXml($array, &$xml)
Definition: grima-util.php:77
static getMmsFromHoldingID($holding_id)
populates the MMS ID
Definition: grima-lib.php:2653
getAllLocations($libraryCode)
Retrieve Locations - retrieve all locations in a library.
Definition: grima-lib.php:1030
class Location
Definition: grima-lib.php:3722
class HoldingsList
Definition: grima-lib.php:2475
addToAlma()
adds record as a new record to Alma, updates Bib with current Alma version
Definition: grima-lib.php:2236
getLocation($libraryCode, $locationCode)
Retrieve Location - retrieve a Library Location from Alma.
Definition: grima-lib.php:1059
updateAlma()
replaces the Bib in Alma
Definition: grima-lib.php:2246
deleteItem($mms_id, $holding_id, $item_pid, $override="false", $holdings="retain")
Withdraw Item - delete an item record from Alma.
Definition: grima-lib.php:685
getItemList($mms_id, $holding_id, $limit, $offset)
Retrieve Items list - retrieve the items list from a holding or bib from Alma.
Definition: grima-lib.php:555
getItemList($limit=-1)
Definition: grima-lib.php:2526
loadFromAlma($collection_id, $service_id)
load record from Alma
Definition: grima-lib.php:3152
deleteSet($set_id)
Delete a Set - delete a set (not its items) from Alma.
Definition: grima-lib.php:1212
updateAlma()
replace the collection record in Alma
Definition: grima-lib.php:3092
deleteSubfieldMatching($tag, $code, $regex)
delete subfields matching a regex
Definition: grima-lib.php:2105
__construct($mms_id=null)
Definition: grima-lib.php:2485
getItems()
populate items property with Items objects ## XXX TEST
Definition: grima-lib.php:2402
$ds_barcode
Definition: grima-lib.php:1620
loadFromAlmaX($holding_id)
populates the record from Alma - only requires holding_id
Definition: grima-lib.php:2695
loadValues($obj)
load values into the form
Definition: grima-lib.php:1571
setup()
create the record structure in Alma
Definition: grima-lib.php:1715
runOnElements($function, $args=array(), $filter=null, $filter_args=array())
run a function on every element in the set
Definition: grima-lib.php:3450
getAllLocations()
get all locations configured for the library
Definition: grima-lib.php:3643
This object lets grima store data in a catalog record.
Definition: grima-lib.php:1625
static LookupUser($username, $institution='', $password=FALSE)
Definition: grima-lib.php:4029
offsetGet($offset)
Definition: grima-lib.php:1320
moveToBib($mms_id)
moves the holding from one bib to another – only for empty holdings!
Definition: grima-lib.php:2798
offsetGet($offset)
Definition: grima-lib.php:2592
createSetFromImport($job_instance_id, $population)
Create a Set from an import job.
Definition: grima-lib.php:1161
$xml
Definition: grima-lib.php:2582
run()
Definition: grima-lib.php:1400
putElectronicCollection($collection_id, $collection)
Update Electronic Collection Record - updates the copy of the collection in Alma. ...
Definition: grima-lib.php:935
class Set IN PROGRESS
Definition: grima-lib.php:3374
print_failure()
Definition: grima-lib.php:1379
deleteTreeFromAlma()
delete the holding and all of its items
Definition: grima-lib.php:2830
appendField($tag, $ind1, $ind2, $subfields)
add a field to the MARC record
Definition: grima-lib.php:2011
getElectronicServices($collection_id)
Retrieve Electronic Services - retrieve a list of services from a collection in Alma.
Definition: grima-lib.php:959
$itemList
Definition: grima-lib.php:2163
getServices()
load record from Alma
Definition: grima-lib.php:3103
Wrapper for each field in a form, keeping track of its properties.
Definition: grima-lib.php:1786
post($url, $URLparams, $QSparams, $body)
general function for POST (create) API calls
Definition: grima-lib.php:249
postItem($mms_id, $holding_id, $item)
Create Item - add a new item to a holding in Alma.
Definition: grima-lib.php:634
static init()
Definition: grima-lib.php:3901