Grima 2018-08
Whispering into Alma's ear
grima-lib.php
Go to the documentation of this file.
1<?php
16require_once "grima-util.php";
17require_once "grima-xmlbag.php";
18require_once "grima-splats.php";
19require_once "MARC21.php";
20
21if (!defined('sodium_crypto_secretbox')) {
22 @include_once "vendor/paragonie/sodium_compat/autoload.php";
23}
24
25
26// {{{ class Grima
30class Grima
31{
32 public $server;
33 public $apikey;
34
35 // {{{ session management
36
37 function sessionInit($force = false)
38 {
39 // Session Module
40 $session_module_name = getenvWithFileFallbackAndDefault(
41 'SESSION_MODULE',
42 'encrypted_cookie'
43 );
44 if ($session_module_name=="none") {
45 return;
46 } else if ($session_module_name=="encrypted_cookie") {
47 $session_key = base64_decode(
49 'SESSION_KEY', "base64EncodedGrimaKeyOf32BytesTotallyRandom="
50 )
51 );
52 session_set_save_handler(new EncryptedCookieSession($session_key));
53 ini_set("session.use_cookies", false);
54 } else if ($session_module_name) {
55 $new_name = session_module_name($session_module_name);
56 if ($new_name != $session_module_name) {
57 session_name("grima_failsafe");
58 session_start();
59 return;
60 }
61 }
62 // Session Name
63 $session_name = getenvWithFileFallbackAndDefault('SESSION_NAME', 'grima');
64 session_name($session_name);
65 // Session Path
67 'SESSION_PATH', join_paths(sys_get_temp_dir(), 'grima')
68 );
69 if (session_module_name()==='files') {
70 @mkdir($session_path, 0700, true);
71 }
72 session_save_path($session_path);
73 // Session lifetime
74 session_set_cookie_params(365*24*60*60); // one year
75 ini_set('session.gc_maxlifetime', 525600*60); // of love
76 // Start session
77 session_start();
78 }
79
80 function sessionSave($result)
81 {
82 $session_module_name = getenvWithFileFallbackAndDefault(
83 'SESSION_MODULE', 'encrypted_cookie'
84 );
85 if ($session_module_name === "none") {
86 return;
87 }
88 $this->sessionInit(true);
89 foreach ($result as $key => $value) {
90 $_SESSION[$key] = $value;
91 }
92 session_write_close();
93 }
94
95 function sessionDestroy()
96 {
97 $session_module_name = getenvWithFileFallbackAndDefault(
98 'SESSION_MODULE', 'encrypted_cookie'
99 );
100 if ($session_module_name === "none") {
101 return;
102 }
103 $_SESSION = [];
104 if (ini_get("session.use_cookies")) {
105 $params = session_get_cookie_params();
106 $params['expires'] = time() - 42000;
107 unset($params['lifetime']);
108 setcookie(session_name(), '', $params);
109 }
110 $this->sessionInit(true);
111 $_SESSION = [];
112 session_destroy();
113 }
114
115 // }}}
116
117 // {{{ config
118 function __construct()
119 {
120
121 $this->sessionInit();
122
123 $this->apikey =
124 isset($_REQUEST['apikey']) && $_REQUEST['apikey'] ? $_REQUEST['apikey'] : (
125 isset($_SESSION['apikey']) && $_SESSION['apikey'] ? $_SESSION['apikey'] : (
126 getenvWithFileFallbackAndDefault('apikey', '') ) );
127
128 $this->server =
129 isset($_REQUEST['server']) && $_REQUEST['server'] ? $_REQUEST['server'] : (
130 isset($_SESSION['server']) && $_SESSION['server'] ? $_SESSION['server'] : (
131 getenvWithFileFallbackAndDefault('server', '') ) );
132
133 if (strlen($this->server)==2) {
134 $this->server = "https://api-" . $this->server . ".hosted.exlibrisgroup.com";
135 }
136
137 if (!preg_match('!https://api-[a-z]*\.hosted\.exlibrisgroup\.com!', $this->server)) {
138 $this->server = "";
139 }
140
141 if ($this->apikey=='EDITME') {
142 $this->apikey = "";
143 }
144
145 if ($this->apikey && $this->server) {
146 $_SESSION['apikey'] = $this->apikey;
147 $_SESSION['server'] = $this->server;
148 }
149
150 session_write_close();
151
152 return false;
153 }
154
155 // }}}
156
157 // {{{ REST - get/post/put/delete
158
159 // {{{ request - general function for API calls
171 function request($method, $url, $URLparams, $QSparams, $body = null)
172 {
173 $headers = array(
174 'Authorization: apikey ' . urlencode($this->apikey),
175 "Accept: application/xml",
176 );
177
178 // Change "/almaws/v1/users/{user_id}" to "/almaws/v1/users/${URLparams['user_id']}"
179 foreach ($URLparams as $k => $v) {
180 if (!$v) {
181 throw new Exception("URL parameter $k is empty in $method $url");
182 }
183 $url = str_replace('{'.$k.'}', urlencode($v), $url);
184 }
185
186 // Include APIKey in url
187 $url = $this->server . $url . '?apikey=' . urlencode($this->apikey) . '&format=xml';
188
189 // Include query string parameters too
190 foreach ($QSparams as $k => $v) {
191 $url .= "&$k=" . urlencode($v);
192 }
193
194 // Include XML based body as well
195 // XXX: detect if JSON is more appropriate?
196 if ($body) {
197 $bodyxml = $body->saveXML();
198 $headers[] = 'Content-Type: application/xml';
199 }
200
201 $ch = curl_init();
202 curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
203 curl_setopt($ch, CURLOPT_URL, $url);
204 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
205 curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
206
207 $headerCapture = new HeaderCapture();
208 curl_setopt($ch, CURLOPT_HEADERFUNCTION, [&$headerCapture,"capture"]);
209
210 // ExLibris / Debian interoperability problem
211 // https://github.com/openssl/openssl/issues/7126
212 // error_log("OPENSSL_VERSION_NUMBER=" .dechex(OPENSSL_VERSION_NUMBER));
213 // 0x01000214f on reclaim
214 if (OPENSSL_VERSION_NUMBER >= 0x010101000) {
215 // debian 10 workaround until Ex Libris patches their servers
216 curl_setopt($ch, CURLOPT_SSL_CIPHER_LIST, "DEFAULT@SECLEVEL=1");
217 }
218
219
220 if (isset($bodyxml)) {
221 curl_setopt($ch, CURLOPT_POSTFIELDS, $bodyxml);
222 }
223 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
224
225 /* XXX: DEBUG */
226 $today = date("Y-m-d-H_i_s");
227 $debug = fopen("/tmp/grima/debug/curl-$today.log", 'a+');
228 fwrite(
229 $debug, serialize(
230 [
231 "REQUEST_URI"=>isset($_SERVER["REQUEST_METHOD"])?($_SERVER["REQUEST_METHOD"]." ".$_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"]):"cli",
232 "_REQUEST"=>$_REQUEST,
233 "request_body"=>$body,
234 ]
235 )
236 ."\n"
237 );
238
239 curl_setopt($ch, CURLOPT_VERBOSE, true);
240 curl_setopt($ch, CURLOPT_STDERR, $debug);
241 /* */
242
243 $response = curl_exec($ch);
244 // error_log( print_r($headerCapture,true) );
245 /* */
246 fwrite($debug, serialize(["response"=>$response])."\n\n");
247 fclose($debug);
248 /* */
249
250 $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
251 if (curl_errno($ch)) {
252 throw new Exception("Network error: " . curl_error($ch));
253 }
254 curl_close($ch);
255 if ($code == 204) {
256 return; // no body
257 }
258
259 $xml = new DOMDocument();
260 $xml->formatOutput = true;
261 try {
262 if (preg_match(',^application/json,', $headerCapture->headers['content-type'])) {
263 $array = json_decode($response, true);
264 $xml->loadXML('<?xml version="1.0"?' . '><json/>');
265 arrayToXml($array, $xml->documentElement);
266 } else if (!preg_match('/^</', $response)) {
267 $xml->loadXML(
268 '<?xml version="1.0"?' . '><html><body><h1>Not xml</h1><pre>'
269 . htmlspecialchars($response).
270 '</pre></body></html>'
271 );
272 } else {
273 $xml->loadXML($response);
274 }
275 } catch (Exception $e) {
276 throw new Exception("Malformed XML from Alma:\n$method $url -> $code\n$e");
277 }
278
279 $xml->rawText = $response;
280 $xml->httpCode = $code;
281 $xml->headers = $headerCapture->headers;
282 $xml->rawHeaders = $headerCapture->rawHeaders;
283
284 return $xml;
285 }
286 // }}}
287
288 // {{{ get - general function for GET (retrieve) API calls
298 function get($url, $URLparams, $QSparams)
299 {
300 return $this->request("GET", $url, $URLparams, $QSparams, null);
301 }
302 // }}}
303
304 // {{{ post - general function for POST (create) API calls
314 function post($url, $URLparams, $QSparams, $body)
315 {
316 return $this->request("POST", $url, $URLparams, $QSparams, $body);
317 }
318 // }}}
319
320 // {{{ put - general function for PUT (update) API calls
330 function put($url, $URLparams, $QSparams, $body)
331 {
332 return $this->request("PUT", $url, $URLparams, $QSparams, $body);
333 }
334 // }}}
335
336 // {{{ delete - general function for DELETE API calls
346 function delete($url, $URLparams, $QSparams)
347 {
348 return $this->request("DELETE", $url, $URLparams, $QSparams, null);
349 }
350 // }}}
351
352 // {{{ checkForErrorMessage - checks for errorMessage tag, throws exceptions
357 function checkForErrorMessage($xml)
358 {
359 if ($xml instanceOf DomDocument) {
360 $xpath = new DomXpath($xml);
361 $xpath->registerNamespace("err", "http://com/exlibris/urm/general/xmlbeans");
362 $error = $xpath->query('//err:errorMessage');
363 if ($error->length > 0) {
364 throw new Exception("Alma says: " . $error[0]->nodeValue);
365 }
366 }
367 }
368 // }}}
369
370 // }}}
371
372 //{{{Bib APIs
380 // {{{ getBib (Retrieve Bib)
394 function getBib($mms_id, $view = 'full', $expand = 'None')
395 {
396 $ret = $this->get(
397 '/almaws/v1/bibs/{mms_id}',
398 array('mms_id' => $mms_id),
399 array('view' => $view, 'expand' => $expand)
400 );
401 $this->checkForErrorMessage($ret);
402 return $ret;
403 }
404 // }}}
405
406 // {{{ postBib (Create Record)
418 function postBib($bib)
419 {
420 $ret = $this->post(
421 '/almaws/v1/bibs',
422 array(),
423 array(),
424 $bib
425 );
426 $this->checkForErrorMessage($ret);
427 return $ret;
428 }
429 // }}}
430
431 // {{{ putBib (Update Bib Record)
444 function putBib($mms_id, $bib)
445 {
446 $ret = $this->put(
447 '/almaws/v1/bibs/{mms_id}',
448 array('mms_id' => $mms_id),
449 array(),
450 $bib
451 );
452 $this->checkForErrorMessage($ret);
453 return $ret;
454 }
455 // }}}
456
457 // {{{ deleteBib (Delete Bib Record)
469 function deleteBib($mms_id, $override = 'false')
470 {
471 $ret = $this->delete(
472 '/almaws/v1/bibs/{mms_id}',
473 array('mms_id' => $mms_id),
474 array('override' => $override)
475 );
476 $this->checkForErrorMessage($ret);
477 }
478 // }}}
479
483 //}}}
484
485 //{{{Holdings List APIs
493 // {{{ grima -> getHoldingsList (Retrieve Holdings list)
506 function getHoldingsList($mms_id)
507 {
508 $ret = $this->get(
509 '/almaws/v1/bibs/{mms_id}/holdings',
510 array('mms_id' => $mms_id),
511 array()
512 );
513 $this->checkForErrorMessage($ret);
514 return $ret;
515 }
516 // }}}
517
521 //}}}
522
523 //{{{Holding APIs
531 // {{{ getHolding (Retrieve Holdings Record)
544 function getHolding($mms_id, $holding_id)
545 {
546 $ret = $this->get(
547 '/almaws/v1/bibs/{mms_id}/holdings/{holding_id}',
548 array(
549 'mms_id' => $mms_id,
550 'holding_id' => $holding_id
551 ),
552 array()
553 );
554 $this->checkForErrorMessage($ret);
555 return $ret;
556 }
557 // }}}
558
559 // {{{ grima -> postHolding (Create holding record)
572 function postHolding($mms_id, $holding)
573 {
574 $ret = $this->post(
575 '/almaws/v1/bibs/{mms_id}/holdings',
576 array('mms_id' => $mms_id),
577 array(),
578 $holding
579 );
580 $this->checkForErrorMessage($ret);
581 return $ret;
582 }
583 // }}}
584
585 // {{{ putHolding (Update Holdings Record)
599 function putHolding($mms_id, $holding_id, $holding)
600 {
601 $ret = $this->put(
602 '/almaws/v1/bibs/{mms_id}/holdings/{holding_id}',
603 array('mms_id' => $mms_id, 'holding_id' => $holding_id),
604 array(),
605 $holding
606 );
607 $this->checkForErrorMessage($ret);
608 return $ret;
609 }
610 // }}}
611
612 // {{{ grima -> deleteHolding (Delete Holdings Record)
625 function deleteHolding($mms_id, $holding_id, $override = 'false')
626 {
627 $ret = $this->delete(
628 '/almaws/v1/bibs/{mms_id}/holdings/{holding_id}',
629 array(
630 'mms_id' => $mms_id,
631 'holding_id' => $holding_id
632 ),
633 array('override' => $override)
634 );
635 $this->checkForErrorMessage($ret);
636 }
637 // }}}
638
642 //}}}
643
644 //{{{Item List APIs
652 // {{{ getItemList (Retrieve Items list)
667 function getItemList($mms_id, $holding_id, $limit, $offset)
668 {
669 $ret = $this->get(
670 '/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items', array('mms_id' => $mms_id, 'holding_id' => $holding_id),
671 array('limit' => $limit, 'offset' => $offset)
672 );
673 $this->checkForErrorMessage($ret);
674 return $ret;
675 }
676 // }}}
677
681 //}}}
682
683 //{{{Item APIs
691 // {{{ getItem (Retrieve Item and print label information)
704 function getItem($mms_id, $holding_id, $item_pid)
705 {
706 $ret = $this->get(
707 '/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items/{item_pid}', array(
708 'mms_id' => $mms_id,
709 'holding_id' => $holding_id,
710 'item_pid' => $item_pid
711 ),
712 array()
713 );
714 $this->checkForErrorMessage($ret);
715 return $ret;
716 }
717 // }}}
718
719 // {{{ getItemBC (Retrieve Item and print label information (by barcode))
730 function getItemBC($barcode)
731 {
732 $ret = $this->get(
733 '/almaws/v1/items',
734 array(),
735 array(
736 'item_barcode' => $barcode,
737 )
738 );
739 $this->checkForErrorMessage($ret);
740 return $ret;
741 }
742 // }}}
743
744 // {{{ postItem (Create Item)
758 function postItem($mms_id, $holding_id, $item)
759 {
760 $ret = $this->post(
761 '/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items',
762 array('mms_id' => $mms_id, 'holding_id' => $holding_id),
763 array(),
764 $item
765 );
766 $this->checkForErrorMessage($ret);
767 return $ret;
768 }
769 // }}}
770
771 // {{{ putItem (Update Item information)
786 function putItem($mms_id, $holding_id, $item_pid, $item)
787 {
788 $ret = $this->put(
789 '/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items/{item_pid}',
790 array('mms_id' => $mms_id, 'holding_id' => $holding_id, 'item_pid' => $item_pid),
791 array(),
792 $item
793 );
794 return $ret;
795 }
796 // }}}
797
798 // {{{ deleteItem (Withdraw Item)
813 function deleteItem($mms_id, $holding_id, $item_pid, $override = "false",
814 $holdings = "retain"
815 ) {
816 $ret = $this->delete(
817 '/almaws/v1/bibs/{mms_id}/holdings/{holding_id}/items/{item_pid}', array(
818 'mms_id' => $mms_id,
819 'holding_id' => $holding_id,
820 'item_pid' => $item_pid
821 ), array(
822 'override' => $override,
823 'holdings' => $holdings
824 )
825 );
826 $this->checkForErrorMessage($ret);
827 }
828 // }}}
829
833 //}}}
834
835 //{{{Electronic APIs
843 // {{{ getElectronicPortfolio (Retrieve Portfolio)
857 function getElectronicPortfolio($collection_id, $service_id, $portfolio_id)
858 {
859 $ret = $this->get(
860 '/almaws/v1/electronic/e-collections/{collection_id}/e-services/{service_id}/portfolios/{portfolio_id}',
861 array('collection_id' => $collection_id, 'service_id' => $service_id, 'portfolio_id' => $portfolio_id),
862 array()
863 );
864 $this->checkForErrorMessage($ret);
865 return $ret;
866 }
867 // }}}
868
869 // {{{ getElectronicPortfolioFromBib (Retrieve Portfolio)
882 function getElectronicPortfolioFromBib($mms_id, $portfolio_id)
883 {
884 $ret = $this->get(
885 '/almaws/v1/bibs/{mms_id}/portfolios/{portfolio_id}',
886 array('mms_id' => $mms_id, 'portfolio_id' => $portfolio_id),
887 array()
888 );
889 $this->checkForErrorMessage($ret);
890 return $ret;
891 }
892 // }}}
893
894 // {{{ postElectronicPortfolio (Create Electronic Portfolio) #XXX
908 function postElectronicPortfolio($collection_id, $service_id, $portfolio)
909 {
910 $ret = $this->post(
911 '/almaws/v1/electronic/e-collections/{collection_id}/e-services/{service_id}/portfolios',
912 array('collection_id' => $collection_id, 'service_id' => $service_id),
913 array(),
914 $portfolio
915 );
916 $this->checkForErrorMessage($ret);
917 return $ret;
918 }
919 // }}}
920
921 // {{{ postElectronicPortfolioOnBib (Create Electronic Portfolio on Bib)
935 function postElectronicPortfolioOnBib($mms_id, $portfolio, $update = true)
936 {
937 $ret = $this->post(
938 '/almaws/v1/bibs/{mms_id}/portfolios/',
939 array('mms_id' => $mms_id),
940 array(),
941 $portfolio
942 );
943 $this->checkForErrorMessage($ret);
944 if ($update) {
945 $portfolio->loadXML($ret->saveXML());
946 }
947 return $ret;
948 }
949 // }}}
950
951 // {{{ putElectronicPortfolioOnBib (Update Portfolio for a Bib)
964 function putElectronicPortfolioOnBib($mms_id, $portfolio_id, $portfolio)
965 {
966 $ret = $this->put(
967 '/almaws/v1/bibs/{mms_id}/portfolios/{portfolio_id}',
968 array('mms_id' => $mms_id, 'portfolio_id' => $portfolio_id),
969 array(),
970 $portfolio
971 );
972 $this->checkForErrorMessage($ret);
973 return $ret;
974 }
975 // }}}
976
977 // {{{ deleteElectronicPortfolio (Delete Electronic Portfolio)
990 function deleteElectronicPortfolio($collection_id, $service_id, $portfolio_id)
991 {
992 $ret = $this->delete(
993 '/almaws/v1/electronic/e-collections/{collection_id}/e-services/{service_id}/portfolios/{portfolio_id}',
994 array('collection_id' => $collection_id, 'service_id' => $service_id, 'portfolio_id' => $portfolio_id),
995 array()
996 );
997 $this->checkForErrorMessage($ret);
998 }
999 // }}}
1000
1001 // {{{ getElectronicPortfoliosForService (Retrieve Portfolios)
1016 function getElectronicPortfoliosForService($collection_id, $service_id, $limit, $offset)
1017 {
1018 $ret = $this->get(
1019 '/almaws/v1/electronic/e-collections/{collection_id}/e-services/{service_id}/portfolios',
1020 array('collection_id' => $collection_id, 'service_id' => $service_id),
1021 array('limit' => $limit, $offset = $offset)
1022 );
1023 $this->checkForErrorMessage($ret);
1024 return $ret;
1025 }
1026 // }}}
1027
1028 // {{{ grima -> getElectronicPortfoliosForBib (Retrieve Portfolios)
1042 function getElectronicPortfoliosForBib($mms_id, $limit, $offset)
1043 {
1044 $ret = $this->get(
1045 '/almaws/v1/bibs/{mms_id}/portfolios',
1046 array('mms_id' => $mms_id),
1047 array('limit' => $limit, $offset = $offset)
1048 );
1049 $this->checkForErrorMessage($ret);
1050 return $ret;
1051 }
1052 // }}}
1053
1054 // {{{ getElectronicCollection (Retrieve Electronic Collection)
1066 function getElectronicCollection($collection_id)
1067 {
1068 $ret = $this->get(
1069 '/almaws/v1/electronic/e-collections/{collection_id}',
1070 array('collection_id' => $collection_id),
1071 array()
1072 );
1073 $this->checkForErrorMessage($ret);
1074 return $ret;
1075 }
1076 // }}}
1077
1078 // {{{ putElectronicCollection (Update Electronic Collection)
1091 function putElectronicCollection($collection_id, $collection)
1092 {
1093 $ret = $this->put(
1094 '/almaws/v1/electronic/e-collections/{collection_id}',
1095 array('collection_id' => $collection_id),
1096 array(),
1097 $collection
1098 );
1099 $this->checkForErrorMessage($ret);
1100 return $ret;
1101 }
1102 // }}}
1103
1104 // {{{ getElectronicServices (Retrieve Electronic Services)
1117 function getElectronicServices($collection_id)
1118 {
1119 $ret = $this->get(
1120 '/almaws/v1/electronic/e-collections/{collection_id}/e-services',
1121 array('collection_id' => $collection_id),
1122 array()
1123 );
1124 $this->checkForErrorMessage($ret);
1125 return $ret;
1126 }
1127 // }}}
1128
1132 //}}}
1133
1134 //{{{Library APIs
1142 // {{{ grima -> getLibrary (Retrieve a Library)
1154 function getLibrary($libraryCode)
1155 {
1156 $ret = $this->get(
1157 '/almaws/v1/conf/libraries/{libraryCode}',
1158 array('libraryCode' => $libraryCode),
1159 array()
1160 );
1161 $this->checkForErrorMessage($ret);
1162 return $ret;
1163 }
1164 // }}}
1165
1166 // {{{ grima -> getAllLibraries (Retrieve Libraries)
1178 {
1179 $ret = $this->get(
1180 '/almaws/v1/conf/libraries',
1181 array(),
1182 array()
1183 );
1184 $this->checkForErrorMessage($ret);
1185 return $ret;
1186 }
1187 // }}}
1188
1189 // {{{ getAllLocations (Retrieve Locations)
1200 function getAllLocations($libraryCode)
1201 {
1202 $ret = $this->get(
1203 '/almaws/v1/conf/libraries/{libraryCode}/locations',
1204 array('libraryCode' => $libraryCode),
1205 array()
1206 );
1207 $this->checkForErrorMessage($ret);
1208 return $ret;
1209 }
1210 // }}}
1211
1215 //}}}
1216
1217 //{{{Location APIs
1225 // {{{ getLocation (Retrieve Location)
1237 function getLocation($libraryCode, $locationCode)
1238 {
1239 $ret = $this->get(
1240 '/almaws/v1/conf/libraries/{libraryCode}/locations/{locationCode}',
1241 array(
1242 'libraryCode' => $libraryCode,
1243 'locationCode' => $locationCode,
1244 ),
1245 array()
1246 );
1247 $this->checkForErrorMessage($ret);
1248 return $ret;
1249 }
1250 // }}}
1251
1255 //}}}
1256
1257 //{{{Set APIs
1265 // {{{ grima -> getSet (Retrieve a Set)
1277 function getSet($set_id)
1278 {
1279 $ret = $this->get(
1280 '/almaws/v1/conf/sets/{set_id}',
1281 array('set_id' => $set_id),
1282 array()
1283 );
1284 $this->checkForErrorMessage($ret);
1285 return $ret;
1286 }
1287 // }}}
1288
1289 // {{{ grima -> postSet (Create a Set)
1300 function postSet($set)
1301 {
1302 $ret = $this->post(
1303 '/almaws/v1/conf/sets/',
1304 array(),
1305 array(),
1306 $set
1307 );
1308 $this->checkForErrorMessage($ret);
1309 return $ret;
1310 }
1311 // }}}
1312
1313 // {{{ grima -> postSetManageMembers (Manage Members)
1328 function postSetManageMembers($set_id, $id_type, $op, $set)
1329 {
1330 $ret = $this->post(
1331 '/almaws/v1/conf/sets/{set_id}',
1332 array('set_id' => $set_id),
1333 array('id_type' => $id_type, 'op' => $op),
1334 $set
1335 );
1336 $this->checkForErrorMessage($ret);
1337 return $ret;
1338 }
1339
1340 // {{{ Set -> createSetFromImport (Create a Set)
1353 function createSetFromImport($job_instance_id, $population)
1354 {
1355 // create blank set
1356
1357 $body = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?' . '>
1358<set>
1359 <name>Grima set from ' . $job_instance_id . '</name>
1360 <description>members of ' . $job_instance_id . '</description>
1361 <type desc="Itemized">ITEMIZED</type>
1362 <content desc="All Titles">BIB_MMS</content>
1363 <private desc="No">false</private>
1364</set>';
1365
1366 /*
1367 Content:
1368 BIB_MMS -- all titles
1369 ITEM
1370 PORTFOLIO
1371 IEPA
1372 FILE
1373 AUTHORITY_MMS
1374 IEP
1375 IEE
1376 IED
1377 IEC
1378 */
1379
1380 /*
1381 Population:
1382 */
1383
1384 $bodyxml = new DomDocument();
1385 $bodyxml->loadXML($body);
1386
1387 $ret = $this->post('/almaws/v1/conf/sets', array(), array('job_instance_id' => $job_instance_id, 'population' => $population), $bodyxml);
1388 $this->checkForErrorMessage($ret);
1389 return $ret;
1390
1391 }
1392 // }}}
1393
1394 // {{{ grima -> deleteSet (Delete a Set)
1405 function deleteSet($set_id)
1406 {
1407 $ret = $this->delete(
1408 '/almaws/v1/conf/sets/{set_id}',
1409 array(
1410 'set_id' => $set_id,
1411 ),
1412 array()
1413 );
1414 $this->checkForErrorMessage($ret);
1415 }
1416 // }}}
1417
1418 // {{{ grima -> getSetMembers (Does it work????)
1431 function getSetMembers($set_id, $limit = 10, $offset = 0)
1432 {
1433 $ret = $this->get(
1434 '/almaws/v1/conf/sets/{set_id}/members',
1435 array('set_id' => $set_id),
1436 array('limit' => $limit, 'offset' => $offset)
1437 );
1438 $this->checkForErrorMessage($ret);
1439 return $ret;
1440 }
1441 // }}}
1442
1446 //}}}
1447
1448 //{{{Analytics APIs
1456 // XXX check if blank filter is ok
1457 function getAnalytics($path, $filter, $limit = 25, $token = null)
1458 {
1459 return $this->get(
1460 '/almaws/v1/analytics/reports',
1461 array(),
1462 array('path' => urlencode($path), 'filter' => urlencode($filter),
1463 'limit' => $limit, 'token' => $token)
1464 );
1465 }
1466
1470 //}}}
1471
1472 //{{{Job APIs
1480 // {{{ postJob (Submit a manual or scheduled job)
1494 function postJob($job_id, $op, $job)
1495 {
1496 $ret = $this->post(
1497 '/almaws/v1/conf/jobs/{job_id}',
1498 array('job_id' => $job_id),
1499 array('op' => $op),
1500 $job
1501 );
1502 $this->checkForErrorMessage($ret);
1503 return $ret;
1504 }
1505 // }}}
1506
1510 //}}}
1511
1512
1513}
1514
1515// }}}
1516
1517// {{{ class GrimaTask
1522abstract class GrimaTask implements ArrayAccess
1523{
1524
1525 public $error = false;
1526 public $args = array();
1527 public $el_override = array();
1528
1529 public $auto_args = array();
1530
1531 public $messages = array();
1532
1533 function offsetExists($offset)
1534 {
1535 return isset($this->args[$offset]);
1536 }
1537
1538 function offsetGet($offset)
1539 {
1540 return $this->args[$offset];
1541 }
1542
1543 function offsetSet($offset, $value)
1544 {
1545 $this->args[$offset] = $value;
1546 }
1547
1548 function offsetUnset($offset)
1549 {
1550 unset($this->args[$offset]);
1551 }
1552
1553 function __construct()
1554 {
1555 $webroot = __DIR__;
1556 $base = get_class($this); //basename($_SERVER['PHP_SELF'],'.php');
1557 if (file_exists(join_paths($webroot, $base, "$base.xml")) and (!isset($this->formxml))) {
1558 $this->formxml = file_get_contents(join_paths($webroot, $base, "$base.xml"));
1559 }
1560 if (isset($this->formxml)) {
1561 $this->form = new GrimaForm();
1562 $this->form->fromXML($this->formxml);
1563 }
1564 }
1565
1566 function setup_splat()
1567 {
1568 $webroot = __DIR__;
1569 $basename = get_class($this); //basename($_SERVER['PHP_SELF'],'.php');
1570
1571 $this->splat = new zemowsplat\Splat();
1572
1573 $this->splat->addBases(
1574 array(
1575 "$webroot/$basename/splats", // per-task overrides
1576 "$webroot/splats", // default
1577 )
1578 );
1579
1580 $this->splatVars = array(
1581 'title' => $this->form->title,
1582 'basename' => $basename,
1583 'webroot' => $webroot,
1584 'local_stylesheets' => array('Grima.css'),
1585 'form' => &$this->form,
1586 'messages' => &$this->messages,
1587 'width' => 9,
1588 );
1589 if (isset($this['redirect_url'])) {
1590 $this->splatVars['redirect_url'] = $this['redirect_url'];
1591 }
1592 }
1593
1594 function print_form()
1595 {
1596 $this->form->loadValues($this);
1597 $this->splat->splat('print_form', $this->splatVars);
1598 }
1599
1600 function print_success()
1601 {
1602 $this->form->loadPersistentValues($this);
1603 $this->splat->splat('print_success', $this->splatVars);
1604 }
1605
1606 function print_failure()
1607 {
1608 $this->form->loadValues($this);
1609 $this->splat->splat('print_failure', $this->splatVars);
1610 }
1611
1612 function check_login()
1613 {
1614 global $grima;
1615 if (isset($grima->apikey) and isset($grima->server)
1616 and ($grima->apikey) and ($grima->server)
1617 ) {
1618 return true;
1619 } else {
1620 GrimaTask::call('Login', array("redirect_url"=>$_SERVER['PHP_SELF']));
1621 exit;
1622 return false;
1623 }
1624 }
1625
1626 public function addMessage($type, $message)
1627 {
1628 $this->messages[] = new GrimaTaskMessage($type, $message);
1629 }
1630
1631 public function run()
1632 {
1633 $this->check_login(); // if not logged in, print login form
1634 $this->error = false;
1635 $this->get_input();
1636 $this->setup_splat(); // should happen after form xml read in get_input
1637 if ($this->check_input()) {
1638 try {
1639 $this->do_task();
1640 } catch (Exception $e) {
1641 $this->addMessage('error', $e->getMessage());
1642 $this->error = true;
1643 }
1644 } else {
1645 $this->print_form();
1646 exit;
1647 }
1648 if ($this->error) {
1649 $this->print_failure();
1650 } else {
1651 $this->print_success();
1652 }
1653 }
1654
1655 public static function RunIt()
1656 {
1657 $task = new static();
1658 $task->run();
1659 }
1660
1661 function get_input()
1662 {
1663 if (isset($this->form)) {
1664 if (php_sapi_name() == "cli") { // command line
1665
1666 $param = "";
1667 foreach ($this->form->fields as $field) {
1668 $short = $field->shortname;
1669 if ($field->required) {
1670 $param .= "$short:";
1671 } else {
1672 $param .= "$short::";
1673 }
1674 }
1675
1676 if ($option = getopt($param)) {
1677 foreach ($this->form->fields as $field) {
1678 if (isset($option[$field->shortname])) {
1679 $this[$field->name] = $option[$field->shortname];
1680 }
1681 }
1682 /*
1683 } else {
1684 $this->usage(); exit;
1685 */
1686 }
1687
1688 if (!$this->check_input()) {
1689 $this->usage(); exit;
1690 }
1691
1692 } else { // web
1693 foreach ($this->form->fields as $field) {
1694 if (isset($_REQUEST[$field->name])) {
1695 $this[$field->name] = $_REQUEST[$field->name];
1696 /* sanitize */
1697 }
1698 }
1699 }
1700
1701 } else {
1702 // ??
1703 $this->get_input_param($this->auto_args);
1704 }
1705 }
1706
1707 function check_input()
1708 {
1709 if (isset($this->form)) {
1710 $input_good = true;
1711 foreach ($this->form->fields as $field) {
1712 if ($field->required) {
1713 if (!isset($this[$field->name])
1714 or !($this[$field->name])
1715 ) {
1716 $field->error_condition = "error";
1717 $field->error_message = "Field is required\n";
1718 $input_good = false;
1719 }
1720 }
1721 }
1722 return $input_good;
1723 } else {
1724
1725 foreach ($this->auto_args as $k => $v) {
1726 if (preg_match('/[^:]:$/', $k)) {
1727 if (!isset($this->args[$v]) or !($this->args[$v])) {
1728 return false;
1729 }
1730 }
1731 }
1732 return true;
1733 }
1734 }
1735
1736 function get_input_param($param)
1737 {
1738 if (php_sapi_name() == "cli") { // command line
1739 if ($options = getopt(implode(array_keys($param)))) {
1740 foreach ($param as $k => $v) {
1741 $this->args[$v] = $options[$k[0]];
1742 }
1743 if (!$this->check_input()) {
1744 $this->usage(); exit;
1745 }
1746 } else {
1747 $this->usage(); exit;
1748 }
1749 } else { // web
1750 foreach ($param as $k => $v) {
1751 if (isset($_REQUEST[$v])) {
1752 $this->args[$v] = $_REQUEST[$v];
1753 }
1754 }
1755 if (!$this->check_input()) {
1756 $this->print_form(); exit;
1757 }
1758 }
1759 }
1760
1761 abstract function do_task();
1762
1763 function usage()
1764 {
1765 // XXX rewrite for grima form
1766 global $argv;
1767 print "Usage: php ${argv[0]} ";
1768
1769 foreach ($this->form->fields as $field) {
1770 $k = $field->shortname;
1771 $n = $field->name;
1772 if ($field->required) {
1773 print "-$k <$n> ";
1774 } else {
1775 print "[-$k <$n>] ";
1776 }
1777 }
1778 print "\n"; exit;
1779
1780
1781 // old format
1782 /*
1783 foreach ($this->auto_args as $k => $v) {
1784 if (preg_match('/^(.):$/', $k, $m)) {
1785 print "-${m[1]} <$v> ";
1786 } else {
1787 if (preg_match('/^(.)::$/', $k, $m)) {
1788 print "[ -${m[1]} <$v> ] ";
1789 } else {
1790 if (preg_match('/^.$/', $k)) {
1791 print "[ -$k ] ";
1792 }
1793 }
1794 }
1795 }
1796 print "\n";
1797 exit;
1798 */
1799 }
1800
1801 public static function call($grimaname, $args = array())
1802 {
1803 $url = rtrim("../$grimaname/$grimaname.php?" . http_build_query($args), "?");
1804 do_redirect($url);
1805 }
1806
1807}
1808
1809// }}}
1810
1811// {{{ class GrimaTaskMessage
1817{
1818 public $type;
1819 /* bootstrap type: debug, info, success, warning, error */
1820 public $message;
1821
1823 {
1824 $this->type = $type;
1825 $this->message = $message;
1826 }
1827}
1828
1829// }}}
1830
1831//{{{ class GrimaForm
1837{
1838 public $fields = array();
1839 public $title;
1840 protected $action;
1841
1842 // {{{ loadValues
1848 function loadValues($obj)
1849 {
1850 foreach ($this->fields as $field) {
1851 if (isset($obj[$field->name])) {
1852 $field->value = $obj[$field->name];
1853 }
1854 }
1855 }
1856 // }}}
1857
1858 // {{{ loadPersistentValues
1865 {
1866 foreach ($this->fields as $field) {
1867 if (($field->persistent) and isset($obj[$field->name])) {
1868 $field->value = $obj[$field->name];
1869 }
1870 }
1871 }
1872 // }}}
1873
1874 // {{{ fromXML
1879 function fromXML($xml)
1880 {
1881 $doc = new DomDocument();
1882 $doc->loadXML($xml);
1883 $xpath = new DomXpath($doc);
1884 $this->title = $xpath->query('//Title')[0]->nodeValue;
1885 $this->action = basename($_SERVER['PHP_SELF']); // allow set?
1886
1887 $nodes = $xpath->query('//Field');
1888 foreach ($nodes as $node) {
1889 $this->fields[$node->getAttribute('name')] = new GrimaFormField($node);
1890 }
1891 }
1892 // }}}
1893
1894}
1895
1896// }}}
1897
1898//{{{ class GrimaDataStore
1899
1900$ds_barcode = 'GRIMABYZEMKAT';
1901
1906class GrimaDataStore implements ArrayAccess
1907{
1908
1909 public $marc;
1910 public $bib;
1911 public $holding;
1912 public $item;
1913
1914 // {{{ __construction
1918 function __construct()
1919 {
1920 global $ds_barcode;
1921 $this->item = new Item();
1922 try {
1923 $this->item->loadFromAlmaBarcode($ds_barcode);
1924 } catch (Exception $e) {
1925 $this->setup();
1926 }
1927 $this->bib = new Bib();
1928 $this->bib->loadFromAlma($this->item['mms_id']);
1929 $this->bib->useNormac();
1930 }
1931 // }}}
1932
1933 // {{{ array access functions
1934 function offsetExists($offset)
1935 {
1936 $fields = $this->bib->marc->getFields('ZEM');
1937 foreach ($fields as $field) {
1938 $keys = $field->getSubfields('k');
1939 if ($keys[0]->data == $offset) {
1940 return true;
1941 }
1942 }
1943 return false;
1944 }
1945
1946 function offsetGet($offset)
1947 {
1948 $fields = $this->bib->marc->getFields('ZEM');
1949 foreach ($fields as $field) {
1950 $keys = $field->getSubfields('k');
1951 if ($keys[0]->data == $offset) {
1952 $values = $field->getSubfields('v');
1953 return $values[0]->data;
1954 }
1955 }
1956 return false;
1957 }
1958
1959 function offsetSet($offset, $value)
1960 {
1961 $fields = $this->bib->marc->getFields('ZEM');
1962 foreach ($fields as $field) {
1963 $keys = $field->getSubfields('k');
1964 if ($keys[0]->data == $offset) {
1965 $values = $field->getSubfields('v');
1966 $values[0]->data = $value;
1967 $this->bib->updateAlma();
1968 return true;
1969 }
1970 }
1971 $subarray = array(
1972 new ISO2709Subfield('k', $offset),
1973 new ISO2709Subfield('v', $value),
1974 );
1975 $field = new ISO2709Field('ZEM', null, " ", $subarray);
1976 $this->bib->marc->appendField($field);
1977 $this->bib->updateAlma();
1978 return false;
1979 }
1980
1981 function offsetUnset($offset)
1982 {
1983 $fields = $this->bib->marc->getFields('ZEM');
1984 foreach ($fields as $field) {
1985 $keys = $field->getSubfields('k');
1986 if ($keys[0]->data == $offset) {
1987 $field->delete();
1988 $this->bib->updateAlma();
1989 return true;
1990 }
1991 }
1992 return false;
1993 }
1994 // }}}
1995
1996 // {{{ setup
2000 function setup()
2001 {
2002 global $ds_barcode;
2003 $bib = new Bib();
2004 $bib['title'] = "GRIMA'S DATA STORE"; // XXX indicators 00?
2005 $bib['suppress_from_publishing'] = "true";
2006 $bib->addToAlma();
2007 $this->bib = $bib;
2008 $this->bib->useNormac();
2009
2010 $library = Library::getOneLibrary();
2011 $location = $library->getOneLocation();
2012
2013 $holding = new Holding();
2014 $holding['library_code'] = $library['code'];
2015 $holding['location_code'] = $location['code'];
2016 $holding->addToAlmaBib($bib['mms_id']);
2017 $this->holding = $holding;
2018
2019 $item = new Item();
2020 $item['barcode'] = $ds_barcode;
2021 $item->addToAlmaHolding($bib['mms_id'], $holding['holding_id']);
2022 $this->item = $item;
2023
2024 preg_match('/\d{5}$/', $bib['mms_id'], $m);
2025 $this['alma institution code'] = $m[0];
2026 }
2027 // }}}
2028
2029 // {{{ exists
2033 public static function exists()
2034 {
2035 global $ds_barcode;
2036 $item = new Item();
2037 try {
2038 $item->loadFromAlmaBarcode($ds_barcode);
2039 return true;
2040 } catch (Exception $e) {
2041 return false;
2042 }
2043 }
2044
2045 // }}}
2046
2047 // {{{ destroy
2051 public function destroy()
2052 {
2053 global $ds_barcode;
2054 $item = new Item();
2055 $item->loadFromAlmaBarcode($ds_barcode);
2056 $bib = new Bib();
2057 $bib->loadFromAlma($item['mms_id']);
2058 $bib->deleteTreeFromAlma();
2059 }
2060
2061 // }}}
2062
2063}
2064
2065// }}}
2066
2067//{{{ class GrimaFormField
2068
2074{
2075
2076 public $value;
2077
2078 public $name;
2080 public $label;
2085 public $visible;
2086 public $rows;
2087 public $dataset;
2088 protected $autocomplete;
2089 protected $highlight;
2090 public $error_condition = ""; /* can be warning or error */
2091 public $error_message = "";
2092
2093 // {{{ booly - is it true or false
2101 function booly($str, $default = 'undefined')
2102 {
2103 switch(strtolower($str)) {
2104 case 'true':
2105 case 't':
2106 case 'on':
2107 case 'yes':
2108 case '1':
2109 return true;
2110 case 'false':
2111 case 'f':
2112 case 'off':
2113 case 'no':
2114 case '0':
2115 return false;
2116 default:
2117 return $default;
2118 }
2119 }
2120 // }}}
2121
2122 // {{{ __construct
2128 function __construct($field)
2129 {
2130 $this->name = $field->getAttribute('name');
2131 $this->shortname = $field->getAttribute('shortname');
2132 $this->label = $field->getAttribute('label');
2133 $this->placeholder = $field->getAttribute('placeholder');
2134 $this->rows = $field->getAttribute('rows');
2135 $this->type = $field->getAttribute('type');
2136 if (!$this->type) {
2137 $this->type = 'input';
2138 }
2139 $this->required = $this->booly($field->getAttribute('required'), true);
2140 $this->persistent = $this->booly($field->getAttribute('persistent'), false);
2141 $this->autofocus = $this->booly($field->getAttribute('autofocus'), false);
2142 $this->autocomplete = $this->booly($field->getAttribute('autocomplete'), false);
2143 $this->visible = $this->booly($field->getAttribute('visible'), true);
2144 $this->dataset = $field->getAttribute('dataset');
2145 $this->options = array();
2146 if ($this->dataset) {
2147 if ($this->dataset == "institutions") {
2148 $institutions = GrimaDB::getInstitutions();
2149 $currentUser = GrimaUser::GetCurrentUser();
2150 foreach ($institutions as $institution) {
2151 $i = htmlspecialchars($institution);
2152 $s = ($institution === $currentUser['institution']) ? ' selected="selected"' : '';
2153 $this->options[] = "<option value=\"$i\"$s>$i</option>";
2154 }
2155 }
2156 } else {
2157 foreach ($field->getElementsByTagName("option") as $option) {
2158 $this->options[] = $option->ownerDocument->saveXML($option);
2159 }
2160 }
2161 }
2162 // }}}
2163
2164}
2165
2166// }}}
2167
2168//{{{ class AlmaObject
2173class AlmaObject implements ArrayAccess
2174{
2175 public $el_access = array();
2176 public $xml;
2177 public $templateDir = __DIR__ . "/templates";
2178
2179 // {{{ __construct
2183 function __construct()
2184 {
2185 $this->xml = new DomDocument();
2186 $blankRecord = get_class($this);
2187 $this->xml->loadXML(file_get_contents("{$this->templateDir}/{$blankRecord}.xml"));
2188 }
2189 // }}}
2190
2191 function offsetExists($offset)
2192 {
2193 if (isset($this->el_override)) {
2194 return array_key_exists($offset, $this->el_override);
2195 }
2196 return array_key_exists($offset, $this->el_access);
2197 }
2198
2199 function offsetGet($offset)
2200 {
2201 if ((isset($this->el_override))
2202 and (isset($this->el_override[$offset]))
2203 ) {
2204 return $this->el_override[$offset];
2205 }
2206 $xpath = new DomXpath($this->xml);
2207 $node = $xpath->query($this->el_address[$offset]);
2208 if (sizeof($node) >= 1) {
2209 return $node[0]->nodeValue;
2210 }
2211 return null;
2212 }
2213
2214 // XXX el_override ?
2215 function offsetSet($offset, $value)
2216 {
2217 $xpath = new DomXpath($this->xml);
2218 $node = $xpath->query($this->el_address[$offset]);
2219 $node[0]->nodeValue =
2220 preg_replace('/&(?!#?[a-z0-9]+;)/', '&amp;', $value); # XXX
2221 }
2222
2223 function offsetUnset($offset)
2224 {
2225 $xpath = new DomXpath($this->xml);
2226 $node = $xpath->query($this->el_address[$offset]);
2227 $node[0]->nodeValue = null;
2228 }
2229
2230}
2231
2232// }}}
2233
2234// {{{ class AlmaObjectWithMARC
2240{
2241
2242 public $marc; // Normac MARC21Record
2243
2244 function useNormac()
2245 {
2246 if (isset($this->marc)) {
2247 return;
2248 }
2249 $this->marc = new MARC21Record();
2250 $this->marc->loadFromString($this->xml->saveXML());
2251 }
2252
2253 // {{{ AlmaObjectWithMARC -> addControlField
2260 function addControlField($tag, $data)
2261 {
2262 $this->useNormac(); // this function uses normac
2263 $subarray = array();
2264 $field = new ISO2709Field($tag, $data);
2265 $this->marc->AppendField($field, true);
2266 }
2267 // }}}
2268
2269 // {{{ AlmaObjectWithMARC -> addDataField
2277 function addDataField($tag, $indicators, $subfields)
2278 {
2279 $this->useNormac(); // this function uses normac
2280 $subarray = array();
2281 foreach ($subfields as $k => $v) {
2282 $subarray[] = new ISO2709Subfield($k, $v);
2283 }
2284 $field = new ISO2709Field($tag, null, $indicators, $subarray);
2285 $fieldstr = $field->AsMnemonicString();
2286
2287 $fields = $this->marc->getFields($tag);
2288 $found = false;
2289 foreach($fields as $f) {
2290 $fstr = $f->AsMnemonicString();
2291 if ($fstr == $fieldstr) {
2292 $found = true;
2293 } else {
2294 #error_log($fstr . " vs " . $fieldstr);
2295 }
2296 }
2297 if (! $found) {
2298 $this->marc->AppendField($field, true);
2299 }
2300 }
2301 // }}}
2302
2303 // {{{ AlmaObjectWithMARC -> normacToXML
2307 function normacToXML()
2308 {
2309 if (isset($this->marc)) {
2310 $newRecord = new DOMDocument;
2311 $marc = preg_replace('/&(?!#?[a-z0-9]+;)/', '&amp;',
2312 $this->marc->asXMLString());
2313 $newRecord->loadXML($marc);
2314
2315 $xpath = new DOMXpath($this->xml);
2316 $record = $xpath->query('//record');
2317 $node = $record[0];
2318 $node->parentNode->removeChild($node);
2319
2320 $node = $this->xml->importNode($newRecord->documentElement, true);
2321 $this->xml->documentElement->appendChild($node);
2322 }
2323 }
2324
2325 // }}}
2326
2327 // {{{ AlmaObjectWithMARC -> appendField
2336 function appendField($tag, $ind1, $ind2, $subfields)
2337 ## XXX: replace with normac
2338 {
2339 $this->useNormac(); // this function uses normac
2340
2341 $subs = array();
2342 foreach ($subfields as $k => $v) {
2343 $subfa = new ISO2709Subfield($k, $v);
2344 $subs[] = $subfa;
2345 }
2346 $field = new ISO2709Field($tag, null, $ind1.$ind2, $subs);
2347 $this->marc->AppendField($field,true);
2348
2349 /* XXX: sheesh
2350 $frag = "<datafield ind1=\"$ind1\" ind2=\"$ind2\" tag=\"$tag\">";
2351 foreach ($subfields as $k => $v) {
2352 $frag .= "<subfield code=\"$k\">$v</subfield>";
2353 }
2354 $frag .= "</datafield>";
2355 $xpath = new DomXpath($this->xml);
2356 $record = $xpath->query("//record");
2357 appendInnerXML($record[0], $frag);
2358 */
2359 }
2360 // }}}
2361
2362 // {{{ AlmaObjectWithMARC -> getFields
2369 function getFields($tag)
2370 {
2371 $this->useNormac(); // this function uses normac
2372 return $this->marc->getFields($tag);
2373 }
2374 // }}}
2375
2376 // {{{ AlmaObjectWithMARC -> getMarcFields # XXX IN PROGRESS
2383 function getMarcFields($tag)
2384 {
2385 $xpath = new DomXpath($this->xml);
2386 $tag = preg_replace('/X*$/', '', $tag);
2387 $tag = preg_replace('/\.*$/', '', $tag);
2388 $fields = $xpath->query("//record/datafield[starts-with(@tag,'$tag')]");
2389 $fieldarr = array();
2390 foreach ($fields as $field) {
2391 $marcfield = new MARCField();
2392 // indicators?
2393 $subfieldarr = array();
2394 foreach ($field->childNodes as $child) {
2395 $subfieldarr[] = array(
2396 $child->attributes[0]->value,
2397 $child->nodeValue
2398 );
2399 }
2400 $fieldarr[] = $subfieldarr;
2401 }
2402 return $fieldarr;
2403 }
2404 // }}}
2405
2406 // {{{ AlmaObjectWithMARC -> getSubfieldValues
2414 function getSubfieldValues($tag, $code)
2415 {
2416 $xpath = new DomXpath($this->xml);
2417 $subfields = $xpath->query("//record/datafield[@tag='$tag']/subfield[@code='$code']");
2418 $arr = array();
2419 foreach ($subfields as $subfield) {
2420 $arr[] = $subfield->nodeValue;
2421 }
2422 return $arr;
2423 }
2424 // }}}
2425
2426 // {{{ AlmaObjectWithMARC -> deleteField
2432 function deleteField($tag)
2433 {
2434 $xpath = new DomXpath($this->xml);
2435 $fields = $xpath->query("//record/datafield[@tag='$tag']");
2436 foreach ($fields as $field) {
2437 $field->parentNode->removeChild($field);
2438 }
2439 }
2440 // }}}
2441
2442 // {{{ AlmaObjectWithMARC -> deleteSubfieldMatching
2448 function deleteSubfieldMatching($tag, $code, $regex)
2449 {
2450 $xpath = new DomXPath($this->xml);
2451 $subfs = $xpath->query("//datafield[@tag='$tag']/subfield[@code='$code']");
2452 foreach ($subfs as $subf) {
2453 if (preg_match($regex, $subf->nodeValue)) {
2454 $subf->parentNode->removeChild($subf);
2455 }
2456 }
2457 }
2458 // }}}
2459
2460 // {{{ AlmaObjectWithMARC -> deleteFieldMatching # XXX TEST
2472 function deleteFieldMatching($tag,$code,$regex)
2473 {
2474 $this->useNormac();
2475 foreach ($this->getFields($tag) as $field) {
2476 foreach ($field->getSubfields($code) as $subfield) {
2477 if (preg_match($regex,$subfield->data)) {
2478 $field->delete();
2479 break;
2480 }
2481 }
2482 }
2483 }
2484 // }}}
2485
2486 // {{{ AlmaObjectWithMARC -> replaceOrAddSubfield
2494 function replaceOrAddSubfield($tag, $code, $value)
2495 ## XXX: replace with normac
2496 {
2497 // very shady but sometimes needed
2498 $xpath = new DomXpath($this->xml);
2499 $fields = $xpath->query("//record/datafield[@tag='$tag']");
2500 if (sizeof($fields) == 0) {
2501 $this->appendField($tag, ' ', ' ', array($code => $value));
2502 } else {
2503 $done = false;
2504 foreach ($fields[0]->childNodes as $subfield) {
2505 if ($subfield->nodeType !== 1) {
2506 continue;
2507 }
2508 if ($subfield->getAttribute("code") == $code) {
2509 $subfield->nodeValue = $value;
2510 $done = true;
2511 break;
2512 }
2513 }
2514 if (!$done) {
2515 $subfield = $this->xml->createElement("subfield");
2516 $subfield->setAttribute("code", $code);
2517 $subfield->appendChild($this->xml->createTextNode($value));
2518 $fields[0]->appendChild($subfield);
2519 }
2520 }
2521 }
2522 // }}}
2523
2524}
2525// }}}
2526
2527// {{{ class Bib
2533{
2534 public $holdingsList; // HoldingsList object
2535 public $holdings = array();
2536
2538 public $items = array();
2539
2540 public $portfolioList = array(); // just an array for now
2541
2542 /* public $networknums = array(); */
2543
2544 protected $el_address = array(
2545 'mms_id' => '//mms_id',
2546 'leader' => '//leader',
2547 'record_format' => '//record_format',
2548 'title' => '//title',
2549 'author' => '//author',
2550 'place_of_publication' => '//place_of_publication',
2551 'publisher_const' => '//publisher_const',
2552 'publisher' => '//publisher_const',
2553 'suppress_from_publishing' => '//suppress_from_publishing',
2554 );
2555
2556 function offsetGet($offset)
2557 {
2558 if ($offset == 'Type') {
2559 $leader = $this['leader'];
2560 return $leader[6];
2561 }
2562 if ($offset == 'BLvl') {
2563 $leader = $this['leader'];
2564 return $leader[7];
2565 }
2566 if ($offset == 'ELvl') {
2567 $leader = $this['leader'];
2568 return $leader[17];
2569 }
2570 if ($offset == 'Desc') {
2571 $leader = $this['leader'];
2572 return $leader[18];
2573 }
2574 if ($offset == 'Lang') {
2575 $my008s = $this->getFields('008');
2576 $my008 = $my008s[0];
2577 $data = $my008->data;
2578 return substr($data,35,3);
2579 }
2580 return parent::offsetGet($offset);
2581 }
2582
2583 // override because these go multiple places
2584 function offsetSet($offset, $value)
2585 {
2586 parent::offsetSet($offset, $value);
2587 if ($offset == 'author') {
2588 $this->replaceOrAddSubfield('100', 'a', $value);
2589 }
2590 if ($offset == 'title') {
2591 $this->replaceOrAddSubfield('245', 'a', $value);
2592 }
2593 if (($offset == 'publisher_const') or ($offset == 'publisher')) {
2594 $this->replaceOrAddSubfield('264', 'b', $value);
2595 }
2596 if ($offset == 'place_of_publication') {
2597 $this->replaceOrAddSubfield('264', 'a', $value);
2598 }
2599 }
2600
2601 // {{{ Bib -> loadFromAlma (get) - gets Bib from Alma
2607 function loadFromAlma($mms_id)
2608 {
2609 global $grima;
2610 $this->xml = $grima->getBib($mms_id);
2611 $this->useNormac(); # XXX okay?
2612 }
2613 // }}}
2614
2615 // {{{ Bib -> addToAlma (post) - adds the Bib to Alma
2620 function addToAlma()
2621 {
2622 global $grima;
2623 $this->normacToXML();
2624 $this->xml = $grima->postBib($this->xml);
2625 }
2626 // }}}
2627
2628 // {{{ Bib -> updateAlma (put) - replaces the Bib in Alma
2632 function updateAlma()
2633 {
2634 global $grima;
2635 // XXX parent here?
2636 $this->normacToXML();
2637 $this->xml = $grima->putBib($this['mms_id'], $this->xml);
2638 }
2639 // }}}
2640
2641 // {{{ Bib -> deleteFromAlma (delete) - deletes the Bib from Alma
2646 {
2647 global $grima;
2648 $grima->deleteBib($this['mms_id']);
2649 }
2650 // }}}
2651
2652 // {{{ getMmsFromOCLC (get) - gets the MMS for an OCLC number
2656 public static function getMmsFromOCLC($oclc) # XXX
2657 {
2658 global $grima;
2659
2660 $report = new AnalyticsReport();
2661 $report->path = "/shared/Community/Reports/Institutions/University of Kentucky/Kathryn/OclcToMMS";
2662 $report->filter = '
2663<sawx:expr xsi:type="sawx:comparison" op="equal" xmlns:saw="com.siebel.analytics.web/report/v1.1"
2664xmlns:sawx="com.siebel.analytics.web/expression/v1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2665xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2666 <sawx:expr xsi:type="sawx:sqlExpression">"Holding Details"."Holding Id"</sawx:expr><sawx:expr xsi:type="xsd:string">{holding_id}</sawx:expr>
2667</sawx:expr>';
2668
2669 $report->filter = '
2670<sawx:expr xsi:type="sawx:comparison" op="equal" xmlns:saw="com.siebel.analytics.web/report/v1.1"
2671xmlns:sawx="com.siebel.analytics.web/expression/v1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2672xmlns:xsd="http://www.w3.org/2001/XMLSchema">
2673 <sawx:expr xsi:type="sawx:sqlExpression">"Bibliographic Details"."OCLC Control Number (035a)"</sawx:expr></sawx:expr>
2674';
2675
2676 $report->runReport(array('oclc' => $oclc), 1);
2677 if (count($report->rows) == 1) {
2678 return $report->rows[0][1];
2679 } else {
2680 throw new Exception("No MMS for OCLC #$oclc");
2681 }
2682 }
2683 // }}}
2684
2685 // {{{ Bib -> deleteTreeFromAlma (delete) - deletes Bib and inventory from Alma
2690 {
2691 global $grima;
2692 $this->getHoldings();
2693 foreach ($this->holdings as $holding) {
2694 $holding->deleteTreeFromAlma(); // XXX
2695 }
2696 $this->deleteAllPortfolios($this['mms_id']); // XXX
2697 $grima->deleteBib($this['mms_id']);
2698 }
2699 // }}}
2700
2701 // {{{ Bib -> hasInventory - does the bib have holdings or portfolios in Alma?
2705 function hasInventory()
2706 {
2707 $this->getHoldingsList();
2708 if (count($this->holdingsList->entries) > 0) {
2709 return true;
2710 }
2711 $this->getPortfolioList();
2712 if (count($this->portfolioList) > 0) {
2713 print_r($this->portfolioList);
2714 return true;
2715 } else {
2716 return false;
2717 }
2718 }
2719 // }}}
2720
2721 // {{{ Bib -> linkedToCZ - is the bib linked to community zone?
2725 function linkedToCZ()
2726 {
2727 $xpath = new DomXPath($this->xml);
2728 $nodes = $xpath->query("//linked_record_id[@type='CZ']");
2729 return (count($nodes) > 0);
2730 }
2731 // }}}
2732
2733 // {{{ Bib -> unlinkFromCZ - does this work
2738 function unlinkFromCZ()
2739 {
2740 $xpath = new DomXPath($this->xml);
2741 $nodes = $xpath->query("//linked_record_id[@type='CZ']");
2742 foreach ($nodes as $node) {
2743 $node->parentNode->removeChild($node);
2744 }
2745 $this->updateAlma();
2746 }
2747 // }}}
2748
2749 // {{{ Bib -> getHoldings - get holdings objects
2753 function getHoldings()
2754 {
2755 $this->getHoldingsList();
2756 foreach ($this->holdingsList->entries as $entry) {
2757 $holding = new Holding();
2758 $holding->loadFromAlma($this['mms_id'], $entry['holding_id']);
2759 $holding['mms_id'] = $this['mms_id'];
2760 $this->holdings[] = $holding;
2761 }
2762 return $this->holdings;
2763 }
2764 // }}}
2765
2766 // {{{ Bib -> deleteAllPortfolios - delete all portfolios from the bib
2771 {
2772 global $grima;
2773 while ($this->getPortfolioList()) {
2774 foreach ($this->portfolioList as $portfolio) {
2775 error_log("deleting " . $portfolio['pid'] . "\n");
2776 $portfolio->deleteFromAlma();
2777 }
2778 $this->portfolioList = array();
2779 sleep(3);
2780 }
2781 }
2782 // }}}
2783
2784 // {{{ Bib -> getHoldingsList
2789 {
2790 $this->holdingsList = new HoldingsList($this['mms_id']);
2791 }
2792 // }}}
2793
2794 // {{{ Bib -> getPortfolioList
2799 {
2800 // maybe rename
2801 global $grima;
2802 $limit = 10; $offset = 0; // where to allow passing
2803 $ret = $grima->getElectronicPortfoliosForBib($this['mms_id'], $limit, $offset);
2804 $xpath = new DOMXpath($ret);
2805 $ports = $xpath->query('//portfolio');
2806 foreach ($ports as $portnode) {
2807 $newport = new ElectronicPortfolio();
2808 $newport->loadFromPortfolioListNode($portnode);
2809 $this->portfolioList[] = $newport;
2810 }
2811 return count($ports); // XXX is this the right thing to return?
2812 }
2813 // }}}
2814
2815 // {{{ Bib -> getPortfolios
2819 function getPortfolios()
2820 {
2821 $this->getPortfolioList();
2822 foreach ($this->portfolioList as $port) {
2823 $port->loadFromAlma($port['portfolio_id']);
2824 }
2825 return $this->portfolioList;
2826 }
2827 // }}}
2828
2829 // {{{ getItems - get items objects
2833 function getItems()
2834 {
2835 $this->getItemList();
2836 $this->items =& $this->itemList->items;
2837 return $this->items;
2838 }
2839 // }}}
2840
2841 // {{{ getItemList - populates itemList property from Alma
2845 function getItemList()
2846 {
2847 // XXX
2848 global $grima;
2849 $this->itemList = new ItemList($this['mms_id'], 'ALL');
2850 }
2851 // }}}
2852
2853 // {{{ Bib -> get_title_proper
2859 {
2860 $xpath = new DomXpath($this->xml);
2861 $title = $xpath->query("//record/datafield[@tag='245']/subfield[@code='a']");
2862 return preg_replace("/[ \/=:,;\.]*$/", "", $title[0]->nodeValue);
2863 }
2864 // }}}
2865
2866 /*
2867 function get_networkNumbers() {
2868 $xpath = new DomXpath($this->xml);
2869 $netNums = $xpath->query("//bib/network_numbers/network_number");
2870 $ret = array();
2871 for ($j=0;$j<$netNums->length;$j++) {
2872 $ret[] = $netNums[$j]->nodeValue;
2873 }
2874 return $ret;
2875 }
2876 */
2877
2878 // {{{ Bib -> getLCCallNumber #XXX
2885 {
2886 $xpath = new DomXPath($this->xml);
2887 $calls = array();
2888 foreach (array('050','090') as $tag) {
2889 foreach ($xpath->query("//datafield[@tag='$tag']") as $call) {
2890 $calls[] = $call;
2891 }
2892 }
2893 $ret = array();
2894
2895 foreach ($calls as $node) {
2896 $classs = $xpath->query("subfield[@code='a']", $node);
2897 $items = $xpath->query("subfield[@code='b']", $node);
2898 if ((count($classs) > 0) and (count($items) > 0)) {
2899 $ret = array($classs[0]->nodeValue,$items[0]->nodeValue);
2900 }
2901 }
2902 return $ret;
2903 }
2904 // }}}
2905
2906}
2907
2908// }}}
2909
2910// {{{ class HoldingsList
2915{
2916 public $el_address = array(
2917 'mms_id' => '//mms_id',
2918 'title' => '//title',
2919 'author' => '//author',
2920 );
2921 public $xml;
2922 // public $holdings = array(); # should this go in BIB?
2923 public $entries = array(); // array of HoldingsListEntry
2924
2925 function __construct($mms_id = null)
2926 {
2927 if (!is_null($mms_id)) {
2928 $this->loadFromAlma($mms_id);
2929 }
2930 }
2931
2932 function loadFromAlma($mms_id)
2933 {
2934 global $grima;
2935 $this->xml = $grima->getHoldingsList($mms_id);
2936 $xpath = new DomXpath($this->xml);
2937 $hs = $xpath->query('//holding');
2938 $this->entries = array(); // clear
2939 // $this->holdings = array(); # clear
2940 foreach ($hs as $h) {
2941 // $this->holdings[] = new HoldingsListEntry($h,$mms_id);
2942 $this->entries[] = new HoldingsListEntry($h, $mms_id);
2943 }
2944 }
2945}
2946
2947// }}}
2948
2949// {{{ class HoldingsListEntry
2954{
2955 protected $el_address = array(
2956 'holding_id' => '//holding_id',
2957 'call_number' => '//holding/call_number',
2958 'library_code' => '//holding/library',
2959 'library' => '//holding/library/@desc',
2960 'location_code' => '//holding/location',
2961 'location' => '//holding/location/@desc'
2962 );
2963 public $xml;
2964
2965 function __construct($node, $mms_id)
2966 {
2967 $this->xml = new DomDocument();
2968 $this->xml->appendChild($this->xml->importNode($node, true));
2969 $this->el_override['mms_id'] = $mms_id;
2970 }
2971
2972 function getItemList($limit = -1)
2973 {
2974 global $grima;
2975 $this->getMmsIfNeeded();
2976 $this->itemList = new ItemList($this['mms_id'], $this['holding_id'], $limit);
2977 }
2978}
2979
2980// }}}
2981
2982// {{{ class ItemList
2986class ItemList extends AlmaObject
2987{
2988 public $items = array(); // maybe ok as is
2989
2990 function __construct($mms_id, $holding_id, $limit = -1)
2991 {
2992
2993 global $grima;
2994 $curr_offset = 0;
2995 $req_limit = ($limit == -1)?100:$limit;
2996
2997 do {
2998 if ($curr_offset > 0) {
2999 if (($curr_offset+1)*100 > $limit) {
3000 $req_limit = $limit - $curr_offset*100;
3001 } else {
3002 $req_limit = 100;
3003 }
3004 }
3005 $xml = $grima->getItemList($mms_id, $holding_id, $req_limit, $curr_offset*100);
3006 $xpath = new DomXpath($xml);
3007 $is = $xpath->query('//item');
3008 foreach ($is as $i) {
3009 $new_item = new Item();
3010 $new_item->loadFromItemListNode($i);
3011 $this->items[] = $new_item;
3012 }
3013 $xpath = new DomXPath($xml);
3014 if (!$curr_offset) {
3015 $length = $xpath->query('//items/@total_record_count')[0]->nodeValue;
3016 if ($limit == -1) { $limit = $length;
3017 }
3018 }
3019 $curr_offset++;
3020
3021 } while (($curr_offset*100 < $length) and ($curr_offset*100 < $limit));
3022
3023 }
3024
3025}
3026
3027// }}}
3028
3029// {{{ class Holding
3034{
3035 public $itemList; // object
3036 public $items = array();
3037 public $xml;
3038
3039 function offsetSet($offset, $value)
3040 {
3041 if ($offset == "mms_id") {
3042 $this->el_override['mms_id'] = $value;
3043 return;
3044 }
3045 if ($offset == "location_code") {
3046 $this->setLocationCode($value);
3047 return;
3048 }
3049 parent::offsetSet($offset, $value);
3050 }
3051
3052 function offsetGet($offset)
3053 {
3054 // XXX TEST
3055 if ($offset == "library") {
3056 $lib = new Library();
3057 $lib->loadFromAlma($this['library_code']);
3058 return $lib['name'];
3059 }
3060 if ($offset == "location") {
3061 $loc = new Location();
3062 $loc->loadFromAlma($this['library_code'], $this['location_code']);
3063 return $loc['name'];
3064 }
3065 if ($offset == "inst_code") {
3066 return $this->getInstitutionCode();
3067 }
3068 if ($offset == "library_code") {
3069 return $this->getLibraryCode();
3070 }
3071 if ($offset == "location_code") {
3072 return $this->getLocationCode();
3073 }
3074 if ($offset == "call_number") {
3075 $Hs = $this->getSubfieldValues("852", "h");
3076 $Is = $this->getSubfieldValues("852", "i");
3077 $acc = "";
3078 foreach ($Hs as $h) {
3079 $acc .= "$h ";
3080 }
3081 foreach ($Is as $i) {
3082 $acc .= "$i ";
3083 }
3084 return rtrim($acc);
3085 }
3086 return parent::offsetGet($offset);
3087 }
3088
3089 // {{{ $el_address
3090 public $el_address = array(
3091 'holding_id' => '//holding_id',
3092 'shelving_scheme' => "/holding/record/datafield[@tag='852']/@ind1",
3093
3094 /* should no longer be needed */
3095 'inst_code' => "/holding/record/datafield[@tag='852']/subfield[@code='a']",
3096 'library_code' => "/holding/record/datafield[@tag='852']/subfield[@code='b']",
3097 'location_code' => "/holding/record/datafield[@tag='852']/subfield[@code='c']",
3098
3099 'classification_part' => "/holding/record/datafield[@tag='852']/subfield[@code='h']",
3100 '852h' => "/holding/record/datafield[@tag='852']/subfield[@code='h']",
3101
3102 'item_part' => "/holding/record/datafield[@tag='852']/subfield[@code='i']",
3103 '852i' => "/holding/record/datafield[@tag='852']/subfield[@code='i']",
3104 /* should no longer be needed */
3105
3106 );
3107 // }}}
3108
3109 // {{{ loadFromAlma (get) - populates record from Alma
3116 function loadFromAlma($mms_id, $holding_id)
3117 {
3118 global $grima;
3119 $this->xml = $grima->getHolding($mms_id, $holding_id);
3120 $this['mms_id'] = $mms_id;
3121 }
3122 // }}}
3123
3124 // {{{ getMmsFromHoldingID (get) - gets the MMS for a holding ID
3128 public static function getMmsFromHoldingID($holding_id)
3129 {
3130 global $grima;
3131
3132 $report = new AnalyticsReport();
3133 // $report->path = "/shared/UK Libraries- University of Kentucky (UKY)/Reports/Kathryn/HoldingToMMS";
3134 $report->path = "/shared/Community/Reports/Institutions/University of Kentucky/Kathryn/HoldingToMMS";
3135 $report->filter = '
3136<sawx:expr xsi:type="sawx:comparison" op="equal" xmlns:saw="com.siebel.analytics.web/report/v1.1"
3137xmlns:sawx="com.siebel.analytics.web/expression/v1.1" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3138xmlns:xsd="http://www.w3.org/2001/XMLSchema">
3139 <sawx:expr xsi:type="sawx:sqlExpression">"Holding Details"."Holding Id"</sawx:expr><sawx:expr xsi:type="xsd:string">{holding_id}</sawx:expr>
3140</sawx:expr>';
3141
3142 $report->runReport(array('holding_id' => $holding_id), 1);
3143 if (count($report->rows) == 1) {
3144 return $report->rows[0][1];
3145 } else {
3146 throw new Exception("No MMS for Holding ID $holding_id");
3147 }
3148 }
3149 // }}}
3150
3151 // {{{ getMmsIfNeeded (get) - populates MMS if needed
3156 {
3157 global $grima;
3158
3159 if (!isset($this['mms_id']) or (!$this['mms_id'])) {
3160 $this['mms_id'] = Holding::getMmsFromHoldingID($this['holding_id']);
3161 }
3162 }
3163 // }}}
3164
3165 // {{{ loadFromAlmaX (get) - populates record from Alma using holding_id
3171 function loadFromAlmaX($holding_id)
3172 {
3173 global $grima;
3174
3175 $mms_id = Holding::getMmsFromHoldingID($holding_id);
3176 $this->xml = $grima->getHolding($mms_id, $holding_id);
3177 $this['mms_id'] = $mms_id;
3178 }
3179 // }}}
3180
3181 // {{{ addToAlmaBib (post) - adds new holding record to specified bib
3187 function addToAlmaBib($mms_id)
3188 {
3189 global $grima;
3190 $this->normacToXML();
3191 $this->xml = $grima->postHolding($mms_id, $this->xml);
3192 $this['mms_id'] = $mms_id;
3193 }
3194 // }}}
3195
3196 // {{{ hasItems - checks whether a holding record has any items
3200 function hasItems()
3201 {
3202 $this->getItemList();
3203 return (sizeof($this->itemList->items) > 0); // XXX test
3204 }
3205 // }}}
3206
3207 // {{{ updateAlma (put) - update record in Alma
3211 function updateAlma()
3212 {
3213 global $grima;
3214 $this->normacToXML(); # XXX
3215 #error_log($this->xml->saveXML());
3216 $grima->putHolding($this['mms_id'], $this['holding_id'], $this->xml);
3217 }
3218 // }}}
3219
3220 // {{{ deleteFromAlma (delete) - delete record in Alma
3225 {
3226 global $grima;
3227 $grima->deleteHolding($this['mms_id'], $this['holding_id']);
3228 }
3229 // }}}
3230
3231 // {{{ getMethodOfAcquisition - get method of acq (fixed field)
3238 {
3239 $this->useNormac();
3240 $fixed_fields = $this->getFields("008")[0];
3241 return substr($fixed_fields->data, 7, 1);
3242 }
3243 // }}}
3244
3245 // {{{ setMethodOfAcquisition - get method of acq (fixed field)
3253 {
3254 $this->useNormac();
3255 $fixed_fields = $this->getFields("008")[0];
3256 $newstr = substr_replace($fixed_fields->data,$code,7,1);
3257 $fixed_fields->data = $newstr;
3258 }
3259 // }}}
3260
3261 // {{{ getInstitutionCode - get institution code, 852a
3267 {
3268 $this->useNormac();
3269 return $this->getFields("852")[0]->getSubFields("a")[0]->data;
3270 }
3271 // }}}
3272
3273 // {{{ setInstitutionCode - set institution code, 852a
3279 function setInstitutionCode($code)
3280 {
3281 $this->useNormac();
3282 $instsf = $this->getFields("852")[0]->getSubFields("a")[0];
3283 $instsf->data = $code;
3284 }
3285 // }}}
3286
3287 // {{{ getLibraryCode - get library code, 852b
3293 {
3294 $this->useNormac();
3295 return $this->getFields("852")[0]->getSubFields("b")[0]->data;
3296 }
3297 // }}}
3298
3299 // {{{ setLibraryCode - set institution code, 852b
3305 function setLibraryCode($code)
3306 {
3307 $this->useNormac();
3308 $libsf = $this->getFields("852")[0]->getSubFields("b")[0];
3309 $libsf->data = $code;
3310 }
3311 // }}}
3312
3313 // {{{ getLocationCode - get location code, 852c
3319 {
3320 $this->useNormac();
3321 return $this->getFields("852")[0]->getSubFields("c")[0]->data;
3322 }
3323 // }}}
3324
3325 // {{{ setLocationCode - set institution code, 852c
3331 function setLocationCode($code)
3332 {
3333 $this->useNormac();
3334 $locsf = $this->getFields("852")[0]->getSubFields("c")[0];
3335 $locsf->data = $code;
3336 }
3337 // }}}
3338
3339 function setCallNumberType($ind1) # upgrade to normac
3340 {
3341 # XXX check argument
3342 $this->useNormac();
3343 $the852 = $this->getFields("852")[0];
3344 $the852->indicators[0] = $ind1;
3345
3346 /*
3347 $this->normacToXML();
3348 $xpath = new DomXpath($this->xml);
3349 $xpath->query("//record/datafield[@tag='852']")->item(0)->setAttribute("ind1", $ind1);
3350
3351 $field852 = $xpath->query("//record/datafield[@tag='852']")->item(0);
3352 $subfieldHs = $xpath->query("subfield[@code='h']", $field852);
3353 foreach ($subfieldHs as $subfieldH) {
3354 $subfieldH->parentNode->removeChild($subfieldH);
3355 }
3356 $subfieldIs = $xpath->query("subfield[@code='i']", $field852);
3357 foreach ($subfieldIs as $subfieldI) {
3358 $subfieldI->parentNode->removeChild($subfieldI);
3359 }
3360
3361 appendInnerXML($field852, "<subfield code=\"h\">$h</subfield>");
3362 if ($i) {
3363 appendInnerXML($field852, "<subfield code=\"i\">$i</subfield>");
3364 }
3365 */
3366 }
3367
3368 function setCallNumber($h, $i, $ind1) # upgrade to normac
3369 {
3370 $this->normacToXML();
3371 $xpath = new DomXpath($this->xml);
3372 $xpath->query("//record/datafield[@tag='852']")->item(0)->setAttribute("ind1", $ind1);
3373
3374 $field852 = $xpath->query("//record/datafield[@tag='852']")->item(0);
3375 $subfieldHs = $xpath->query("subfield[@code='h']", $field852);
3376 foreach ($subfieldHs as $subfieldH) {
3377 $subfieldH->parentNode->removeChild($subfieldH);
3378 }
3379 $subfieldIs = $xpath->query("subfield[@code='i']", $field852);
3380 foreach ($subfieldIs as $subfieldI) {
3381 $subfieldI->parentNode->removeChild($subfieldI);
3382 }
3383
3384 appendInnerXML($field852, "<subfield code=\"h\">$h</subfield>");
3385 if ($i) {
3386 appendInnerXML($field852, "<subfield code=\"i\">$i</subfield>");
3387 }
3388 }
3389
3390 // {{{ moveToBib - moves a holding from one bib to another
3394 function moveToBib($mms_id)
3395 {
3396 $this->deleteFromAlma();
3397 $this->addToAlmaBib($mms_id);
3398 }
3399 // }}}
3400
3401 // {{{ getItems - get items objects
3405 function getItems()
3406 {
3407 $this->getItemList();
3408 $this->items =& $this->itemList->items;
3409 return $this->items;
3410 }
3411 // }}}
3412
3413 // {{{ getItemList - populates itemList property from Alma
3417 function getItemList()
3418 {
3419 global $grima;
3420 $this->getMmsIfNeeded();
3421 $this->itemList = new ItemList($this['mms_id'], $this['holding_id']);
3422 }
3423 // }}}
3424
3425 // {{{ Holding -> deleteTreeFromAlma - delete the holding and all items
3430 {
3431 global $grima;
3432 $this->getItemList();
3433 foreach ($this->itemList->items as $item) {
3434 $item->deleteFromAlma("true");
3435 }
3436 $this->deleteFromAlma();
3437 }
3438 // }}}
3439
3440 // {{{ Holding -> deleteSubfieldMatching # XXX TEST
3452 function deleteSubfieldMatching($tag,$code,$regex)
3453 {
3454 $this->useNormac();
3455 foreach ($this->getFields($tag) as $field) {
3456 foreach ($field->getSubfields($code) as $subfield) {
3457 if (preg_match($regex,$subfield->data)) {
3458 $subfield->delete();
3459 }
3460 }
3461 }
3462 }
3463 // }}}
3464
3465 // {{{ Holding -> deleteFieldMatching # XXX TEST
3477 function deleteFieldMatching($tag,$code,$regex)
3478 {
3479 $this->useNormac();
3480 foreach ($this->getFields($tag) as $field) {
3481 foreach ($field->getSubfields($code) as $subfield) {
3482 if (preg_match($regex,$subfield->data)) {
3483 $field->delete();
3484 break;
3485 }
3486 }
3487 }
3488 }
3489 // }}}
3490
3491
3492}
3493
3494// }}}
3495
3496// {{{ class Item
3500class Item extends AlmaObject
3501{
3502
3503 public $el_address = array(
3504 'item_pid' => '//pid',
3505 'barcode' => '//barcode',
3506 'creation_date' => '//creation_date',
3507 'modification_date' => '//modification_date',
3508 'base_status' => '//base_status',
3509 'physical_material_type_code' => '//physical_material_type',
3510 'physical_material_type' => '//physical_material_type/@desc',
3511 'location' => '//location/@desc',
3512 'location_code' => '//location',
3513 'library' => '//location/@desc',
3514 'library_code' => '//library',
3515 'policy' => '//policy',
3516 'item_policy' => '//policy',
3517 'provenance' => '//provenance',
3518 'po_line' => '//po_line',
3519 'is_magnetic' => '//is_magnetic',
3520 'arrival_date' => '//arrival_date',
3521 'year_of_issue' => '//year_of_issue',
3522 'enumeration_a' => '//enumeration_a',
3523 'enumeration_b' => '//enumeration_b',
3524 'enumeration_c' => '//enumeration_c',
3525 'enumeration_d' => '//enumeration_d',
3526 'enumeration_e' => '//enumeration_e',
3527 'enumeration_f' => '//enumeration_f',
3528 'enumeration_g' => '//enumeration_g',
3529 'enumeration_h' => '//enumeration_h',
3530 'chronology_i' => '//chronology_i',
3531 'chronology_j' => '//chronology_j',
3532 'chronology_k' => '//chronology_k',
3533 'chronology_l' => '//chronology_l',
3534 'chronology_m' => '//chronology_m',
3535 'description' => '//description',
3536 'alternative_call_number' => '//alternative_call_number',
3537 'alternative_call_number_type' => '//alternative_call_number_type',
3538 'storage_location_id' => '//storage_location_id',
3539 'receiving_operator' => '//receiving_operator',
3540 'process_type' => '//process_type',
3541 'in_temp_location' => '//in_temp_location',
3542 'mms_id' => '//mms_id',
3543 'holding_id' => '//holding_id',
3544 'title' => '//title',
3545 'author' => '//author',
3546 'call_number' => '//call_number',
3547 'pages' => '//pages',
3548 'pieces' => '//pieces',
3549 'public_note' => '//public_note',
3550 'fulfillment_note' => '//fulfillment_note',
3551 'internal_note_1' => '//internal_note_1',
3552 'internal_note_2' => '//internal_note_2',
3553 'internal_note_3' => '//internal_note_3',
3554 'statistics_note_1' => '//statistics_note_1',
3555 'statistics_note_2' => '//statistics_note_2',
3556 'statistics_note_3' => '//statistics_note_3',
3557 'requested' => '//requested',
3558 'physical_condition' => '//physical_condition',
3559 'temp_library' => '//temp_library',
3560 'temp_location' => '//temp_location',
3561 'temp_policy' => '//temp_policy',
3562 'copy_id' => '//copy_id',
3563 'inventory_date' => '//inventory_date',
3564 'inventory_number' => '//inventory_number',
3565 'inventory_price' => '//inventory_price',
3566 );
3567
3568 // {{{ fluff
3573 function fluff()
3574 {
3575 $fields = array( # XXX: fill with the rest
3576 "pieces",
3577 "copy_id",
3578 "inventory_date",
3579 "inventory_number",
3580 );
3581 $xpath = new DomXpath($this->xml);
3582 $stem = $xpath->query("//item_data")[0];
3583 foreach($fields as $field) {
3584 $try = $xpath->query("//$field");
3585 if (sizeof($try) == 0) {
3586 # XXX add empty node with that name
3587 $node = $this->xml->createElement($field);
3588 $stem->appendChild($node);
3589 }
3590 }
3591 }
3592 // }}}
3593
3594 // {{{ loadFromAlma (get)
3602 function loadFromAlma($mms_id, $holding_id, $item_pid)
3603 {
3604 global $grima;
3605 $this->xml = $grima->getItem($mms_id, $holding_id, $item_pid);
3606 }
3607 // }}}
3608
3609 // {{{ loadFromAlmaX (get)
3614 function loadFromAlmaX($item_pid)
3615 {
3616 global $grima;
3617 $this->xml = $grima->getItem('X', 'X', $item_pid);
3618 }
3619 // }}}
3620
3621 // {{{ loadFromAlmaBarcode (get)
3626 function loadFromAlmaBarcode($barcode)
3627 {
3628 global $grima;
3629 $this->xml = $grima->getItemBC($barcode);
3630 }
3631 // }}}
3632
3633 // {{{ loadFromAlmaBCorX (get)
3640 function loadFromAlmaBCorX($id)
3641 {
3642 global $grima;
3643 if (preg_match("/^23.*/", $id)) { // item_pid
3644 // probably should know about suffix too
3645 $this->loadFromAlmaX($id);
3646 } else {
3647 $this->loadFromAlmaBarcode($id);
3648 }
3649 }
3650 // }}}
3651
3652 // {{{ loadFromItemListNode
3658 function loadFromItemListNode($node)
3659 {
3660 $this->xml = new DomDocument();
3661 $this->xml->appendChild($this->xml->importNode($node, true));
3662 }
3663 // }}}
3664
3665 // {{{ addToAlmaHolding (post)
3672 function addToAlmaHolding($mms_id, $holding_id)
3673 {
3674 global $grima;
3675 $this->mms_id = $mms_id;
3676 $this->holding_id = $holding_id;
3677 $this->xml = $grima->postItem($mms_id, $holding_id, $this->xml);
3678 return $this->xml;
3679 }
3680 // }}}
3681
3682 // {{{ addToAlmaHoldingX (post)
3688 // XXX check this
3689 function addToAlmaHoldingX($holding_id)
3690 {
3691 $this->addToAlmaHolding('X', $holding_id);
3692 return $this->xml;
3693 }
3694 // }}}
3695
3696 // {{{ updateAlma (put)
3701 function updateAlma()
3702 {
3703 global $grima;
3704 return $grima->putItem(
3705 $this['mms_id'],
3706 $this['holding_id'],
3707 $this['item_pid'],
3708 $this->xml
3709 );
3710 }
3711 // }}}
3712
3713 // {{{ deleteFromAlma (delete)
3720 function deleteFromAlma($override = "false", $holdings = "retain")
3721 {
3722 global $grima;
3723 $grima->deleteItem($this['mms_id'], $this['holding_id'], $this['item_pid'], $override, $holdings);
3724 }
3725 // }}}
3726
3727}
3728
3729// }}}
3730
3731// {{{ class ElectronicCollection
3736{
3737
3738 public $xml;
3739 public $services = array();
3740
3741
3742 // {{{ ElectronicCollection -> el_address
3743 public $el_address = array( // XXX many more fields here
3744 'collection_id' => '//id',
3745 'id' => '//id',
3746 'public_name' => '//public_name',
3747 );
3748 // }}}
3749
3750 // {{{ ElectronicCollection -> loadFromAlma (get)
3754 function loadFromAlma($collection_id)
3755 {
3756 global $grima;
3757 $this->xml = $grima->getElectronicCollection($collection_id);
3758 // $this['collection_id'] = $collection_id;
3759 }
3760 // }}}
3761
3762 // {{{ ElectronicCollection -> updateAlma (put)
3766 function updateAlma()
3767 {
3768 global $grima;
3769 $this->xml = $grima->putElectronicCollection($this['collection_id'], $this->xml);
3770 }
3771 // }}}
3772
3773 // {{{ ElectronicCollection -> getServices (get) XXX
3777 function getServices()
3778 {
3779 global $grima;
3780 $ret = $grima->getElectronicServices($this['collection_id']);
3781 $xpath = new DomXpath($ret);
3782 $eservices = $xpath->query('//electronic_services/electronic_service');
3783 foreach ($eservices as $service) {
3784 $service_id = $service->firstChild->nodeValue; // XXX really?
3785 $ser = new ElectronicService();
3786 $ser->loadFromServiceListNode($service);
3787 $this->services[] = $ser;
3788 }
3789 }
3790 // }}}
3791
3792}
3793
3794// }}}
3795
3796// {{{ class ElectronicService
3801{
3803
3804 public $portfolios = array();
3805
3806 public $el_address = array(
3807 'id' => '//id',
3808 'service_id' => '//id',
3809 );
3810
3811 public $el_override = array();
3812
3813 function __construct()
3814 {
3815 // load from template
3816 }
3817
3818 function offsetSet($offset, $value)
3819 {
3820 if ($offset == "collection_id") {
3821 $this->el_override['collection_id'] = $value;
3822 } else {
3823 parent::offsetSet($offset, $value);
3824 }
3825 }
3826
3827 // {{{ ElectronicService -> loadFromAlma (get)
3831 function loadFromAlma($collection_id, $service_id)
3832 {
3833 global $grima;
3834 $this->xml = $grima->getElectronicService($collection_id, $service_id);
3835 $this['collection_id'] = $collection_id;
3836 }
3837 // }}}
3838
3839 // {{{ ElectronicService -> loadFromServiceListNode
3846 {
3847 $this->xml = new DomDocument();
3848 $this->xml->appendChild($this->xml->importNode($node, true));
3849 $xpath = new DomXPath($this->xml);
3850 $service_link = $xpath->query('//electronic_service')->item(0)->attributes['link']->nodeValue;
3851 preg_match('!/e-collections/(.*)/e-services/!', $service_link, $m);
3852 $this['collection_id'] = $m[1];
3853 }
3854
3856 {
3857 $curr_offset = 0;
3858 do {
3859 $retrieved = $this->getPortfolios(100, $curr_offset);
3860 $curr_offset += 100;
3861 } while ($retrieved == 100);
3862 }
3863
3864 // {{{ ElectronicService -> getPortfolios
3872 /* mark full or brief? */
3873 // function retrievePortfolios($limit = 10, $offset = 0) { # RENAME
3874 function getPortfolios($limit = 10, $offset = 0)
3875 {
3876 global $grima;
3877 $ret = $grima->getElectronicPortfoliosForService($this['collection_id'], $this['service_id'], $limit, $offset);
3878 $xpath = new DOMXpath($ret);
3879 $ports = $xpath->query('//portfolio');
3880 foreach ($ports as $portnode) {
3881 $newport = new ElectronicPortfolio();
3882 $newport->loadFromPortfolioListNode($portnode);
3883 $this->portfolios[] = $newport;
3884 }
3885 return count($ports);
3886 }
3887
3888 // }}}
3889
3890 // {{{ ElectronicService -> deleteAllPortfolios - delete all portfolios from the bib
3896 function deleteAllPortfolios($bib_treat)
3897 {
3898 global $grima;
3899 while ($this->getPortfolios(100, 0)) {
3900 foreach ($this->portfolios as $portfolio) {
3901 $portfolio->deleteFromAlma($bib_treat);
3902 }
3903 $this->portfolios = array();
3904 sleep(2);
3905 }
3906 }
3907 // }}}
3908
3909 /*
3910 function deleteAllPortfolios() { #XXX
3911 global $grima;
3912 # get portfolio list? or just ten at a time?
3913 $this->retrievePortfolios();
3914 while (sizeof($this->portfolios) > 0) {
3915 foreach ($this->portfolios as $portfolio) {
3916 print $portfolio['mms_id']->nodeValue; exit;
3917 #$portfolio->delete();
3918 }
3919 }
3920 }
3921 */
3922
3923}
3924
3925//}}}
3929//}}}
3930
3931//{{{ class ElectronicPortfolio
3937{
3938 public $xml;
3939
3940 // {{{ ElectronicPortfolio -> el_address
3941 public $el_address = array(
3942 'portfolio_id' => '//portfolio/id',
3943 'is_local' => '//is_local',
3944 'is_standalone' => '//is_standalone',
3945 'mms_id' => '//mms_id',
3946 'title' => '//title',
3947 'service' => '//service',
3948 'url' => '//url',
3949 'static_url' => '//static_url',
3950 'availability' => '//availability',
3951 'collection_id' => '//electronic_collection/id',
3952 'service_id' => '//electronic_collection/service',
3953 'material_type' => '//material_type',
3954 'url_type' => '//url_type',
3955 'public_note' => '//public_note',
3956 'proxy_enabled' => '//proxy_enabled',
3957 'proxy' => '//proxy',
3958 'library' => '//library',
3959 'po_line' => '//po_line'
3960 );
3961 // }}}
3962
3963 // {{{ ElectronicPortfolio -> addToAlmaService
3970 function addToAlmaService($collection_id, $service_id)
3971 {
3972 global $grima;
3973 $ret = $grima->postElectronicPortfolio($collection_id, $service_id, $this->xml);
3974 return $ret;
3975 }
3976 // }}}
3977
3978 // {{{ ElectronicPortfolio -> addToAlmaBib
3986 function addToAlmaBib($mms_id)
3987 {
3988 global $grima;
3989 $ret = $grima->postElectronicPortfolioOnBib($mms_id, $this->xml);
3990 return $ret;
3991 }
3992 // }}}
3993
3994 // {{{ ElectronicPortfolio -> loadFromAlma
4000 function loadFromAlma($portfolio_id)
4001 {
4002 global $grima;
4003 $this->xml = $grima->getElectronicPortfolio('X', 'X', $portfolio_id);
4004 }
4005
4006 function loadFromAlmaX($portfolio_id)
4007 {
4008 global $grima;
4009 $this->xml = $grima->getElectronicPortfolio('X', 'X', $portfolio_id);
4010 }
4011 // }}}
4012
4013 // {{{ ElectronicPortfolio -> loadFromPortfolioListNode
4020 {
4021 $this->xml = new DomDocument();
4022 $this->xml->appendChild($this->xml->importNode($node, true));
4023 }
4024 // }}}
4025
4026 // {{{ ElectronicPortfolio -> updateAlma
4030 function updateAlma()
4031 {
4032 global $grima;
4033 $this->xml = $grima->putElectronicPortfolioOnBib($this['mms_id'], $this['portfolio_id'], $this->xml);
4034 }
4035 // }}}
4036
4037 // {{{ ElectronicPortfolio -> deleteFromAlma
4043 function deleteFromAlma($bib_treat = "retain")
4044 {
4045 // accept a variable?
4046 global $grima;
4047 $mms_id = $this['mms_id'];
4048 $grima->deleteElectronicPortfolio(
4049 'X', 'X',
4050 $this['portfolio_id']
4051 );
4052 if ($bib_treat == "delete") {
4053 $bib = new Bib();
4054 $bib->loadFromAlma($mms_id);
4055 sleep(2);
4056 if (! $bib->hasInventory()) {
4057 if ($bib->linkedToCZ()) {
4058 print "LINKED TO CZ";
4059 $bib->unlinkFromCZ();
4060 exit;
4061 } else {
4062 $bib->deleteFromAlma();
4063 }
4064 }
4065 }
4066 }
4067 // }}}
4068
4069 // {{{ ElectronicPortfolio -> activate
4073 function activate()
4074 {
4075 $this['availability'] = 11;
4076 }
4077 // }}}
4078
4079 // {{{ ElectronicPortfolio -> deactivate
4083 function deactivate()
4084 {
4085 $this['availability'] = 10;
4086 }
4087 // }}}
4088
4089}
4090
4091// }}}
4092
4093// {{{ class Set
4097class Set extends AlmaObject
4098{
4099 public $xml;
4100 public $members = array();
4101
4102 /*
4103 public $id;
4104 public $type; # itemized or logical
4105 public $name;
4106 public $private;
4107 public $active;
4108 public $size; # number of members
4109 */
4110
4111 // {{{ Set -> el_address
4112 public $el_address = array(
4113 'set_id' => '//set/id',
4114 'id' => '//set/id',
4115 'name' => '//set/name',
4116 'description' => '//set/description',
4117 'type' => '//set/type',
4118 'content' => '//content',
4119 'private' => '//private',
4120 'status' => '//status',
4121 'status_date' => '//status_date',
4122 'note' => '//note',
4123 'created_by' => '//created_by',
4124 'created_date' => '//created_date',
4125 'query' => '//query',
4126 'number_of_members' => '//number_of_members',
4127 'additional_info' => '//additional_info',
4128 );
4129 // }}}
4130
4131 // {{{ Set -> addToAlma (post) - adds the Set to Alma
4135 function addToAlma()
4136 {
4137 global $grima;
4138 $this->xml = $grima->postSet($this->xml);
4139 }
4140 // }}}
4141
4142 // {{{ Set -> loadFromAlma
4148 function loadFromAlma($set_id)
4149 {
4150 global $grima;
4151 $this->xml = $grima->getSet($set_id);
4152 }
4153 // }}}
4154
4155 // {{{ Set -> createFromImport
4162 function createFromImport($job_id, $population)
4163 {
4164 global $grima;
4165 $this->xml = $grima->createSetFromImport($job_id, $population);
4166 }
4167 // }}}
4168
4169 // {{{ Set -> runOnElements
4177 function runOnElements($function, $args = array(), $filter = null, $filter_args = array())
4178 {
4179 global $grima;
4180 $xml = $grima->getSetMembers($this['set_id'], 0);
4181 $xpath = new DomXpath($xml);
4182 $this->size = $xpath->query("//members")->item(0)->getAttribute("total_record_count");
4183 $limit = $this->size;
4184 for ($j = 0; $j < ceil($limit/100); $j++) { // how many queries
4185 $xml = $grima->getSetMembers($this['id'], 100, $j*100);
4186 $xpath = new DomXpath($xml);
4187 foreach ($xpath->query("//member") as $member) {
4188 $id = $member->childNodes[0]->nodeValue;
4189 if (!isset($filter) or ($filter($id, $filter_args))) {
4190 $function($id, $args);
4191 }
4192 }
4193 }
4194
4195 }
4196 // }}}
4197
4198 function getMembers($limit = -1)
4199 {
4200 // put in $members
4201 // limit -1 means all
4202 global $grima;
4203 if ($limit == -1) { // get them all
4204 $xml = $grima->getSetMembers($this['set_id'], 0);
4205 $xpath = new DomXpath($xml);
4206 $this->size = $xpath->query("//members")->item(0)->getAttribute("total_record_count");
4207 $limit = $this->size;
4208 }
4209
4210 for ($j = 0; $j < ceil($limit/100); $j++) { // how many queries
4211 $xml = $grima->getSetMembers($this['id'], 100, $j*100);
4212 $xpath = new DomXpath($xml);
4213 foreach ($xpath->query("//member") as $member) {
4214 $this->members[] = new SetMember(
4215 $member->childNodes[0]->nodeValue,
4216 $member->childNodes[1]->nodeValue
4217 );
4218 }
4219 }
4220 }
4221
4222 // {{{ Set -> deleteFromAlma (delete)
4227 {
4228 global $grima;
4229 $grima->deleteSet($this['set_id']);
4230 }
4231 // }}}
4232
4233 // {{{ Set -> addMember (delete)
4239 function addMember($id)
4240 {
4241 $frag = "<member><id>$id</id></member>";
4242 $source = new DOMDocument();
4243 $source->loadXML($frag);
4244
4245 $xpath = new DomXpath($this->xml);
4246 $members = $xpath->query('//members')->item(0);
4247 $members->appendChild($this->xml->importNode($source->documentElement, true));
4248
4249 }
4250 // }}}
4251
4252 // {{{ Set -> appendInAlma ()
4258 function appendInAlma($setToAppend)
4259 {
4260 global $grima;
4261 $grima->postSetManageMembers(
4262 $this['set_id'], null, 'add_members',
4263 $setToAppend->xml
4264 );
4265 }
4266 // }}}
4267
4268 // {{{ Set -> removeAllMembersInAlma ()
4273 {
4274 global $grima;
4275 $emptyset = new Set();
4276 $emptyset->addMember('9941266686802636'); /* hack */
4277 $grima->postSetManageMembers(
4278 $this['set_id'], null, 'replace_members',
4279 $emptyset->xml
4280 );
4281 $grima->postSetManageMembers(
4282 $this['set_id'], null, 'delete_members',
4283 $emptyset->xml
4284 );
4285 }
4286
4287}
4288
4289// }}}
4290
4291// }}}
4292
4293// {{{ class SetMember
4298{
4299 public $id;
4301
4303 {
4304 $this->id = $id;
4305 $this->description = $description;
4306 }
4307}
4308
4309// }}}
4310
4311// {{{ class Library
4315class Library extends AlmaObject
4316{
4317 public $el_address = array(
4318 'code' => '//code',
4319 'path' => '//path',
4320 'name' => '//name',
4321 'resource_sharing' => '//resource_sharing',
4322 );
4323
4324 // {{{ Library -> loadFromAlma
4330 function loadFromAlma($code)
4331 {
4332 global $grima;
4333 $this->xml = $grima->getLibrary($code);
4334 }
4335 // }}}
4336
4337 // {{{ Library -> getAllLibraries
4343 public static function getAllLibraries()
4344 {
4345 global $grima;
4346 $xml = $grima->getAllLibraries();
4347 $xpath = new DomXpath($xml);
4348 $codes = $xpath->query('//library/code');
4349 $libarray = array();
4350 foreach ($codes as $code) {
4351 $lib = new Library();
4352 $lib->loadFromAlma($code->nodeValue);
4353 $libarray[] = $lib;
4354 }
4355 return $libarray;
4356 }
4357 // }}}
4358
4359 // {{{ Library -> getOneLibrary
4366 public static function getOneLibrary($otherthan = array())
4367 {
4368 global $grima;
4369 $xml = $grima->getAllLibraries();
4370 $xpath = new DomXpath($xml);
4371 $codes = $xpath->query('//library/code');
4372
4373 foreach ($codes as $code) {
4374 if (! in_array($code, $otherthan)) {
4375 $lib = new Library();
4376 $lib->loadFromAlma($code);
4377 return $lib;
4378 }
4379 }
4380 throw new Exception("No libraries found.");
4381 }
4382 // }}}
4383
4384 // {{{ Library -> getAllLocations
4390 public function getAllLocations()
4391 {
4392 global $grima;
4393 $xml = $grima->getAllLocations($this['code']);
4394 $xpath = new DomXpath($xml);
4395 $codes = $xpath->query('//location/code');
4396 $locarray = array();
4397 foreach ($codes as $code) {
4398 $loc = new Location();
4399 $loc->loadFromAlma($this['code'], $code->nodeValue);
4400 $locarray[] = $loc;
4401 }
4402 return $locarray;
4403 }
4404 // }}}
4405
4406 // {{{ Library -> getOneLocation
4413 public function getOneLocation($otherthan = array())
4414 {
4415 global $grima;
4416 $xml = $grima->getAllLocations($this['code']);
4417 $xpath = new DomXpath($xml);
4418 $codes = $xpath->query('//location/code');
4419 foreach ($codes as $code) {
4420 if (! in_array($code->nodeValue, $otherthan)) {
4421 $loc = new Location();
4422 $loc->loadFromAlma($this['code'], $code->nodeValue);
4423 return $loc;
4424 }
4425 }
4426 throw new Exception("No locations found.");
4427 }
4428 // }}}
4429
4430 // {{{ Library -> getOneLibraryLocation
4438 public static function getOneLibraryLocation($otherthan = array())
4439 {
4440 global $grima;
4441
4442 $xml = $grima->getAllLibraries();
4443 $xpath = new DomXpath($xml);
4444 $library_codes = $xpath->query('//library/code');
4445
4446 foreach ($library_codes as $library_code) {
4447 $lib = new Library();
4448 $lib->loadFromAlma($library_code->nodeValue);
4449 $arr = array();
4450 foreach ($otherthan as $other) {
4451 if ($other[0] == $library_code->nodeValue) {
4452 $arr[] = $other[1];
4453 }
4454 }
4455 try {
4456 $loc = $lib->getOneLocation($arr);
4457 return $loc;
4458 } catch (Exception $e) {
4459 continue;
4460 }
4461 }
4462 throw new Exception("No suitable library/location found.");
4463 }
4464 // }}}
4465
4466}
4467
4468// }}}
4469
4470// {{{ class Location
4474class Location extends AlmaObject
4475{
4476
4477 public $el_override_fields = array('library_code','flag');
4478
4479 public $el_address = array(
4480 'code' => '//code',
4481 'name' => '//name',
4482 'external_name' => '//external_name',
4483 'type' => '//type',
4484 'remote_storage' => '//remote_storage',
4485 'map' => '//map',
4486 'suppress_from_publishing' => '//suppress_from_publishing',
4487 'fulfillment_unit' => '//fulfillment_unit',
4488 'accession_placement' => '//accession_placement',
4489 'call_number_type' => '//call_number_type',
4490 );
4491
4492 function offsetSet($offset, $value)
4493 {
4494 if (in_array($offset, $this->el_override_fields)) {
4495 $this->el_override[$offset] = $value;
4496 } else {
4497 parent::offsetSet($offset, $value);
4498 }
4499 /*
4500 if ($offset == "library_code") {
4501 $this->el_override['library_code'] = $value;
4502 } else {
4503 parent::offsetSet($offset,$value);
4504 }
4505 */
4506 }
4507
4508 // {{{ Location -> loadFromAlma
4514 function loadFromAlma($libraryCode, $locationCode)
4515 {
4516 global $grima;
4517 $this->xml = $grima->getLocation($libraryCode, $locationCode);
4518 $this['library_code'] = $libraryCode;
4519 }
4520 // }}}
4521
4522}
4523
4524// }}}
4525
4526// {{{ class Job
4530class Job extends AlmaObject
4531{
4532 public $el_address = array(
4533 'id' => '//id',
4534 'name' => '//name',
4535 'description' => '//description',
4536 'type' => '//type',
4537 'category' => '//category',
4538 'content' => '//content',
4539 'schedule' => '//schedule',
4540 'creator' => '//creator',
4541 'next_run' => '//next_run',
4542 'related_profile' => '//related_profile',
4543 'additional_info' => '//additional_info',
4544 );
4545
4546 // {{{ Job -> addParameter
4553 function addParameter($name, $value)
4554 {
4555 $frag = "<parameter><name>$name</name><value>$value</value></parameter>";
4556 $source = new DOMDocument();
4557 $source->loadXML($frag);
4558
4559 $xpath = new DomXpath($this->xml);
4560 $members = $xpath->query('//parameters')->item(0);
4561 $members->appendChild($this->xml->importNode($source->documentElement, true));
4562 }
4563 // }}}
4564
4565 // {{{ Job -> runInAlma
4571 function runInAlma()
4572 {
4573 global $grima;
4574 $grima->postJob($this['id'], 'run', $this->xml);
4575 }
4576 // }}}
4577
4578}
4579
4580// }}}
4581
4582// {{{ class AnalyticsReport
4587{
4588
4589 public $path;
4590 public $filter;
4592 public $rows = array();
4593
4594 // {{{ AnalyticsReport -> runReport
4600 function runReport($filter_params = array(), $limit = -1, $token = "")
4601 {
4602 global $grima;
4603 if (isset($this->filter)) {
4604 $passfilter = $this->filter;
4605 foreach ($filter_params as $k => $v) {
4606 $passfilter = str_replace('{'.$k.'}', urlencode($v), $passfilter);
4607 }
4608 } else {
4609 $passfilter = null;
4610 }
4611
4612 if ($limit == -1) { $limit = 1000;
4613 } // no limit
4614 if ($limit < 25) { $limit = 25;
4615 } // must be in chunks of 25
4616 $this->reportXml = $grima->getAnalytics($this->path, $passfilter, $limit, $token);
4617
4618 $xpath = new DomXpath($this->reportXml);
4619 $xpath->registerNamespace("x", "urn:schemas-microsoft-com:xml-analysis:rowset");
4620
4621 $rows = $xpath->query('//x:Row');
4622 foreach ($rows as $row) {
4623 $newrow = array();
4624 $cols = $xpath->query("./*[contains(name(),'Column')]", $row);
4625 foreach ($cols as $col) {
4626 $newrow[] = $col->nodeValue;
4627 }
4628 $this->rows[] = $newrow;
4629 }
4630 }
4631
4632 // }}}
4633
4634
4635}
4636
4637// }}}
4638
4639// {{{ class GrimaDB
4640
4641// {{{ shared
4642function tableExists($pdo, $table)
4643{
4644 $table_esc = "\"" . preg_replace("/\"/", "\"\"", $table) . "\"";
4645 try {
4646 $result = $pdo->query("SELECT 1 FROM $table_esc LIMIT 1");
4647 } catch (Exception $e) {
4648 // We got an exception == table not found
4649 return false;
4650 }
4651 // Result is either boolean FALSE (no table found) or PDOStatement Object (table found)
4652 return $result !== false;
4653}
4654
4655// }}}
4656
4657// {{{ class GrimaDB
4662class GrimaDB implements ArrayAccess, IteratorAggregate
4663{
4664
4665 private static $db = false;
4666 private $info; // array
4667
4668 static function init()
4669 {
4670 return self::getDb();
4671 }
4672
4673 static function isEmpty()
4674 {
4675 $db = self::getDb();
4676 if ($db === 'stateless') {
4677 return true;
4678 }
4679 $result = $db->query('SELECT COUNT(*) as c FROM institutions');
4680 if ($result) {
4681 foreach ($result as $row) {
4682 if ($row['c']>0) { return false;
4683 }
4684 }
4685 }
4686 return true;
4687 }
4688
4689 static function isStateless()
4690 {
4691 $db = self::getDb();
4692 return $db === 'stateless';
4693 }
4694
4695 protected static function getDb()
4696 {
4697 if (!self::$db) {
4698 try {
4699 $db_url = getenvWithFileFallbackAndDefault('DATABASE_URL', '');
4700 $db_user = getenvWithFileFallbackAndDefault('DATABASE_USER', '');
4701 $db_pass = getenvWithFileFallbackAndDefault('DATABASE_PASS', '');
4702 if ($db_user && $db_pass) {
4703 self::$db = new PDO($db_url, $db_user, $db_pass);
4704 } else {
4705 self::$db = new PDO($db_url);
4706 }
4707 if (!tableExists(self::$db, "institutions")) {
4708 self::$db->exec("CREATE TABLE institutions ( institution VARCHAR(100) PRIMARY KEY, apikey VARCHAR(100), server VARCHAR(100) )");
4709 }
4710 if (!tableExists(self::$db, "users")) {
4711 self::$db->exec("CREATE TABLE users ( institution VARCHAR(100), username VARCHAR(100), password VARCHAR(255), isAdmin INTEGER )");
4712 }
4713 } catch (Exception $e) {
4714 self::$db = 'stateless';
4715 }
4716 }
4717 return self::$db;
4718 }
4719
4720 protected function getPasswordAlgorithm()
4721 {
4722 return defined("PASSWORD_ARGON2I") ? constant("PASSWORD_ARGON2I") : PASSWORD_DEFAULT;
4723 }
4724
4725 static function getInstitutions()
4726 {
4727 $ret = array();
4728 try {
4729 $db = self::getDb();
4730 if ($db === 'stateless') {
4731 return $ret;
4732 }
4733 $result = $db->query('SELECT institution FROM institutions');
4734 foreach ($result as $row) {
4735 $ret[] = $row['institution'];
4736 }
4737 } catch (Exception $e) {
4738 }
4739 return $ret;
4740 }
4741
4742 function offsetExists($offset)
4743 {
4744 return isset($this->info[$offset]);
4745 }
4746
4747 function offsetGet($offset)
4748 {
4749 return isset($this->info[$offset]) ? $this->info[$offset] : '';
4750 }
4751
4752 function offsetSet($offset, $value)
4753 {
4754 $this->info[$offset] = $value;
4755 }
4756
4757 function offsetUnset($offset)
4758 {
4759 unset($this->info[$offset]);
4760 }
4761
4762 function getIterator()
4763 {
4764 return new ArrayIterator($this->info);
4765 }
4766
4767}
4768
4769// }}}
4770
4771// {{{ class GrimaInstitution
4777{
4778 public function addToDB()
4779 {
4780 $db = $this->getDb();
4781 if ($db === 'stateless') {
4782 throw new Exception("No database has been specified. Grima cannot add an institution until it knows where to add it.");
4783 }
4784 $query = $db->prepare('INSERT INTO institutions(institution,apikey,server) VALUES (:institution,:apikey,:server)');
4785 if (!$query) {
4786 $errorCode = $db->errorCode();
4787 $errorInfo = $db->errorInfo();
4788 throw new Exception(
4789 "Could not even prepare to insert into institution database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4790 );
4791 }
4792 $success = $query->execute(
4793 array(
4794 'institution' => $this['institution'],
4795 'apikey' => $this['apikey'],
4796 'server' => $this['server'],
4797 )
4798 );
4799 if (!$success) {
4800 $errorCode = $query->errorCode();
4801 $errorInfo = $query->errorInfo();
4802 throw new Exception(
4803 "Could not insert into user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4804 );
4805 }
4806 }
4807}
4808
4809// }}}
4810
4811// {{{ class GrimaUser
4816class GrimaUser extends GrimaDB
4817{
4818 public static function FromArray($data)
4819 {
4820 $user = new GrimaUser();
4821 foreach ($data as $key => $val) {
4822 $user[$key] = $val;
4823 }
4824 return $user;
4825 }
4826
4827 public static function LookupUser($username, $institution = '', $password = false)
4828 {
4829 $db = self::getDb();
4830 if ($db === 'stateless') {
4831 return false;
4832 }
4833 $query = $db->prepare(
4834 'SELECT * ' .
4835 'FROM institutions NATURAL JOIN users '.
4836 'WHERE institution=:institution '.
4837 'AND username=:username'
4838 );
4839 $success = $query->execute(
4840 array(
4841 'institution' => $institution,
4842 'username' => $username,
4843 )
4844 );
4845 if ($success) {
4846 $row = $query->fetch(PDO::FETCH_ASSOC);
4847 if ($row===false) { return false;
4848 }
4849 $user = GrimaUser::FromArray($row);
4850 if (($password !== false) && ($user['password']!='')) {
4851 if (!password_verify($password, $user['password'])) { return false;
4852 }
4853 if (password_needs_rehash($user['password'], $user->getPasswordAlgorithm())) {
4854 $user['password'] = $password;
4855 $user->updateDB();
4856 }
4857 }
4858 unset($user['password']);
4859 return $user;
4860 } else {
4861 $errorCode = $query->errorCode();
4862 $errorInfo = $query->errorInfo();
4863 throw new Exception(
4864 "Could not select from user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4865 );
4866 }
4867 }
4868
4869 public static function RenameUser($username, $institution, $newusername)
4870 {
4871 $db = self::getDb();
4872 if ($db === 'stateless') {
4873 throw new Exception("No database has been specified. Grima cannot save changes to users if it does not where to save them.");
4874 }
4875 $query = $db->prepare(
4876 'UPDATE users ' .
4877 'SET username=:newusername ' .
4878 'WHERE institution=:institution '.
4879 'AND username=:username'
4880 );
4881 $success = $query->execute(
4882 array(
4883 'institution' => $institution,
4884 'username' => $username,
4885 'newusername' => $newusername,
4886 )
4887 );
4888 if ($success) {
4889 return true;
4890 } else {
4891 $errorCode = $query->errorCode();
4892 $errorInfo = $query->errorInfo();
4893 throw new Exception(
4894 "Could not update user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4895 );
4896 }
4897 }
4898
4899 public static function ResetPassword($username, $institution, $password)
4900 {
4901 $db = self::getDb();
4902 if ($db === 'stateless') {
4903 throw new Exception("No database specified. Grima cannot save changes to users if it doesn't know where to save them.");
4904 }
4905 $query = $db->prepare(
4906 'UPDATE users ' .
4907 'SET password=:password ' .
4908 'WHERE institution=:institution '.
4909 'AND username=:username'
4910 );
4911 $passwordHash = password_hash($password, self::getPasswordAlgorithm());
4912 $success = $query->execute(
4913 array(
4914 'institution' => $institution,
4915 'username' => $username,
4916 'password' => $passwordHash,
4917 )
4918 );
4919 if ($success) {
4920 return true;
4921 } else {
4922 $errorCode = $query->errorCode();
4923 $errorInfo = $query->errorInfo();
4924 throw new Exception(
4925 "Could not update user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4926 );
4927 }
4928 }
4929
4930 public static function GetCurrentUser()
4931 {
4932 if (!isset($_SESSION)) { return false;
4933 }
4934 return GrimaUser::FromArray($_SESSION);
4935 }
4936
4937 public static function SetCurrentUser($username, $password, $institution = '')
4938 {
4939 global $grima;
4940 $user = GrimaUser::LookupUser($username, $institution, $password);
4941 if ($user !== false) {
4942 $grima->sessionSave($user);
4943 } else {
4944 return false;
4945 }
4946 }
4947
4948 public static function LogoutCurrentUser()
4949 {
4950 global $grima;
4951 $grima->sessionDestroy();
4952 }
4953
4954 function addToDB()
4955 {
4956 $db = $this->getDb();
4957 if ($db === 'stateless') {
4958 throw new Exception("No database specified. Grima cannot add a user to the database, if it doesn't where to add it.");
4959 }
4960 $query = $db->prepare('INSERT INTO users( username, password, institution, isAdmin ) VALUES (:username, :password, :institution, :isAdmin)');
4961 if (!$query) {
4962 $errorCode = $db->errorCode();
4963 $errorInfo = $db->errorInfo();
4964 throw new Exception(
4965 "Could not even prepare to insert into user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4966 );
4967 }
4968 $success = $query->execute(
4969 array(
4970 'username' => $this['username'],
4971 'password' => password_hash($this['password'], $this->getPasswordAlgorithm()),
4972 'institution' => $this['institution'],
4973 'isAdmin' => $this['isAdmin'],
4974 )
4975 );
4976 if (!$success) {
4977 $errorCode = $query->errorCode();
4978 $errorInfo = $query->errorInfo();
4979 throw new Exception(
4980 "Could not insert into user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4981 );
4982 }
4983 }
4984
4985 function updateDB()
4986 {
4987 $db = $this->getDb();
4988 if ($db === 'stateless') {
4989 throw new Exception("No database specified. Grima cannot save changes to users if it doesn't know where to save them.");
4990 }
4991 $query = $db->prepare('UPDATE users SET isAdmin=:isAdmin, password=:password WHERE username=:username AND institution=:institution');
4992 if (!$query) {
4993 $errorCode = $db->errorCode();
4994 $errorInfo = $db->errorInfo();
4995 throw new Exception(
4996 "Could not even prepare to update user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
4997 );
4998 }
4999 $success = $query->execute(
5000 array(
5001 'username' => $this['username'],
5002 'password' => password_hash($this['password'], $this->getPasswordAlgorithm()),
5003 'institution' => $this['institution'],
5004 'isAdmin' => $this['isAdmin'],
5005 )
5006 );
5007 if (!$success) {
5008 $errorCode = $query->errorCode();
5009 $errorInfo = $query->errorInfo();
5010 throw new Exception(
5011 "Could not update user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
5012 );
5013 }
5014 }
5015
5016 function deleteFromDB()
5017 {
5018 $db = $this->getDb();
5019 if ($db === 'stateless') {
5020 throw new Exception("No database specified. Grima cannot save changes to users if it doesn't know where to save them.");
5021 }
5022 $query = $db->prepare('DELETE FROM users WHERE username=:username AND institution=:institution');
5023 if (!$query) {
5024 $errorCode = $db->errorCode();
5025 $errorInfo = $db->errorInfo();
5026 throw new Exception(
5027 "Could not even prepare to delete from user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
5028 );
5029 }
5030 $success = $query->execute(
5031 array(
5032 'username' => $this['username'],
5033 'institution' => $this['institution'],
5034 )
5035 );
5036 if (!$success) {
5037 $errorCode = $query->errorCode();
5038 $errorInfo = $query->errorInfo();
5039 throw new Exception(
5040 "Could not delete from user database: [$errorCode] {$errorInfo[0]} {$errorInfo[2]}"
5041 );
5042 }
5043 }
5044}
5045
5046// }}}
5047
5048// }}}
5049
5050// {{{ class EncryptedCookieSession
5051
5053implements SessionHandlerInterface
5054{
5055
5056 private $save_path;
5059 private $last_read;
5060
5061 public function __construct($key)
5062 {
5063 $this->session_key = $key;
5064 $this->last_read = "Not This";
5065 }
5066
5068 {
5069 $this->save_path = $save_path;
5070 $this->session_name = $session_name;
5071 //error_log("session_open($save_path,$session_name)");
5072 return true;
5073 }
5074
5075 public function close()
5076 {
5077 //error_log("session_close()");
5078 return true;
5079 }
5080
5081 public function destroy($session_id)
5082 {
5083 //error_log("sessionDestroy($session_id)");
5084 $params = session_get_cookie_params();
5085 $params['expires'] = time() - 42000;
5086 unset($params['lifetime']);
5087 setcookie($this->session_name, '', $params);
5088 return true;
5089 }
5090
5091 public function gc($maxlifetime)
5092 {
5093 //error_log("session_gc($maxlifetime)");
5094 return true;
5095 }
5096
5097 public function read($session_id)
5098 {
5099 //error_log("session_read($session_id)");
5100 $nonce_ciphertext_b64 = isset($_COOKIE[$this->session_name]) ? $_COOKIE[$this->session_name] : "";
5101 if ($nonce_ciphertext_b64) {
5102 $nonce_ciphertext = base64_decode($nonce_ciphertext_b64);
5103 if (strlen($nonce_ciphertext)>24+16) {
5104 $nonce = substr($nonce_ciphertext, 0, 24);
5105 $ciphertext = substr($nonce_ciphertext, 24);
5106 $plaintext = sodium_crypto_secretbox_open($ciphertext, $nonce, $this->session_key);
5107 // error_log("session_read -> $plaintext");
5108 $this->last_read = $plaintext;
5109 return $plaintext ?: "";
5110 } else {
5111 error_log("cookie too short: $nonce_ciphertext_b64");
5112 }
5113 } else {
5114 //error_log("cookie not set or empty");
5115 }
5116 return "";
5117 }
5118
5119 public function write($session_id, $session_data)
5120 {
5121 // error_log("session_write($session_id,$session_data)");
5122 if (!$session_data) { return true;
5123 }
5124 if ($this->last_read == $session_data) { return true;
5125 }
5126 $nonce = random_bytes(24);
5127 $ciphertext = sodium_crypto_secretbox($session_data, $nonce, $this->session_key);
5128 $nonce_ciphertext = $nonce.$ciphertext;
5129 $nonce_ciphertext_b64 = base64_encode($nonce_ciphertext);
5130 $options = session_get_cookie_params();
5131 if (isset($options['expires']) && $options['expires']) {
5132 $options['expires'] += time();
5133 }
5134 unset($options['lifetime']);
5135 setcookie($this->session_name, $nonce_ciphertext_b64, $options);
5136 return true;
5137 }
5138
5139}
5140
5141// }}}
5142
5144
5145/* vim: set foldmethod=marker noexpandtab shiftwidth=4 tabstop=4: */
Methods for fields and subfields in Alma's MARCXML.
Definition: grima-lib.php:2240
appendField($tag, $ind1, $ind2, $subfields) ## XXX
add a field to the MARC record
Definition: grima-lib.php:2336
getFields($tag)
get fields for the given MARC tag
Definition: grima-lib.php:2369
addDataField($tag, $indicators, $subfields)
add a data field to the MARC record using Normac
Definition: grima-lib.php:2277
getMarcFields($tag)
get fields for the given MARC tag
Definition: grima-lib.php:2383
deleteFieldMatching($tag, $code, $regex)
delete fields with subfields matching a regex
Definition: grima-lib.php:2472
deleteField($tag)
delete all $tag fields from the MARC record
Definition: grima-lib.php:2432
addControlField($tag, $data)
add a data field to the MARC record using Normac
Definition: grima-lib.php:2260
deleteSubfieldMatching($tag, $code, $regex)
delete subfields matching a regex
Definition: grima-lib.php:2448
normacToXML()
convert normac fields back to XML
Definition: grima-lib.php:2307
getSubfieldValues($tag, $code)
get subfield value
Definition: grima-lib.php:2414
replaceOrAddSubfield($tag, $code, $value) ## XXX
replace or add subfield value in marc
Definition: grima-lib.php:2494
Base class for objects returned from alma APIs (mostly just array access)
Definition: grima-lib.php:2174
__construct()
create new blank Alma Object
Definition: grima-lib.php:2183
$el_access
Definition: grima-lib.php:2175
$templateDir
Definition: grima-lib.php:2177
offsetExists($offset)
Definition: grima-lib.php:2191
offsetGet($offset)
Definition: grima-lib.php:2199
offsetSet($offset, $value)
Definition: grima-lib.php:2215
offsetUnset($offset)
Definition: grima-lib.php:2223
class AnalyticsReport
Definition: grima-lib.php:4587
runReport($filter_params=array(), $limit=-1, $token="")
pull Analytics report from Alma
Definition: grima-lib.php:4600
Definition: grima-lib.php:2533
deleteFromAlma()
deletes the Bib from Alma
Definition: grima-lib.php:2645
deleteAllPortfolios()
delete all portfolios from the bib
Definition: grima-lib.php:2770
$itemList
Definition: grima-lib.php:2537
getHoldingsList()
populate holdingsList property with info from Alma
Definition: grima-lib.php:2788
getItemList()
populates itemList property from Alma
Definition: grima-lib.php:2845
getPortfolios()
get full portfolios for the bib
Definition: grima-lib.php:2819
unlinkFromCZ()
does this work @XXX: Not supported for CZ
Definition: grima-lib.php:2738
$el_address
Definition: grima-lib.php:2544
linkedToCZ()
is the bib linked to the community zone?
Definition: grima-lib.php:2725
getHoldings()
populate holdings property with Holdings items
Definition: grima-lib.php:2753
getLCCallNumber()
get LC call number, giving pref to 090 and later fields
Definition: grima-lib.php:2884
get_title_proper()
a tidy title proper
Definition: grima-lib.php:2858
$holdingsList
Definition: grima-lib.php:2534
addToAlma()
adds record as a new record to Alma, updates Bib with current Alma version
Definition: grima-lib.php:2620
loadFromAlma($mms_id)
populates the bib with a record from Alma
Definition: grima-lib.php:2607
static getMmsFromOCLC($oclc) # XXX
populates the MMS ID
Definition: grima-lib.php:2656
offsetSet($offset, $value)
Definition: grima-lib.php:2584
$holdings
Definition: grima-lib.php:2535
getPortfolioList()
populate portfolioList property with brief info from Alma
Definition: grima-lib.php:2798
$portfolioList
Definition: grima-lib.php:2540
hasInventory()
populate holdings property with Holdings items
Definition: grima-lib.php:2705
$items
Definition: grima-lib.php:2538
deleteTreeFromAlma()
deletes the Bib and its inventory #XXX
Definition: grima-lib.php:2689
offsetGet($offset)
Definition: grima-lib.php:2556
updateAlma()
replaces the Bib in Alma
Definition: grima-lib.php:2632
getItems()
populate items property with Items objects ## XXX TEST
Definition: grima-lib.php:2833
class ElectronicCollection
Definition: grima-lib.php:3736
loadFromAlma($collection_id)
load record from Alma
Definition: grima-lib.php:3754
getServices()
load record from Alma
Definition: grima-lib.php:3777
updateAlma()
replace the collection record in Alma
Definition: grima-lib.php:3766
class ElectronicPortfolio
Definition: grima-lib.php:3937
loadFromAlma($portfolio_id)
populate portfolio with info from Alma
Definition: grima-lib.php:4000
updateAlma()
replaces the Portfolio in Alma
Definition: grima-lib.php:4030
addToAlmaBib($mms_id)
add portfolio to Bib in Alma @XXX: Insufficient permission
Definition: grima-lib.php:3986
addToAlmaService($collection_id, $service_id)
populate item record from the information in an PortfolioList node @XXX: Insufficient permission
Definition: grima-lib.php:3970
loadFromPortfolioListNode($node)
populate item record from the information in an PortfolioList node
Definition: grima-lib.php:4019
deleteFromAlma($bib_treat="retain")
delete portfolio from Alma
Definition: grima-lib.php:4043
deactivate()
deactivates the portfolio record
Definition: grima-lib.php:4083
activate()
activates the portfolio record
Definition: grima-lib.php:4073
loadFromAlmaX($portfolio_id)
Definition: grima-lib.php:4006
class ElectronicService
Definition: grima-lib.php:3801
loadFromAlma($collection_id, $service_id)
load record from Alma
Definition: grima-lib.php:3831
deleteAllPortfolios($bib_treat)
delete all portfolios from the service
Definition: grima-lib.php:3896
__construct()
create new blank Alma Object
Definition: grima-lib.php:3813
getPortfolios($limit=10, $offset=0)
get list of portfolios from Alma
Definition: grima-lib.php:3874
retrieveAllPortfolios()
Definition: grima-lib.php:3855
loadFromServiceListNode($node)
populate item record from the information in an ServiceList node
Definition: grima-lib.php:3845
offsetSet($offset, $value)
Definition: grima-lib.php:3818
open($save_path, $session_name)
Definition: grima-lib.php:5067
write($session_id, $session_data)
Definition: grima-lib.php:5119
gc($maxlifetime)
Definition: grima-lib.php:5091
destroy($session_id)
Definition: grima-lib.php:5081
read($session_id)
Definition: grima-lib.php:5097
Shared access to the database for GrimaUser and GrimaInstitution.
Definition: grima-lib.php:4663
getIterator()
Definition: grima-lib.php:4762
offsetSet($offset, $value)
Definition: grima-lib.php:4752
static getInstitutions()
Definition: grima-lib.php:4725
static isStateless()
Definition: grima-lib.php:4689
offsetGet($offset)
Definition: grima-lib.php:4747
getPasswordAlgorithm()
Definition: grima-lib.php:4720
$info
Definition: grima-lib.php:4666
static getDb()
Definition: grima-lib.php:4695
static init()
Definition: grima-lib.php:4668
offsetExists($offset)
Definition: grima-lib.php:4742
offsetUnset($offset)
Definition: grima-lib.php:4757
static $db
Definition: grima-lib.php:4665
static isEmpty()
Definition: grima-lib.php:4673
This object lets grima store data in a catalog record.
Definition: grima-lib.php:1907
setup()
create the record structure in Alma
Definition: grima-lib.php:2000
offsetUnset($offset)
Definition: grima-lib.php:1981
offsetSet($offset, $value)
Definition: grima-lib.php:1959
offsetGet($offset)
Definition: grima-lib.php:1946
static exists()
checks whether a grima data store has been set up in Alma
Definition: grima-lib.php:2033
destroy()
remove the grima data store from Alma
Definition: grima-lib.php:2051
__construct()
make sure it exists
Definition: grima-lib.php:1918
offsetExists($offset)
Definition: grima-lib.php:1934
Wrapper for each field in a form, keeping track of its properties.
Definition: grima-lib.php:2074
__construct($field)
create a new GrimaFormField
Definition: grima-lib.php:2128
booly($str, $default='undefined')
return boolean meaning of common terms ("true","on","1","yes")
Definition: grima-lib.php:2101
A holder for the user inputs to a grima, base on the XML file.
Definition: grima-lib.php:1837
loadPersistentValues($obj)
load persistent values into the form
Definition: grima-lib.php:1864
fromXML($xml)
interpret XML to determine form fields and behavior
Definition: grima-lib.php:1879
loadValues($obj)
load values into the form
Definition: grima-lib.php:1848
Interface to the GrimaDB database to retrieve API keys.
Definition: grima-lib.php:4777
A thin wrapper around a message and urgency level.
Definition: grima-lib.php:1817
__construct($type, $message)
Definition: grima-lib.php:1822
The base class for most "grimas".
Definition: grima-lib.php:1523
usage()
Definition: grima-lib.php:1763
get_input_param($param)
Definition: grima-lib.php:1736
offsetSet($offset, $value)
Definition: grima-lib.php:1543
__construct()
Definition: grima-lib.php:1553
addMessage($type, $message)
Definition: grima-lib.php:1626
static RunIt()
Definition: grima-lib.php:1655
print_form()
Definition: grima-lib.php:1594
setup_splat()
Definition: grima-lib.php:1566
static call($grimaname, $args=array())
Definition: grima-lib.php:1801
do_task()
offsetExists($offset)
Definition: grima-lib.php:1533
check_login()
Definition: grima-lib.php:1612
$auto_args
Definition: grima-lib.php:1529
offsetUnset($offset)
Definition: grima-lib.php:1548
get_input()
Definition: grima-lib.php:1661
print_failure()
Definition: grima-lib.php:1606
$messages
Definition: grima-lib.php:1531
print_success()
Definition: grima-lib.php:1600
run()
Definition: grima-lib.php:1631
$el_override
Definition: grima-lib.php:1527
check_input()
Definition: grima-lib.php:1707
offsetGet($offset)
Definition: grima-lib.php:1538
Interface to the GrimaDB database to check password and get institution's apikey.
Definition: grima-lib.php:4817
deleteFromDB()
Definition: grima-lib.php:5016
static RenameUser($username, $institution, $newusername)
Definition: grima-lib.php:4869
addToDB()
Definition: grima-lib.php:4954
static LookupUser($username, $institution='', $password=false)
Definition: grima-lib.php:4827
static GetCurrentUser()
Definition: grima-lib.php:4930
static LogoutCurrentUser()
Definition: grima-lib.php:4948
updateDB()
Definition: grima-lib.php:4985
static FromArray($data)
Definition: grima-lib.php:4818
static SetCurrentUser($username, $password, $institution='')
Definition: grima-lib.php:4937
static ResetPassword($username, $institution, $password)
Definition: grima-lib.php:4899
postJob($job_id, $op, $job)
Submit a manual or scheduled job.
Definition: grima-lib.php:1494
getHoldingsList($mms_id)
Retrieve Holdings list - download brief descriptions of holdings for the bib.
Definition: grima-lib.php:506
put($url, $URLparams, $QSparams, $body)
general function for PUT (update) API calls
Definition: grima-lib.php:330
postElectronicPortfolioOnBib($mms_id, $portfolio, $update=true)
Create Electronic Portfolio - add a new portfolio to Alma Bib.
Definition: grima-lib.php:935
getElectronicCollection($collection_id)
Retrieve Electronic Collection - retrieve a collection record from Alma.
Definition: grima-lib.php:1066
deleteBib($mms_id, $override='false')
Delete Bib Record - deletes the bib record from Alma.
Definition: grima-lib.php:469
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:667
sessionSave($result)
Definition: grima-lib.php:80
sessionInit($force=false)
Definition: grima-lib.php:37
__construct()
Definition: grima-lib.php:118
deleteSet($set_id)
Delete a Set - delete a set (not its items) from Alma.
Definition: grima-lib.php:1405
sessionDestroy()
Definition: grima-lib.php:95
getSet($set_id)
Retrieve a Set - retrieve a Set from Alma.
Definition: grima-lib.php:1277
request($method, $url, $URLparams, $QSparams, $body=null)
general function for REST API calls
Definition: grima-lib.php:171
putElectronicCollection($collection_id, $collection)
Update Electronic Collection Record - updates the copy of the collection in Alma.
Definition: grima-lib.php:1091
postHolding($mms_id, $holding)
Create holding record - add a new holdings record to a bib.
Definition: grima-lib.php:572
$apikey
Definition: grima-lib.php:33
createSetFromImport($job_instance_id, $population)
Create a Set from an import job.
Definition: grima-lib.php:1353
putBib($mms_id, $bib)
Update Bib Record - updates the copy of the bib in Alma.
Definition: grima-lib.php:444
postSetManageMembers($set_id, $id_type, $op, $set)
Manage Members - modify sets in Alma.
Definition: grima-lib.php:1328
getElectronicPortfoliosForService($collection_id, $service_id, $limit, $offset)
Retrieve Portfolios - retrieve a list of portfolios from Alma.
Definition: grima-lib.php:1016
getAllLocations($libraryCode)
Retrieve Locations - retrieve all locations in a library.
Definition: grima-lib.php:1200
getSetMembers($set_id, $limit=10, $offset=0)
get the members of a set, IF IT WORKS?!?!
Definition: grima-lib.php:1431
getBib($mms_id, $view='full', $expand='None')
Retrieve Bib - retrieve a bib record from Alma.
Definition: grima-lib.php:394
$server
Definition: grima-lib.php:32
getAllLibraries()
Retrieve Libraries - retrieve all libraries.
Definition: grima-lib.php:1177
getLibrary($libraryCode)
Retrieve a Library - retrieve a Library from Alma.
Definition: grima-lib.php:1154
postItem($mms_id, $holding_id, $item)
Create Item - add a new item to a holding in Alma.
Definition: grima-lib.php:758
getItemBC($barcode)
Retrieve Item and print label information (by barcode))
Definition: grima-lib.php:730
post($url, $URLparams, $QSparams, $body)
general function for POST (create) API calls
Definition: grima-lib.php:314
deleteHolding($mms_id, $holding_id, $override='false')
Delete Holdings Record - delete the holdings record from Alma.
Definition: grima-lib.php:625
getElectronicPortfoliosForBib($mms_id, $limit, $offset)
Retrieve Portfolios list (Bib) - retrieve a list of portfolios from Alma.
Definition: grima-lib.php:1042
getHolding($mms_id, $holding_id)
Retrieve Holdings Record - retrieve holdings record from Alma.
Definition: grima-lib.php:544
postBib($bib)
Create Record - adds a new bib record to Alma.
Definition: grima-lib.php:418
deleteElectronicPortfolio($collection_id, $service_id, $portfolio_id)
Delete Electronic Portfolio - delete portfolio from Alma.
Definition: grima-lib.php:990
getLocation($libraryCode, $locationCode)
Retrieve Location - retrieve a Library Location from Alma.
Definition: grima-lib.php:1237
getElectronicServices($collection_id)
Retrieve Electronic Services - retrieve a list of services from a collection in Alma.
Definition: grima-lib.php:1117
putElectronicPortfolioOnBib($mms_id, $portfolio_id, $portfolio)
Update Electronic Portfolio - update portfolio in Alma.
Definition: grima-lib.php:964
putHolding($mms_id, $holding_id, $holding)
Update Holdings Record - replace the holdings record in Alma.
Definition: grima-lib.php:599
checkForErrorMessage($xml)
checks for errorMessage tag, throws exceptions
Definition: grima-lib.php:357
putItem($mms_id, $holding_id, $item_pid, $item)
Update Item information - replace item record in Alma.
Definition: grima-lib.php:786
getItem($mms_id, $holding_id, $item_pid)
Retrieve Item and print label information.
Definition: grima-lib.php:704
deleteItem($mms_id, $holding_id, $item_pid, $override="false", $holdings="retain")
Withdraw Item - delete an item record from Alma.
Definition: grima-lib.php:813
getElectronicPortfolioFromBib($mms_id, $portfolio_id)
Retrieve Portfolio - retrieve a portfolio record from Alma.
Definition: grima-lib.php:882
postElectronicPortfolio($collection_id, $service_id, $portfolio)
Create Electronic Portfolio - add a new portfolio to Alma.
Definition: grima-lib.php:908
getAnalytics($path, $filter, $limit=25, $token=null)
Definition: grima-lib.php:1457
getElectronicPortfolio($collection_id, $service_id, $portfolio_id)
Retrieve Portfolio - retrieve a portfolio record from Alma.
Definition: grima-lib.php:857
postSet($set)
Create a Set - add a new set to Alma.
Definition: grima-lib.php:1300
class Holding
Definition: grima-lib.php:3034
addToAlmaBib($mms_id)
adds a new holding record to the specified bib
Definition: grima-lib.php:3187
moveToBib($mms_id)
moves the holding from one bib to another – only for empty holdings!
Definition: grima-lib.php:3394
static getMmsFromHoldingID($holding_id)
populates the MMS ID
Definition: grima-lib.php:3128
deleteFieldMatching($tag, $code, $regex)
delete fields with subfields matching a regex
Definition: grima-lib.php:3477
deleteFromAlma()
delete the holding record from Alma
Definition: grima-lib.php:3224
offsetGet($offset)
Definition: grima-lib.php:3052
$items
Definition: grima-lib.php:3036
getMethodOfAcquisition()
retrieves method of acquisition, fixed field 008/07 https://www.loc.gov/marc/holdings/hd008....
Definition: grima-lib.php:3237
deleteSubfieldMatching($tag, $code, $regex)
delete subfields matching a regex
Definition: grima-lib.php:3452
offsetSet($offset, $value)
Definition: grima-lib.php:3039
updateAlma()
update holding record in Alma
Definition: grima-lib.php:3211
setMethodOfAcquisition($code)
sets method of acquisition, fixed field 008/07 https://www.loc.gov/marc/holdings/hd008....
Definition: grima-lib.php:3252
loadFromAlmaX($holding_id)
populates the record from Alma - only requires holding_id
Definition: grima-lib.php:3171
$xml
Definition: grima-lib.php:3037
getItemList()
populates itemList property from Alma
Definition: grima-lib.php:3417
$itemList
Definition: grima-lib.php:3035
getLibraryCode()
retrieves library code from 852b
Definition: grima-lib.php:3292
deleteTreeFromAlma()
delete the holding and all of its items
Definition: grima-lib.php:3429
getMmsIfNeeded()
populates the MMS ID if not already known
Definition: grima-lib.php:3155
getItems()
populate items property with Items objects ## XXX TEST
Definition: grima-lib.php:3405
setCallNumber($h, $i, $ind1) # upgrade to normac
Definition: grima-lib.php:3368
setLibraryCode($code)
sets library code from 852b
Definition: grima-lib.php:3305
$el_address
Definition: grima-lib.php:3090
setLocationCode($code)
sets location code from 852c
Definition: grima-lib.php:3331
getInstitutionCode()
retrieves institution code from 852a
Definition: grima-lib.php:3266
loadFromAlma($mms_id, $holding_id)
populates the record from Alma
Definition: grima-lib.php:3116
getLocationCode()
retrieves location code from 852c
Definition: grima-lib.php:3318
setCallNumberType($ind1) # upgrade to normac
Definition: grima-lib.php:3339
hasItems()
checks whether a holding record has any items
Definition: grima-lib.php:3200
setInstitutionCode($code)
sets institution code from 852a
Definition: grima-lib.php:3279
class HoldingsListEntry
Definition: grima-lib.php:2954
__construct($node, $mms_id)
Definition: grima-lib.php:2965
getItemList($limit=-1)
Definition: grima-lib.php:2972
class HoldingsList
Definition: grima-lib.php:2915
__construct($mms_id=null)
Definition: grima-lib.php:2925
loadFromAlma($mms_id)
Definition: grima-lib.php:2932
class ItemList
Definition: grima-lib.php:2987
$items
Definition: grima-lib.php:2988
__construct($mms_id, $holding_id, $limit=-1)
Definition: grima-lib.php:2990
class Item
Definition: grima-lib.php:3501
addToAlmaHolding($mms_id, $holding_id)
add new item record to holding in Alma
Definition: grima-lib.php:3672
loadFromItemListNode($node)
populate item record from the information in an ItemList node
Definition: grima-lib.php:3658
updateAlma()
replace item record in Alma
Definition: grima-lib.php:3701
loadFromAlma($mms_id, $holding_id, $item_pid)
populates item record from Alma
Definition: grima-lib.php:3602
addToAlmaHoldingX($holding_id)
add new item record to holding in Alma
Definition: grima-lib.php:3689
fluff()
populates item record from Alma
Definition: grima-lib.php:3573
loadFromAlmaBarcode($barcode)
populates item record from Alma, using barcode
Definition: grima-lib.php:3626
$el_address
Definition: grima-lib.php:3503
loadFromAlmaBCorX($id)
populates item record from Alma using either identifier
Definition: grima-lib.php:3640
loadFromAlmaX($item_pid)
populates item record from Alma, only needs item_pid
Definition: grima-lib.php:3614
deleteFromAlma($override="false", $holdings="retain")
delete record from Alma
Definition: grima-lib.php:3720
class Job
Definition: grima-lib.php:4531
addParameter($name, $value)
add a parameter to the job
Definition: grima-lib.php:4553
runInAlma()
run a job in Alma
Definition: grima-lib.php:4571
$el_address
Definition: grima-lib.php:4532
class Library
Definition: grima-lib.php:4316
$el_address
Definition: grima-lib.php:4317
static getOneLibraryLocation($otherthan=array())
get one library configured by the institution
Definition: grima-lib.php:4438
static getOneLibrary($otherthan=array())
get one library configured by the institution
Definition: grima-lib.php:4366
getAllLocations()
get all locations configured for the library
Definition: grima-lib.php:4390
loadFromAlma($code)
populate library with info from Alma
Definition: grima-lib.php:4330
static getAllLibraries()
get all libraries configured for the institution
Definition: grima-lib.php:4343
getOneLocation($otherthan=array())
get one location configured for the library
Definition: grima-lib.php:4413
class Location
Definition: grima-lib.php:4475
$el_address
Definition: grima-lib.php:4479
$el_override_fields
Definition: grima-lib.php:4477
loadFromAlma($libraryCode, $locationCode)
populate location with info from Alma
Definition: grima-lib.php:4514
offsetSet($offset, $value)
Definition: grima-lib.php:4492
class SetMember
Definition: grima-lib.php:4298
__construct($id, $description)
Definition: grima-lib.php:4302
$description
Definition: grima-lib.php:4300
class Set IN PROGRESS
Definition: grima-lib.php:4098
createFromImport($job_id, $population)
create a new set in Alma based on a job that just ran
Definition: grima-lib.php:4162
removeAllMembersInAlma()
remove all members of the set
Definition: grima-lib.php:4272
runOnElements($function, $args=array(), $filter=null, $filter_args=array())
run a function on every element in the set
Definition: grima-lib.php:4177
$xml
Definition: grima-lib.php:4099
getMembers($limit=-1)
Definition: grima-lib.php:4198
appendInAlma($setToAppend)
add elements to a set
Definition: grima-lib.php:4258
$el_address
Definition: grima-lib.php:4112
loadFromAlma($set_id)
populate set with info from Alma
Definition: grima-lib.php:4148
addMember($id)
add member to a local set
Definition: grima-lib.php:4239
addToAlma()
adds Set to Alma, updates object with current Alma version
Definition: grima-lib.php:4135
deleteFromAlma()
delete set from Alma
Definition: grima-lib.php:4226
$members
Definition: grima-lib.php:4100
bib records returned from alma
Simple template engine.
Definition: grima-splats.php:12
tableExists($pdo, $table)
Definition: grima-lib.php:4642
$ds_barcode
Definition: grima-lib.php:1900
$grima
Definition: grima-lib.php:5143
getenvWithFileFallbackAndDefault( $env_name, $default=false)
Definition: grima-util.php:52
arrayToXml($array, &$xml)
Definition: grima-util.php:77
do_redirect($url)
Definition: grima-util.php:45
appendInnerXML( $elt, $xmlString)
Definition: grima-util.php:20
join_paths(... $paths)
Definition: grima-util.php:156