Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.95% |
3 / 316 |
|
5.26% |
1 / 19 |
CRAP | |
0.00% |
0 / 1 |
Evergreen | |
0.95% |
3 / 316 |
|
5.26% |
1 / 19 |
3214.34 | |
0.00% |
0 / 1 |
__construct | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
init | |
11.76% |
2 / 17 |
|
0.00% |
0 / 1 |
9.18 | |||
getStatus | |
0.00% |
0 / 30 |
|
0.00% |
0 / 1 |
42 | |||
getStatuses | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
6 | |||
getHolding | |
0.00% |
0 / 36 |
|
0.00% |
0 / 1 |
56 | |||
getPurchaseHistory | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
patronLogin | |
0.00% |
0 / 24 |
|
0.00% |
0 / 1 |
30 | |||
getMyTransactions | |
0.00% |
0 / 58 |
|
0.00% |
0 / 1 |
56 | |||
getMyFines | |
0.00% |
0 / 35 |
|
0.00% |
0 / 1 |
12 | |||
getMyHolds | |
0.00% |
0 / 37 |
|
0.00% |
0 / 1 |
12 | |||
getMyProfile | |
0.00% |
0 / 28 |
|
0.00% |
0 / 1 |
30 | |||
getNewItems | |
0.00% |
0 / 26 |
|
0.00% |
0 / 1 |
20 | |||
getFunds | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getSuppressedRecords | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
12 | |||
getDepartments | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getInstructors | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getCourses | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
findReserves | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
formatDate | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 |
1 | <?php |
2 | |
3 | /** |
4 | * Evergreen ILS Driver |
5 | * |
6 | * PHP version 8 |
7 | * |
8 | * Copyright (C) Villanova University 2007. |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2, |
12 | * as published by the Free Software Foundation. |
13 | * |
14 | * This program is distributed in the hope that it will be useful, |
15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | * GNU General Public License for more details. |
18 | * |
19 | * You should have received a copy of the GNU General Public License |
20 | * along with this program; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | * |
23 | * @category VuFind |
24 | * @package ILS_Drivers |
25 | * @author Warren Layton, NRCan Library <warren.layton@gmail.com> |
26 | * @author Galen Charlton, Equinox <gmcharlt@equinoxOLI.org> |
27 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
28 | * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki |
29 | */ |
30 | |
31 | namespace VuFind\ILS\Driver; |
32 | |
33 | use PDO; |
34 | use PDOException; |
35 | use VuFind\Date\DateException; |
36 | use VuFind\Exception\ILS as ILSException; |
37 | |
38 | use function count; |
39 | |
40 | /** |
41 | * VuFind Connector for Evergreen |
42 | * |
43 | * Written by Warren Layton at the NRCan (Natural Resources Canada) |
44 | * Library. |
45 | * |
46 | * @category VuFind |
47 | * @package ILS_Drivers |
48 | * @author Warren Layton, NRCan Library <warren.layton@gmail.com> |
49 | * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License |
50 | * @link https://vufind.org/wiki/development:plugins:ils_drivers Wiki |
51 | */ |
52 | class Evergreen extends AbstractBase implements \Laminas\Log\LoggerAwareInterface |
53 | { |
54 | use \VuFind\Log\LoggerAwareTrait; |
55 | |
56 | /** |
57 | * Database connection |
58 | * |
59 | * @var PDO |
60 | */ |
61 | protected $db; |
62 | |
63 | /** |
64 | * Database name |
65 | * |
66 | * @var string |
67 | */ |
68 | protected $dbName; |
69 | |
70 | /** |
71 | * Date converter object |
72 | * |
73 | * @var \VuFind\Date\Converter |
74 | */ |
75 | protected $dateConverter; |
76 | |
77 | /** |
78 | * Constructor |
79 | * |
80 | * @param \VuFind\Date\Converter $dateConverter Date converter |
81 | */ |
82 | public function __construct(\VuFind\Date\Converter $dateConverter) |
83 | { |
84 | $this->dateConverter = $dateConverter; |
85 | } |
86 | |
87 | /** |
88 | * Evergreen constants |
89 | */ |
90 | public const EVG_ITEM_STATUS_IN_TRANSIT = '6'; |
91 | |
92 | /** |
93 | * Initialize the driver. |
94 | * |
95 | * Validate configuration and perform all resource-intensive tasks needed to |
96 | * make the driver active. |
97 | * |
98 | * @throws ILSException |
99 | * @throws PDOException |
100 | * @return void |
101 | */ |
102 | public function init() |
103 | { |
104 | if (empty($this->config)) { |
105 | throw new ILSException('Configuration needs to be set.'); |
106 | } |
107 | |
108 | // Define Database Name |
109 | $this->dbName = $this->config['Catalog']['database']; |
110 | |
111 | try { |
112 | $this->db = new PDO( |
113 | 'pgsql:host=' |
114 | . $this->config['Catalog']['hostname'] |
115 | . ' user=' |
116 | . $this->config['Catalog']['user'] |
117 | . ' dbname=' |
118 | . $this->config['Catalog']['database'] |
119 | . ' password=' |
120 | . $this->config['Catalog']['password'] |
121 | . ' port=' |
122 | . $this->config['Catalog']['port'] |
123 | ); |
124 | } catch (PDOException $e) { |
125 | throw $e; |
126 | } |
127 | } |
128 | |
129 | /** |
130 | * Get Status |
131 | * |
132 | * This is responsible for retrieving the status information of a certain |
133 | * record. |
134 | * |
135 | * @param string $id The record id to retrieve the holdings for |
136 | * |
137 | * @throws ILSException |
138 | * @return mixed On success, an associative array with the following keys: |
139 | * id, availability (boolean), status, location, reserve, callnumber. |
140 | */ |
141 | public function getStatus($id) |
142 | { |
143 | $holding = []; |
144 | |
145 | // Build SQL Statement |
146 | $sql = <<<HERE |
147 | SELECT ccs.name AS status, acn.label AS callnumber, aou.name AS location |
148 | FROM config.copy_status ccs |
149 | INNER JOIN asset.copy ac ON ac.status = ccs.id |
150 | INNER JOIN asset.call_number acn ON acn.id = ac.call_number |
151 | INNER JOIN actor.org_unit aou ON aou.id = ac.circ_lib |
152 | WHERE |
153 | acn.record = ? AND |
154 | NOT ac.deleted |
155 | HERE; |
156 | |
157 | // Execute SQL |
158 | try { |
159 | $holding = []; |
160 | $sqlStmt = $this->db->prepare($sql); |
161 | $sqlStmt->bindParam(1, $id, PDO::PARAM_INT); |
162 | $sqlStmt->execute(); |
163 | } catch (PDOException $e) { |
164 | $this->throwAsIlsException($e); |
165 | } |
166 | |
167 | // Build Holdings Array |
168 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
169 | switch ($row['status']) { |
170 | case 'Available': |
171 | $available = true; |
172 | $reserve = false; |
173 | break; |
174 | case 'On holds shelf': |
175 | $available = false; |
176 | $reserve = true; |
177 | break; |
178 | default: |
179 | $available = false; |
180 | $reserve = false; |
181 | break; |
182 | } |
183 | |
184 | $holding[] = [ |
185 | 'id' => $id, |
186 | 'availability' => $available, |
187 | 'status' => $row['status'], |
188 | 'location' => $row['location'], |
189 | 'reserve' => $reserve, |
190 | 'callnumber' => $row['callnumber'], |
191 | ]; |
192 | } |
193 | |
194 | return $holding; |
195 | } |
196 | |
197 | /** |
198 | * Get Statuses |
199 | * |
200 | * This is responsible for retrieving the status information for a |
201 | * collection of records. |
202 | * |
203 | * @param array $idList The array of record ids to retrieve the status for |
204 | * |
205 | * @throws ILSException |
206 | * @return array An array of getStatus() return values on success. |
207 | */ |
208 | public function getStatuses($idList) |
209 | { |
210 | $status = []; |
211 | foreach ($idList as $id) { |
212 | $status[] = $this->getStatus($id); |
213 | } |
214 | return $status; |
215 | } |
216 | |
217 | /** |
218 | * Get Holding |
219 | * |
220 | * This is responsible for retrieving the holding information of a certain |
221 | * record. |
222 | * |
223 | * @param string $id The record id to retrieve the holdings for |
224 | * @param array $patron Patron data |
225 | * @param array $options Extra options (not currently used) |
226 | * |
227 | * @throws DateException |
228 | * @throws ILSException |
229 | * @return array On success, an associative array with the following |
230 | * keys: id, availability (boolean), status, location, reserve, callnumber, |
231 | * duedate, number, barcode. |
232 | * |
233 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
234 | */ |
235 | public function getHolding($id, array $patron = null, array $options = []) |
236 | { |
237 | $holding = []; |
238 | |
239 | // Build SQL Statement |
240 | $sql = <<<HERE |
241 | SELECT ccs.name AS status, acn.label AS callnumber, aou.name AS location, |
242 | ac.copy_number, ac.barcode, |
243 | extract (year from circ.due_date) as due_year, |
244 | extract (month from circ.due_date) as due_month, |
245 | extract (day from circ.due_date) as due_day |
246 | FROM config.copy_status ccs |
247 | INNER JOIN asset.copy ac ON ac.status = ccs.id |
248 | INNER JOIN asset.call_number acn ON acn.id = ac.call_number |
249 | INNER JOIN actor.org_unit aou ON aou.id = ac.circ_lib |
250 | FULL JOIN action.circulation circ ON ( |
251 | ac.id = circ.target_copy AND circ.checkin_time IS NULL |
252 | ) |
253 | WHERE |
254 | acn.record = ? AND |
255 | NOT ac.deleted |
256 | HERE; |
257 | |
258 | // Execute SQL |
259 | try { |
260 | $sqlStmt = $this->db->prepare($sql); |
261 | $sqlStmt->bindParam(1, $id, PDO::PARAM_INT); |
262 | $sqlStmt->execute(); |
263 | } catch (PDOException $e) { |
264 | $this->throwAsIlsException($e); |
265 | } |
266 | |
267 | // Build Holdings Array |
268 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
269 | switch ($row['status']) { |
270 | case 'Available': |
271 | $available = true; |
272 | $reserve = false; |
273 | break; |
274 | case 'On holds shelf': |
275 | // Instead of relying on status = 'On holds shelf', |
276 | // I might want to see if: |
277 | // action.hold_request.current_copy = asset.copy.id |
278 | // and action.hold_request.capture_time is not null |
279 | // and I think action.hold_request.fulfillment_time is null |
280 | $available = false; |
281 | $reserve = true; |
282 | break; |
283 | default: |
284 | $available = false; |
285 | $reserve = false; |
286 | break; |
287 | } |
288 | |
289 | if ($row['due_year']) { |
290 | $due_date = $row['due_year'] . '-' . $row['due_month'] . '-' . |
291 | $row['due_day']; |
292 | } else { |
293 | $due_date = ''; |
294 | } |
295 | $holding[] = [ |
296 | 'id' => $id, |
297 | 'availability' => $available, |
298 | 'status' => $row['status'], |
299 | 'location' => $row['location'], |
300 | 'reserve' => $reserve, |
301 | 'callnumber' => $row['callnumber'], |
302 | 'duedate' => $due_date, |
303 | 'number' => $row['copy_number'], |
304 | 'barcode' => $row['barcode'], |
305 | ]; |
306 | } |
307 | |
308 | return $holding; |
309 | } |
310 | |
311 | /** |
312 | * Get Purchase History |
313 | * |
314 | * This is responsible for retrieving the acquisitions history data for the |
315 | * specific record (usually recently received issues of a serial). |
316 | * |
317 | * @param string $id The record id to retrieve the info for |
318 | * |
319 | * @throws ILSException |
320 | * @return array An array with the acquisitions data on success. |
321 | * |
322 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
323 | */ |
324 | public function getPurchaseHistory($id) |
325 | { |
326 | // TODO |
327 | return []; |
328 | } |
329 | |
330 | /** |
331 | * Patron Login |
332 | * |
333 | * This is responsible for authenticating a patron against the catalog. |
334 | * |
335 | * @param string $barcode The patron username OR barcode number |
336 | * @param string $passwd The patron password |
337 | * |
338 | * @throws ILSException |
339 | * @return mixed Associative array of patron info on successful login, |
340 | * null on unsuccessful login. |
341 | */ |
342 | public function patronLogin($barcode, $passwd) |
343 | { |
344 | $sql = <<<HERE |
345 | SELECT usr.id, usr.first_given_name as firstName, |
346 | usr.family_name as lastName, usr.email, usrname |
347 | FROM actor.usr usr |
348 | INNER JOIN actor.card ON usr.card = card.id |
349 | WHERE card.active = true |
350 | AND actor.verify_passwd(usr.id, 'main', |
351 | MD5(actor.get_salt(usr.id, 'main') || MD5(?))) |
352 | HERE; |
353 | if (is_numeric($barcode)) { |
354 | // A barcode was supplied as ID |
355 | $sql .= 'AND card.barcode = ?'; |
356 | } else { |
357 | // A username was supplied as ID |
358 | $sql .= 'AND usr.usrname = ?'; |
359 | } |
360 | |
361 | try { |
362 | $sqlStmt = $this->db->prepare($sql); |
363 | $sqlStmt->bindParam(1, $passwd, PDO::PARAM_STR); |
364 | $sqlStmt->bindParam(2, $barcode, PDO::PARAM_STR); |
365 | $sqlStmt->execute(); |
366 | $row = $sqlStmt->fetch(PDO::FETCH_ASSOC); |
367 | if (isset($row['id']) && ($row['id'] != '')) { |
368 | $return = []; |
369 | $return['id'] = $row['id']; |
370 | $return['firstname'] = $row['firstname']; |
371 | $return['lastname'] = $row['lastname']; |
372 | $return['cat_username'] = $row['usrname']; |
373 | $return['cat_password'] = $passwd; |
374 | $return['email'] = $row['email']; |
375 | $return['major'] = null; // Don't know which table this comes from |
376 | $return['college'] = null; // Don't know which table this comes from |
377 | return $return; |
378 | } else { |
379 | return null; |
380 | } |
381 | } catch (PDOException $e) { |
382 | $this->throwAsIlsException($e); |
383 | } |
384 | } |
385 | |
386 | /** |
387 | * Get Patron Transactions |
388 | * |
389 | * This is responsible for retrieving all transactions (i.e. checked out items) |
390 | * by a specific patron. |
391 | * |
392 | * @param array $patron The patron array from patronLogin |
393 | * |
394 | * @throws DateException |
395 | * @throws ILSException |
396 | * @return array Array of the patron's transactions on success. |
397 | */ |
398 | public function getMyTransactions($patron) |
399 | { |
400 | $transList = []; |
401 | |
402 | $sql = 'select call_number.record as bib_id, ' . |
403 | 'circulation.due_date as due_date, ' . |
404 | 'circulation.target_copy as item_id, ' . |
405 | 'circulation.renewal_remaining as renewal_remaining, ' . |
406 | 'aou_circ.name as borrowing_location, ' . |
407 | 'aou_own.name as owning_library, ' . |
408 | 'copy.barcode as barcode ' . |
409 | "from $this->dbName.action.circulation " . |
410 | "join $this->dbName.asset.copy ON " . |
411 | ' (circulation.target_copy = copy.id) ' . |
412 | "join $this->dbName.asset.call_number ON " . |
413 | ' (copy.call_number = call_number.id) ' . |
414 | "join $this->dbName.actor.org_unit aou_circ ON " . |
415 | ' (circulation.circ_lib = aou_circ.id) ' . |
416 | "join $this->dbName.actor.org_unit aou_own ON " . |
417 | ' (call_number.owning_lib = aou_own.id) ' . |
418 | "where circulation.usr = '" . $patron['id'] . "' " . |
419 | 'and circulation.checkin_time is null ' . |
420 | 'and circulation.xact_finish is null'; |
421 | |
422 | try { |
423 | $sqlStmt = $this->db->prepare($sql); |
424 | $sqlStmt->execute(); |
425 | |
426 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
427 | $due_date = $this->formatDate($row['due_date']); |
428 | $_due_time = new \DateTime($row['due_date']); |
429 | if ($_due_time->format('H:i:s') == '23:59:59') { |
430 | $dueTime = ''; // don't display due time for non-hourly loans |
431 | } else { |
432 | $dueTime = $this->dateConverter->convertToDisplayTime( |
433 | 'Y-m-d H:i', |
434 | $row['due_date'] |
435 | ); |
436 | } |
437 | |
438 | $today = new \DateTime(); |
439 | $now = time(); |
440 | // since Evergreen normalizes the due time of non-hourly |
441 | // loans to be 23:59:59, we use a slightly flexible definition |
442 | // of "due in 24 hours" |
443 | $end_of_today = strtotime($today->format('Y-m-d 23:59:59')); |
444 | $dueTimeStamp = strtotime($row['due_date']); |
445 | $dueStatus = false; |
446 | if (is_numeric($dueTimeStamp)) { |
447 | $_dueTimeLessDay = $dueTimeStamp - (1 * 24 * 60 * 60) - 1; |
448 | if ($now > $dueTimeStamp) { |
449 | $dueStatus = 'overdue'; |
450 | } elseif ($end_of_today > $_dueTimeLessDay) { |
451 | $dueStatus = 'due'; |
452 | } |
453 | } |
454 | |
455 | $transList[] = [ |
456 | 'duedate' => $due_date, |
457 | 'dueTime' => $dueTime, |
458 | 'id' => $row['bib_id'], |
459 | 'barcode' => $row['barcode'], |
460 | 'item_id' => $row['item_id'], |
461 | 'renewLimit' => $row['renewal_remaining'], |
462 | 'renewable' => $row['renewal_remaining'] > 1, |
463 | 'institution_name' => $row['owning_library'], |
464 | 'borrowingLocation' => |
465 | $row['borrowing_location'], |
466 | 'dueStatus' => $dueStatus, |
467 | ]; |
468 | } |
469 | } catch (PDOException $e) { |
470 | $this->throwAsIlsException($e); |
471 | } |
472 | return ['count' => count($transList), 'records' => $transList]; |
473 | } |
474 | |
475 | /** |
476 | * Get Patron Fines |
477 | * |
478 | * This is responsible for retrieving all fines by a specific patron. |
479 | * |
480 | * @param array $patron The patron array from patronLogin |
481 | * |
482 | * @throws DateException |
483 | * @throws ILSException |
484 | * @return mixed Array of the patron's fines on success. |
485 | */ |
486 | public function getMyFines($patron) |
487 | { |
488 | $fineList = []; |
489 | |
490 | $sql = 'select billable_xact_summary.total_owed * 100 as total_owed, ' . |
491 | 'billable_xact_summary.balance_owed * 100 as balance_owed, ' . |
492 | 'billable_xact_summary.last_billing_type, ' . |
493 | 'billable_xact_summary.last_billing_ts, ' . |
494 | 'billable_circulations.create_time as checkout_time, ' . |
495 | 'billable_circulations.due_date, ' . |
496 | 'billable_circulations.target_copy, ' . |
497 | 'call_number.record ' . |
498 | "from $this->dbName.money.billable_xact_summary " . |
499 | "LEFT JOIN $this->dbName.action.billable_circulations " . |
500 | 'ON (billable_xact_summary.id = billable_circulations.id ' . |
501 | ' and billable_circulations.xact_finish is null) ' . |
502 | "LEFT JOIN $this->dbName.asset.copy ON " . |
503 | ' (billable_circulations.target_copy = copy.id) ' . |
504 | "LEFT JOIN $this->dbName.asset.call_number ON " . |
505 | ' (copy.call_number = call_number.id) ' . |
506 | "where billable_xact_summary.usr = '" . $patron['id'] . "' " . |
507 | 'and billable_xact_summary.total_owed <> 0 ' . |
508 | 'and billable_xact_summary.xact_finish is null'; |
509 | |
510 | try { |
511 | $sqlStmt = $this->db->prepare($sql); |
512 | $sqlStmt->execute(); |
513 | |
514 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
515 | $fineList[] = [ |
516 | 'amount' => $row['total_owed'], |
517 | 'fine' => $row['last_billing_type'], |
518 | 'balance' => $row['balance_owed'], |
519 | 'checkout' => $this->formatDate($row['checkout_time']), |
520 | 'createdate' => $this->formatDate($row['last_billing_ts']), |
521 | 'duedate' => $this->formatDate($row['due_date']), |
522 | 'id' => $row['record'], |
523 | ]; |
524 | } |
525 | return $fineList; |
526 | } catch (PDOException $e) { |
527 | $this->throwAsIlsException($e); |
528 | } |
529 | } |
530 | |
531 | /** |
532 | * Get Patron Holds |
533 | * |
534 | * This is responsible for retrieving all holds by a specific patron. |
535 | * |
536 | * @param array $patron The patron array from patronLogin |
537 | * |
538 | * @throws DateException |
539 | * @throws ILSException |
540 | * @return array Array of the patron's holds on success. |
541 | */ |
542 | public function getMyHolds($patron) |
543 | { |
544 | $holdList = []; |
545 | |
546 | $sql = 'select ahr.hold_type, bib_record, ' . |
547 | 'ahr.id as hold_id, ' . |
548 | 'expire_time, request_time, shelf_time, capture_time, ' . |
549 | 'shelf_time, shelf_expire_time, frozen, thaw_date, ' . |
550 | 'org_unit.name as lib_name, acp.status as copy_status ' . |
551 | "from $this->dbName.action.hold_request ahr " . |
552 | "join $this->dbName.actor.org_unit on " . |
553 | ' (ahr.pickup_lib = org_unit.id) ' . |
554 | "join $this->dbName.reporter.hold_request_record rhrr on " . |
555 | ' (rhrr.id = ahr.id) ' . |
556 | "left join $this->dbName.asset.copy acp on " . |
557 | ' (acp.id = ahr.current_copy) ' . |
558 | "where ahr.usr = '" . $patron['id'] . "' " . |
559 | 'and ahr.fulfillment_time is null ' . |
560 | 'and ahr.cancel_time is null'; |
561 | |
562 | try { |
563 | $sqlStmt = $this->db->prepare($sql); |
564 | $sqlStmt->execute(); |
565 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
566 | $holdList[] = [ |
567 | 'type' => $row['hold_type'], |
568 | 'id' => $row['bib_record'], |
569 | 'reqnum' => $row['hold_id'], |
570 | 'location' => $row['lib_name'], |
571 | 'expire' => $this->formatDate($row['expire_time']), |
572 | 'last_pickup_date' => |
573 | $this->formatDate($row['shelf_expire_time']), |
574 | 'available' => $row['shelf_time'], |
575 | 'frozen' => $row['frozen'], |
576 | 'frozenThrough' => $this->formatDate($row['thaw_date']), |
577 | 'create' => $this->formatDate($row['request_time']), |
578 | 'in_transit' => |
579 | $row['copy_status'] == self::EVG_ITEM_STATUS_IN_TRANSIT, |
580 | ]; |
581 | } |
582 | } catch (PDOException $e) { |
583 | $this->throwAsIlsException($e); |
584 | } |
585 | return $holdList; |
586 | } |
587 | |
588 | /** |
589 | * Get Patron Profile |
590 | * |
591 | * This is responsible for retrieving the profile for a specific patron. |
592 | * |
593 | * @param array $patron The patron array |
594 | * |
595 | * @throws ILSException |
596 | * @return array Array of the patron's profile data on success. |
597 | */ |
598 | public function getMyProfile($patron) |
599 | { |
600 | $sql = <<<HERE |
601 | SELECT usr.family_name, usr.first_given_name, usr.day_phone, |
602 | usr.evening_phone, usr.other_phone, aua.street1, |
603 | aua.street2, aua.post_code, pgt.name AS usrgroup, |
604 | aua.city, aua.country, usr.expire_date |
605 | FROM actor.usr |
606 | FULL JOIN actor.usr_address aua ON aua.id = usr.mailing_address |
607 | INNER JOIN permission.grp_tree pgt ON pgt.id = usr.profile |
608 | WHERE usr.active = true |
609 | AND usr.id = ? |
610 | HERE; |
611 | |
612 | try { |
613 | $sqlStmt = $this->db->prepare($sql); |
614 | $sqlStmt->bindParam(1, $patron['id'], PDO::PARAM_INT); |
615 | $sqlStmt->execute(); |
616 | $row = $sqlStmt->fetch(PDO::FETCH_ASSOC); |
617 | |
618 | if ($row['day_phone']) { |
619 | $phone = $row['day_phone']; |
620 | } elseif ($row['evening_phone']) { |
621 | $phone = $row['evening_phone']; |
622 | } else { |
623 | $phone = $row['other_phone']; |
624 | } |
625 | |
626 | if ($row) { |
627 | $patron = [ |
628 | 'firstname' => $row['first_given_name'], |
629 | 'lastname' => $row['family_name'], |
630 | 'address1' => $row['street1'], |
631 | 'address2' => $row['street2'], |
632 | 'city' => $row['city'], |
633 | 'zip' => $row['post_code'], |
634 | 'country' => $row['country'], |
635 | 'phone' => $phone, |
636 | 'group' => $row['usrgroup'], |
637 | 'expiration_date' => $this->formatDate($row['expire_date']), |
638 | ]; |
639 | return $patron; |
640 | } |
641 | } catch (PDOException $e) { |
642 | $this->throwAsIlsException($e); |
643 | } |
644 | return null; |
645 | } |
646 | |
647 | /** |
648 | * Only one of the following 2 function should be implemented. |
649 | * Placing a hold directly can be done with placeHold. |
650 | * Otherwise, getHoldLink will link to Evergreen's page to place |
651 | * a hold via the ILS. |
652 | */ |
653 | |
654 | /** |
655 | * Place Hold |
656 | * |
657 | * Attempts to place a hold or recall on a particular item and returns |
658 | * an array with result details or throws an exception on failure of support |
659 | * classes |
660 | * |
661 | * @param array $holdDetails An array of item and patron data |
662 | * |
663 | * @throws ILSException |
664 | * @return mixed An array of data on the request including |
665 | * whether or not it was successful and a system message (if available) |
666 | */ |
667 | //public function placeHold($holdDetails) |
668 | //{ |
669 | // Need to check asset.copy.status -> config.copy_status.holdable = true |
670 | // If it is holdable, place hold in action.hold_request: |
671 | // request_time to now, current_copy to asset.copy.id, |
672 | // usr to action.usr.id of requesting patron, |
673 | // phone_notify to phone number, email_notify to t/f |
674 | // set pickup_lib too? |
675 | |
676 | /* |
677 | $sql = ""; |
678 | |
679 | try { |
680 | $sqlStmt = $this->db->prepare($sql); |
681 | $sqlStmt->execute(); |
682 | } catch (PDOException $e) { |
683 | $this->throwAsIlsException($e); |
684 | } |
685 | */ |
686 | //} |
687 | |
688 | /** |
689 | * Get Hold Link |
690 | * |
691 | * The goal for this method is to return a URL to a "place hold" web page on |
692 | * the ILS OPAC. This is used for ILSs that do not support an API or method |
693 | * to place Holds. |
694 | * |
695 | * @param string $recordId The id of the bib record |
696 | * @param array $details Item details from getHoldings return array |
697 | * |
698 | * @return string URL to ILS's OPAC's place hold screen. |
699 | */ |
700 | //public function getHoldLink($recordId, $details) |
701 | //{ |
702 | //} |
703 | |
704 | /** |
705 | * Get New Items |
706 | * |
707 | * Retrieve the IDs of items recently added to the catalog. |
708 | * |
709 | * @param int $page Page number of results to retrieve (counting starts at 1) |
710 | * @param int $limit The size of each page of results to retrieve |
711 | * @param int $daysOld The maximum age of records to retrieve in days (max. 30) |
712 | * @param int $fundId optional fund ID to use for limiting results (use a value |
713 | * returned by getFunds, or exclude for no limit); note that "fund" may be a |
714 | * misnomer - if funds are not an appropriate way to limit your new item |
715 | * results, you can return a different set of values from getFunds. The |
716 | * important thing is that this parameter supports an ID returned by getFunds, |
717 | * whatever that may mean. |
718 | * |
719 | * @throws ILSException |
720 | * @return array Associative array with 'count' and 'results' keys |
721 | * |
722 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
723 | */ |
724 | public function getNewItems($page, $limit, $daysOld, $fundId = null) |
725 | { |
726 | $items = []; |
727 | |
728 | $enddate = date('Y-m-d', strtotime('now')); |
729 | $startdate = date('Y-m-d', strtotime("-$daysOld day")); |
730 | |
731 | $sql = 'select count(distinct copy.id) as count ' . |
732 | 'from asset.copy ' . |
733 | "where copy.create_date >= '$startdate' " . |
734 | 'and copy.status = 0 ' . |
735 | "and copy.create_date < '$enddate' LIMIT 50"; |
736 | |
737 | try { |
738 | $sqlStmt = $this->db->prepare($sql); |
739 | $sqlStmt->execute(); |
740 | $row = $sqlStmt->fetch(PDO::FETCH_ASSOC); |
741 | $items['count'] = $row['count']; |
742 | } catch (PDOException $e) { |
743 | $this->throwAsIlsException($e); |
744 | } |
745 | |
746 | // TODO: implement paging support |
747 | //$page = ($page) ? $page : 1; |
748 | //$limit = ($limit) ? $limit : 20; |
749 | //$startRow = (($page-1)*$limit)+1; |
750 | //$endRow = ($page*$limit); |
751 | |
752 | $sql = 'select copy.id, call_number.record from asset.copy ' . |
753 | 'join asset.call_number on (call_number.id = copy.call_number) ' . |
754 | "where copy.create_date >= '$startdate' " . |
755 | 'and copy.status = 0 ' . |
756 | "and copy.create_date < '$enddate' LIMIT 50"; |
757 | |
758 | try { |
759 | $sqlStmt = $this->db->prepare($sql); |
760 | $sqlStmt->execute(); |
761 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
762 | $items['results'][]['id'] = $row['record']; |
763 | } |
764 | } catch (PDOException $e) { |
765 | $this->throwAsIlsException($e); |
766 | } |
767 | return $items; |
768 | } |
769 | |
770 | /** |
771 | * Get Funds |
772 | * |
773 | * Return a list of funds which may be used to limit the getNewItems list. |
774 | * |
775 | * @throws ILSException |
776 | * @return array An associative array with key = fund ID, value = fund name. |
777 | */ |
778 | public function getFunds() |
779 | { |
780 | $list = []; |
781 | |
782 | /* TODO: |
783 | $sql = ""; |
784 | |
785 | try { |
786 | $sqlStmt = $this->db->prepare($sql); |
787 | $sqlStmt->execute(); |
788 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
789 | $list[] = $row['name']; |
790 | } |
791 | } catch (PDOException $e) { |
792 | $this->throwAsIlsException($e); |
793 | } |
794 | */ |
795 | |
796 | return $list; |
797 | } |
798 | |
799 | /** |
800 | * Get suppressed records. |
801 | * |
802 | * @throws ILSException |
803 | * @return array ID numbers of suppressed records in the system. |
804 | */ |
805 | public function getSuppressedRecords() |
806 | { |
807 | $list = []; |
808 | |
809 | $sql = 'select copy.id as id ' . |
810 | "from $this->dbName.asset " . |
811 | 'where copy.opac_visible = false'; |
812 | |
813 | try { |
814 | $sqlStmt = $this->db->prepare($sql); |
815 | $sqlStmt->execute(); |
816 | while ($row = $sqlStmt->fetch(PDO::FETCH_ASSOC)) { |
817 | $list[] = $row['id']; |
818 | } |
819 | } catch (PDOException $e) { |
820 | $this->throwAsIlsException($e); |
821 | } |
822 | |
823 | return $list; |
824 | } |
825 | |
826 | // *** The functions below are not (yet) applicable to Evergreen *** |
827 | |
828 | /** |
829 | * Get Departments |
830 | * |
831 | * Obtain a list of departments for use in limiting the reserves list. |
832 | * |
833 | * @throws ILSException |
834 | * @return array An associative array with key = dept. ID, value = dept. name. |
835 | */ |
836 | public function getDepartments() |
837 | { |
838 | // TODO |
839 | return []; |
840 | } |
841 | |
842 | /** |
843 | * Get Instructors |
844 | * |
845 | * Obtain a list of instructors for use in limiting the reserves list. |
846 | * |
847 | * @throws ILSException |
848 | * @return array An associative array with key = ID, value = name. |
849 | */ |
850 | public function getInstructors() |
851 | { |
852 | // TODO |
853 | return []; |
854 | } |
855 | |
856 | /** |
857 | * Get Courses |
858 | * |
859 | * Obtain a list of courses for use in limiting the reserves list. |
860 | * |
861 | * @throws ILSException |
862 | * @return array An associative array with key = ID, value = name. |
863 | */ |
864 | public function getCourses() |
865 | { |
866 | // TODO |
867 | return []; |
868 | } |
869 | |
870 | /** |
871 | * Find Reserves |
872 | * |
873 | * Obtain information on course reserves. |
874 | * |
875 | * @param string $course ID from getCourses (empty string to match all) |
876 | * @param string $inst ID from getInstructors (empty string to match all) |
877 | * @param string $dept ID from getDepartments (empty string to match all) |
878 | * |
879 | * @throws ILSException |
880 | * @return array An array of associative arrays representing reserve items. |
881 | * |
882 | * @SuppressWarnings(PHPMD.UnusedFormalParameter) |
883 | */ |
884 | public function findReserves($course, $inst, $dept) |
885 | { |
886 | // TODO |
887 | return []; |
888 | } |
889 | |
890 | /** |
891 | * Format date |
892 | * |
893 | * This formats a date coming from Evergreen for display |
894 | * |
895 | * @param string $date The date string to format; may be null |
896 | * |
897 | * @throws ILSException |
898 | * @return string The formatted date |
899 | */ |
900 | protected function formatDate($date) |
901 | { |
902 | if (!$date) { |
903 | return ''; |
904 | } |
905 | return $this->dateConverter->convertToDisplayDate('Y-m-d', $date); |
906 | } |
907 | } |