From 5c1d2b1b02dde92de47e4f367cd583fc4d323487 Mon Sep 17 00:00:00 2001 From: Paul Pound Date: Fri, 27 Mar 2026 14:12:43 -0300 Subject: [PATCH] more changes for rapidILL --- config/schema/upei_roblib_ill.schema.yml | 3 + includes/db.inc | 68 ++++++++----- includes/rapidill.inc | 11 +- includes/utilities.inc | 124 ++++++++++++++++++++--- src/Form/RoblibIllLoanForm.php | 71 +++++++------ src/Form/RoblibIllSettingsForm.php | 18 ++++ 6 files changed, 215 insertions(+), 80 deletions(-) diff --git a/config/schema/upei_roblib_ill.schema.yml b/config/schema/upei_roblib_ill.schema.yml index e4f1e86..98affcc 100644 --- a/config/schema/upei_roblib_ill.schema.yml +++ b/config/schema/upei_roblib_ill.schema.yml @@ -26,3 +26,6 @@ upei_roblib_ill.settings: rapid_ill_branch_name: type: string label: 'RapidILL Branch Name' + evergreen_api_url: + type: string + label: 'Evergreen API URL' diff --git a/includes/db.inc b/includes/db.inc index 3071dde..ae9bf69 100644 --- a/includes/db.inc +++ b/includes/db.inc @@ -5,42 +5,54 @@ */ +use RapidIll\InsertRequest; + /** * Log an ILL request * - * @param array $request - * The request sent to Relais as an array (before it is encoded as JSON) - * @param array $response - * The response from Relais. + * @param \RapidIll\InsertRequest $request + * The request sent to RapidILL as an InsertRequest object. + * @param array|string $response + * The response from RapidILL or an error message. */ -function upei_roblib_ill_log_request($request, $response) { - $time_submitted = isset($request['RequestInfo']['DateSubmitted']) ? $request['RequestInfo']['DateSubmitted'] : ''; - $relais_message = isset($response['ConfirmMessage']) ? $response['ConfirmMessage'] : $response['Problem']['Message']; +function upei_roblib_ill_log_request(InsertRequest $request, $response) { + $soap_data = $request->toSoapArray(); + $time_submitted = time(); + $relais_message = is_array($response) ? (isset($response['ConfirmMessage']) ? $response['ConfirmMessage'] : ($response['Problem']['Message'] ?? '')) : (string) $response; + + $patron_firstname = ''; + $patron_lastname = ''; + if (!empty($soap_data['PatronName'])) { + $parts = explode(' ', $soap_data['PatronName'], 2); + $patron_firstname = trim($parts[0] ?? ''); + $patron_lastname = trim($parts[1] ?? ''); + } + $connection = \Drupal::service('database'); try { $connection->insert('upei_roblib_ill_request') ->fields([ - 'patron_id' => $request['DeliveryAddress']['campus_id'], - 'patron_firstname' => $request['DeliveryAddress']['FirstName'], - 'patron_lastname' => $request['DeliveryAddress']['Surname'], - 'patron_type' => $request['DeliveryAddress']['patron_type'], - 'patron_department' => $request['DeliveryAddress']['Department'], - 'patron_email' => $request['DeliveryAddress']['DeliveryAddress'], - 'notes' => isset($request['DeliveryAddress']['notes']) ? $request['DeliveryAddress']['notes'] : '', - 'genre' => isset($request['BibliographicInfo']['Genre']) ? $request['BibliographicInfo']['Genre'] : '', - 'doi' => isset($request['BibliographicInfo']['AdditionalNumbers']) ? $request['BibliographicInfo']['AdditionalNumbers'] : '', - 'author' => isset($request['BibliographicInfo']['Author']) ? $request['BibliographicInfo']['Author'] : '', - 'citation_date' => isset($request['BibliographicInfo']['Date']) ? $request['BibliographicInfo']['Date'] : '', - 'title' => isset($request['BibliographicInfo']['Title']) ? $request['BibliographicInfo']['Title'] : '', - 'atitle' => isset($request['BibliographicInfo']['ArticleTitle']) ? $request['BibliographicInfo']['ArticleTitle'] : '', - 'issn' => isset($request['BibliographicInfo']['ISSN'][0]) ? $request['BibliographicInfo']['ISSN'][0] : '', - 'isbn' => isset($request['BibliographicInfo']['ISBN'][0]) ? $request['BibliographicInfo']['ISBN'][0] : '', - 'article_author' => isset($request['BibliographicInfo']['ArticleAuthor']) ? $request['BibliographicInfo']['ArticleAuthor'] : '', - 'volume' => isset($request['BibliographicInfo']['Volume']) ? $request['BibliographicInfo']['Volume'] : '', - 'issue' => isset($request['BibliographicInfo']['Issue']) ? $request['BibliographicInfo']['Issue'] : '', - 'pages_requested' => isset($request['BibliographicInfo']['PagesRequested']) ? $request['BibliographicInfo']['PagesRequested'] : '', - 'time_submitted' => strtotime($time_submitted), - 'relais_request_id' => isset($response['RequestNumber']) ? $response['RequestNumber'] : '-1', + 'patron_id' => $soap_data['PatronId'] ?? '', + 'patron_firstname' => $patron_firstname, + 'patron_lastname' => $patron_lastname, + 'patron_type' => '', + 'patron_department' => $soap_data['PatronDepartment'] ?? '', + 'patron_email' => $soap_data['PatronEmail'] ?? '', + 'notes' => $soap_data['PatronNotes'] ?? '', + 'genre' => $soap_data['RapidRequestType'] ?? '', + 'doi' => '', + 'author' => $soap_data['ArticleAuthor'] ?? '', + 'citation_date' => $soap_data['PatronJournalYear'] ?? '', + 'title' => $soap_data['PatronJournalTitle'] ?? '', + 'atitle' => $soap_data['ArticleTitle'] ?? '', + 'issn' => $soap_data['SuggestedIssns']['string'][0] ?? '', + 'isbn' => $soap_data['SuggestedIsbns']['string'][0] ?? '', + 'article_author' => $soap_data['ArticleAuthor'] ?? '', + 'volume' => $soap_data['JournalVol'] ?? '', + 'issue' => $soap_data['JournalIssue'] ?? '', + 'pages_requested' => $soap_data['ArticlePages'] ?? '', + 'time_submitted' => $time_submitted, + 'relais_request_id' => is_array($response) && isset($response['RequestNumber']) ? $response['RequestNumber'] : '-1', 'relais_message' => substr($relais_message, 0, 254), ])->execute(); } catch (Exception $e) { diff --git a/includes/rapidill.inc b/includes/rapidill.inc index 9a72f6d..d6a366f 100644 --- a/includes/rapidill.inc +++ b/includes/rapidill.inc @@ -217,8 +217,7 @@ class RapidIllRequestHandler { \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/db'); \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/utilities'); - // Build the Relais-style array for logging purposes. - $relais_arr = upei_roblib_ill_build_relais_arr($form_state); + $insert_request = $this->buildInsertRequest($form_state); try { $client = $this->createClient(); @@ -227,12 +226,10 @@ class RapidIllRequestHandler { $error_response = [ 'ConfirmMessage' => 'RapidILL is not configured. Please contact the library.', ]; - upei_roblib_ill_log_request($relais_arr, $error_response); + upei_roblib_ill_log_request($insert_request, $error_response); return $error_response; } - $insert_request = $this->buildInsertRequest($form_state); - try { /** @var \RapidIll\InsertResponse $response */ $response = $client->insertRequest($insert_request); @@ -245,7 +242,7 @@ class RapidIllRequestHandler { $error_response = [ 'ConfirmMessage' => 'Error communicating with RapidILL: ' . $e->getMessage(), ]; - upei_roblib_ill_log_request($relais_arr, $error_response); + upei_roblib_ill_log_request($insert_request, $error_response); return $error_response; } @@ -263,7 +260,7 @@ class RapidIllRequestHandler { ]; } - upei_roblib_ill_log_request($relais_arr, $result); + upei_roblib_ill_log_request($insert_request, $result); return $result; } diff --git a/includes/utilities.inc b/includes/utilities.inc index f869a0c..50c2216 100644 --- a/includes/utilities.inc +++ b/includes/utilities.inc @@ -14,7 +14,8 @@ use Drupal\Component\Utility\Xss; use Drupal\Component\Utility\Html; -function upei_roblib_ill_check_arr_item(&$value, $key) { +function upei_roblib_ill_check_arr_item(&$value, $key) +{ if (is_array($value)) { array_walk($value, 'upei_roblib_ill_check_arr_item'); } @@ -33,7 +34,8 @@ function upei_roblib_ill_check_arr_item(&$value, $key) { * An array where the keys in the array that have empty values are removed and * drupal specific elements are removed. */ -function upei_roblib_ill_clean_array($values) { +function upei_roblib_ill_clean_array($values) +{ $arr = array_filter($values); if (isset($arr['ISSN'])) { $arr['ISSN'] = [$arr['ISSN']]; @@ -71,6 +73,95 @@ function upei_roblib_ill_clean_array($values) { return $arr; } +function upei_roblib_ill_build_err_msg($msg_from_server) +{ + $campus_id = Link::fromTextAndUrl(t('Campus ID'), Url::fromUri('http://www.upei.ca/vpaf/campuscard')); + //$campus_id = l(t('Campus ID'), 'http://www.upei.ca/vpaf/campuscard'); + $ill_email = Link::fromTextAndUrl('ill@upei.ca', Url::fromUri('mailto:ill@upei.ca')); + //$ill_email = l(t('ill@upei.ca'), 'mailto:ill@upei.ca'); + $phone = Link::fromTextAndUrl('902-566-0583', Url::fromUri('tel:902-566-0353')); + //$phone = l(t('902-566-0583'), 'tel:902-566-0353'); + $server_response = !empty($msg_from_server) ? 'Server Response: ' . $msg_from_server : ''; + return t('Oops. Something went wrong.
Check the "Your Last Name" and "Your Campus ID" fields - those two need to match what is on file +. (Your @campus_id appears as the NUMBER near the middle of your campus card). If you do not have a @campus_id, please contact the Robertson Library Service Desk, or, +call @phone. Note: if you are a student taking online courses only, please email @ill_email.
@msg_from_server', + [ + '@campus_id' => $campus_id->toString(), + '@ill_email' => $ill_email->toString(), + '@phone' => $phone->toString(), + '@msg_from_server' => $server_response, + ]); +} + +/** + * Lookup the user in Evergreen and verify the surname matches. + * + * @param string $campus_id + * The user's campus ID / barcode. + * @param string $surname + * The surname to check against. + * + * @return bool + * TRUE if the surname matches, FALSE otherwise. + */ +function upei_roblib_ill_authenticate($campus_id, $surname): bool +{ + if (empty($campus_id) || empty($surname)) { + return FALSE; + } + + $config = \Drupal::config('upei_roblib_ill.settings'); + $evergreen_api_url = $config->get('evergreen_api_url'); + if (empty($evergreen_api_url)) { + \Drupal::logger('upei_roblib_ill')->error('Evergreen API URL is not configured.'); + return FALSE; + } + + // Construct the PatronAPI dump URL. + // Format: {evergreen_api_url}/api/patronapi/{campus_id}/dump + $url = rtrim($evergreen_api_url, '/') . '/api/patronapi/' . urlencode($campus_id) . '/dump'; + + try { + $response = \Drupal::httpClient()->get($url, [ + 'timeout' => 10, + ]); + + $data = (string)$response->getBody(); + + // The PatronAPI dump endpoint returns data in a plain text/HTML format + // looking like `PATRN NAME[p]=Smith, John`. + if (preg_match('/PATRN NAME\[p\]=([^\[<]+)/i', $data, $matches)) { + $patron_name = trim($matches[1]); + + // Usually "Lastname, Firstname" + $name_parts = explode(',', $patron_name); + if (count($name_parts) > 0) { + $evergreen_surname = trim($name_parts[0]); + + // Case-insensitive comparison of the extracted surname and provided surname. + if (strcasecmp($evergreen_surname, trim($surname)) === 0) { + return TRUE; + } + } + } + + // Log failure for debugging. + \Drupal::logger('upei_roblib_ill')->warning('Evergreen authentication failed for @campus_id. Expected surname: @surname', [ + '@campus_id' => $campus_id, + '@surname' => $surname, + ]); + + return FALSE; + + } + catch (\Exception $e) { + \Drupal::logger('upei_roblib_ill')->error('Failed to connect to Evergreen API: @message', [ + '@message' => $e->getMessage(), + ]); + return FALSE; + } +} + /** * Allows us to ask for _REQUEST variables that may or may not exist without * generating a php warning. Also does some formatting for certain variables. @@ -81,30 +172,33 @@ function upei_roblib_ill_clean_array($values) { * @return string * The value of the $_REQUEST variable or an empty string. */ -function upei_roblib_ill_get_request_variable($variable) { +function upei_roblib_ill_get_request_variable($variable) +{ if ($variable == 'author' && empty($_REQUEST[$variable])) { //google scholar usually sends auinit aulast instead of author - $initial = isset($_REQUEST['auinit']) ? Html::escape($_REQUEST['auinit']) : NULL; - $last_name = isset($_REQUEST['aulast']) ? Html::escape($_REQUEST['aulast']) : NULL; + $initial = isset($_REQUEST['auinit']) ?Html::escape($_REQUEST['auinit']) : NULL; + $last_name = isset($_REQUEST['aulast']) ?Html::escape($_REQUEST['aulast']) : NULL; return !empty($last_name) ? $last_name . ',' . $initial : ''; } if ($variable == 'issn' && !empty($_REQUEST[$variable])) { // ebsco sometimes sends garbage as issns verify this is a valid issn before displaying the value in the form. - return preg_match('/^\d{4}-?\d{3}[\dxX]$/', $_REQUEST[$variable]) ? Html::escape($_REQUEST[$variable]) : ''; + return preg_match('/^\d{4}-?\d{3}[\dxX]$/', $_REQUEST[$variable]) ?Html::escape($_REQUEST[$variable]) : ''; } // ebsco sometimes returns bookitem we only understand chapter if (($variable == 'genre' && !empty($_REQUEST[$variable])) && $_REQUEST[$variable] == 'bookitem') { return 'chapter'; } - return isset($_REQUEST[$variable]) ? Html::escape($_REQUEST[$variable]) : ''; + return isset($_REQUEST[$variable]) ?Html::escape($_REQUEST[$variable]) : ''; } -function upei_roblib_ill_get_doi_from_request() { +function upei_roblib_ill_get_doi_from_request() +{ return (!empty($_REQUEST['doi'])) ? $_REQUEST['doi'] : upei_roblib_ill_get_doi_from_id(); } -function upei_roblib_ill_get_doi_from_id() { +function upei_roblib_ill_get_doi_from_id() +{ if (!empty($_REQUEST['ID'])) { return (strpos($_REQUEST['ID'], 'doi:') !== 0) ? '' : $_REQUEST['ID']; } @@ -120,7 +214,8 @@ function upei_roblib_ill_get_doi_from_id() { * @return array * The requestInfo portion of a Relais request */ -function upei_roblib_ill_request_info_array($values, $notes) { +function upei_roblib_ill_request_info_array($values, $notes) +{ $requestInfo = []; $genre = $values['Genre']; $requestInfo['DateSubmitted'] = date('Y-m-d H:i:s'); @@ -128,12 +223,12 @@ function upei_roblib_ill_request_info_array($values, $notes) { if ($genre == 'book') { $requestInfo['ServiceType'] = 'L'; $requestInfo['ServiceLabel'] = "R"; - //$requestInfo['RequestSource'] = "C"; + //$requestInfo['RequestSource'] = "C"; } else { $requestInfo['ServiceType'] = 'X'; $requestInfo['ServiceLabel'] = "R"; - //$requestInfo['RequestSource'] = "C"; + //$requestInfo['RequestSource'] = "C"; } return $requestInfo; } @@ -146,7 +241,8 @@ function upei_roblib_ill_request_info_array($values, $notes) { * * @return string */ -function upei_roblib_format_biblio_info($form_state) { +function upei_roblib_format_biblio_info($form_state) +{ $values = $form_state->getValues(); $data = upei_roblib_ill_clean_array($values); $rows = []; @@ -164,4 +260,4 @@ function upei_roblib_format_biblio_info($form_state) { ]; $html_output = \Drupal::service('renderer')->render($output); return $html_output; -} +} \ No newline at end of file diff --git a/src/Form/RoblibIllLoanForm.php b/src/Form/RoblibIllLoanForm.php index 86f20f3..0b28bea 100644 --- a/src/Form/RoblibIllLoanForm.php +++ b/src/Form/RoblibIllLoanForm.php @@ -10,19 +10,22 @@ use Drupal\Core\Form\FormStateInterface; * * @author ppound */ -class RoblibIllLoanForm extends FormBase { +class RoblibIllLoanForm extends FormBase +{ /** * {@inheritdoc} */ - public function getFormId() { + public function getFormId() + { return 'roblib_ill_loan_form'; } /** * {@inheritdoc} */ - public function buildForm(array $form, FormStateInterface $form_state) { + public function buildForm(array $form, FormStateInterface $form_state) + { if ($form_state->has('step') && $form_state->get('step') == 2) { return $this->ill_auth_form($form, $form_state); } @@ -32,8 +35,10 @@ class RoblibIllLoanForm extends FormBase { /** * {@inheritdoc} */ - public function validateForm(array &$form, FormStateInterface $form_state) { + public function validateForm(array &$form, FormStateInterface $form_state) + { \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/relais'); + \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/utilities'); $values = $form_state->getValues(); if ($form_state->get('step') == 2) { $is_valid_email = \Drupal::service('email.validator')->isValid($values['DeliveryAddress']); @@ -46,29 +51,27 @@ class RoblibIllLoanForm extends FormBase { $campus_id = $this->ill_form_message_id($values['campus_id']); - $aid = upei_roblib_ill_authenticate($campus_id, $values['Surname']); - if (is_array($aid) && isset($aid['Problem']['Message'])) { - if (!empty($aid['Problem']['HR_Message'])) { - $form_state->setErrorByName('campus_id', $aid['Problem']['HR_Message']); - } - $form_state->setErrorByName('Surname', $aid['Problem']['Message']); + $auth = upei_roblib_ill_authenticate($campus_id, $values['Surname']); + if (!$auth) { + $err = 'UserID or Surname do not match or not found.'; + $form_state->setErrorByName('Surname', $err); // Log invalid requests, we need to build the full array here to log it, even though we won't send it yet as auth has failed. //$form_state->set('upei_roblib_ill_auth_form_storage') = $form_state->getValues(); \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/db'); - $arr = upei_roblib_ill_build_relais_arr($form_state); - upei_roblib_ill_log_request($arr, $aid); - } - else { - $form_state->set('aid', $aid); - } - } - $trigger = $form_state->getTriggeringElement(); - if($trigger['#value'] != 'Lookup DOI' && empty($form_state->get('step'))) { - if(empty($form_state->getValue('Title'))){ - $form_state->setErrorByName('Title', t('Journal/Book Title is required.')); + \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/rapidill'); + $handler = new \RapidIllRequestHandler(); + $response = $handler->buildInsertRequest($form_state); + upei_roblib_ill_log_request($response, $err); } - if(empty($form_state->getValue('Genre'))) { - $form_state->setErrorByName('Title', t('Item Type is required.')); + + $trigger = $form_state->getTriggeringElement(); + if ($trigger['#value'] != 'Lookup DOI' && empty($form_state->get('step'))) { + if (empty($form_state->getValue('Title'))) { + $form_state->setErrorByName('Title', t('Journal/Book Title is required.')); + } + if (empty($form_state->getValue('Genre'))) { + $form_state->setErrorByName('Title', t('Item Type is required.')); + } } } } @@ -80,7 +83,8 @@ class RoblibIllLoanForm extends FormBase { * @return mixed * the campus id with leading zeros and leading/trailing whitespace removed. */ - function ill_form_message_id($id) { + function ill_form_message_id($id) + { // Equinox made some changes to the database so we may not need to do // this anymore. // Doing this simple change for now in case things need to be reverted. @@ -95,7 +99,8 @@ class RoblibIllLoanForm extends FormBase { * @param \Drupal\Core\Form\FormStateInterface $form_state * The current state of the form. */ - public function submitStepOne(array &$form, FormStateInterface $form_state) { + public function submitStepOne(array &$form, FormStateInterface $form_state) + { $trigger = $form_state->getTriggeringElement(); if ($trigger['#value'] != 'Lookup DOI') { $form_state @@ -108,7 +113,8 @@ class RoblibIllLoanForm extends FormBase { /** * {@inheritdoc} */ - public function submitForm(array &$form, FormStateInterface $form_state) { + public function submitForm(array &$form, FormStateInterface $form_state) + { $trigger = $form_state->getTriggeringElement(); if ($trigger['#value'] == 'Lookup DOI') { //Do nothing as this was just a DOI Lookup @@ -122,11 +128,12 @@ class RoblibIllLoanForm extends FormBase { 'message' => $response['ConfirmMessage'], 'email' => $form_state->getValue('DeliveryAddress'), 'error' => $error, - ]; + ]; $form_state->setRedirect('roblib_ill.loan_form_finished', $parameters); } - public function doiCallback(array &$form, FormStateInterface $form_state) { + public function doiCallback(array &$form, FormStateInterface $form_state) + { \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/doi'); $doi = $form_state->getValue('doi'); $form = upei_roblib_ill_doi_get_data($form, $doi); @@ -144,7 +151,8 @@ class RoblibIllLoanForm extends FormBase { * @return array * An array representing the bibliographic form. */ - function ill_request_form($form, &$form_state) { + function ill_request_form($form, &$form_state) + { \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/utilities'); $config = \Drupal::config('upei_roblib_ill.settings'); $form['#prefix'] = '
'; @@ -358,7 +366,8 @@ class RoblibIllLoanForm extends FormBase { * @return array * An array representing the bibliographic form. */ - function ill_auth_form($form, &$form_state) { + function ill_auth_form($form, &$form_state) + { \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/utilities'); $form['#prefix'] = upei_roblib_format_biblio_info($form_state); $form['#suffix'] = '
* = Required Field
@@ -504,4 +513,4 @@ Freedom of Information and Protection of Privacy Act and will be protected return $form; } -} +} \ No newline at end of file diff --git a/src/Form/RoblibIllSettingsForm.php b/src/Form/RoblibIllSettingsForm.php index 2a65846..0399742 100644 --- a/src/Form/RoblibIllSettingsForm.php +++ b/src/Form/RoblibIllSettingsForm.php @@ -64,6 +64,21 @@ class RoblibIllSettingsForm extends FormBase '#default_value' => $config->get('ill_contact_phone'), ]; + // Evergreen Settings + $form['evergreen_fieldset'] = [ + '#type' => 'fieldset', + '#title' => t('Evergreen ILS Settings'), + '#description' => t('Configuration for authenticating users against Evergreen.'), + ]; + $form['evergreen_fieldset']['evergreen_api_url'] = [ + '#type' => 'url', + '#title' => t('Evergreen API URL'), + '#description' => t('The base URL of your Evergreen server (e.g. https://catalogue.example.com). Used for PatronAPI lookups.'), + '#default_value' => $config->get('evergreen_api_url'), + '#size' => 200, + '#required' => TRUE, + ]; + // RapidILL settings. $form['rapid_ill_fieldset'] = [ '#type' => 'fieldset', @@ -126,6 +141,9 @@ class RoblibIllSettingsForm extends FormBase $config->set('ill_contact_email', $form_state->getValue('ill_contact_email'))->save(); $config->set('ill_contact_phone', $form_state->getValue('ill_contact_phone'))->save(); + // Evergreen settings. + $config->set('evergreen_api_url', rtrim($form_state->getValue('evergreen_api_url'), '/'))->save(); + // RapidILL settings. $config->set('rapid_ill_username', $form_state->getValue('rapid_ill_username'))->save(); // Only update the password if a new value was entered.