Browse Source

switch to sending requests to sharepoint

9.x-3.0
Paul Pound 1 month ago
parent
commit
3b8c36a975
  1. 13
      config/schema/upei_roblib_ill.schema.yml
  2. 54
      includes/db.inc
  3. 206
      includes/sharepoint.inc
  4. 41
      src/Form/RoblibIllLoanForm.php
  5. 51
      src/Form/RoblibIllSettingsForm.php
  6. 13
      upei_roblib_ill.install

13
config/schema/upei_roblib_ill.schema.yml

@ -14,18 +14,9 @@ upei_roblib_ill.settings:
ill_contact_phone: ill_contact_phone:
type: string type: string
label: 'Contact Phone Number' label: 'Contact Phone Number'
rapid_ill_username: sharepoint_webhook_url:
type: string type: string
label: 'RapidILL Username' label: 'Power Automate Webhook URL'
rapid_ill_password:
type: string
label: 'RapidILL Password'
rapid_ill_code:
type: string
label: 'RapidILL Rapid Code'
rapid_ill_branch_name:
type: string
label: 'RapidILL Branch Name'
evergreen_api_url: evergreen_api_url:
type: string type: string
label: 'Evergreen API URL' label: 'Evergreen API URL'

54
includes/db.inc

@ -5,25 +5,23 @@
*/ */
use RapidIll\InsertRequest;
/** /**
* Log an ILL request * Log an ILL request.
* *
* @param \RapidIll\InsertRequest $request * @param array $payload
* The request sent to RapidILL as an InsertRequest object. * The request payload as an associative array (SharePoint webhook format).
* @param array|string $response * @param array|string $response
* The response from RapidILL or an error message. * The response from the ILL service or an error message.
*/ */
function upei_roblib_ill_log_request(InsertRequest $request, $response) { function upei_roblib_ill_log_request(array $payload, $response) {
$soap_data = $request->toSoapArray();
$time_submitted = time(); $time_submitted = time();
$relais_message = is_array($response) ? (isset($response['ConfirmMessage']) ? $response['ConfirmMessage'] : ($response['Problem']['Message'] ?? '')) : (string) $response; $relais_message = is_array($response) ? ($response['ConfirmMessage'] ?? ($response['Problem']['Message'] ?? '')) : (string) $response;
// Parse patron name into first/last.
$patron_firstname = ''; $patron_firstname = '';
$patron_lastname = ''; $patron_lastname = '';
if (!empty($soap_data['PatronName'])) { if (!empty($payload['patronName'])) {
$parts = explode(' ', $soap_data['PatronName'], 2); $parts = explode(' ', $payload['patronName'], 2);
$patron_firstname = trim($parts[0] ?? ''); $patron_firstname = trim($parts[0] ?? '');
$patron_lastname = trim($parts[1] ?? ''); $patron_lastname = trim($parts[1] ?? '');
} }
@ -32,25 +30,25 @@ function upei_roblib_ill_log_request(InsertRequest $request, $response) {
try { try {
$connection->insert('upei_roblib_ill_request') $connection->insert('upei_roblib_ill_request')
->fields([ ->fields([
'patron_id' => $soap_data['PatronId'] ?? '', 'patron_id' => $payload['patronBarcode'] ?? '',
'patron_firstname' => $patron_firstname, 'patron_firstname' => $patron_firstname,
'patron_lastname' => $patron_lastname, 'patron_lastname' => $patron_lastname,
'patron_type' => '', 'patron_type' => $payload['patronType'] ?? '',
'patron_department' => $soap_data['PatronDepartment'] ?? '', 'patron_department' => $payload['patronDepartment'] ?? '',
'patron_email' => $soap_data['PatronEmail'] ?? '', 'patron_email' => $payload['patronEmail'] ?? '',
'notes' => $soap_data['PatronNotes'] ?? '', 'notes' => $payload['notes'] ?? '',
'genre' => $soap_data['RapidRequestType'] ?? '', 'genre' => $payload['format'] ?? '',
'doi' => '', 'doi' => $payload['doi_isbn'] ?? '',
'author' => $soap_data['ArticleAuthor'] ?? '', 'author' => $payload['author'] ?? '',
'citation_date' => $soap_data['PatronJournalYear'] ?? '', 'citation_date' => $payload['year'] ?? '',
'title' => $soap_data['PatronJournalTitle'] ?? '', 'title' => $payload['title'] ?? '',
'atitle' => $soap_data['ArticleTitle'] ?? '', 'atitle' => $payload['articleTitle'] ?? '',
'issn' => $soap_data['SuggestedIssns']['string'][0] ?? '', 'issn' => $payload['issn'] ?? '',
'isbn' => $soap_data['SuggestedIsbns']['string'][0] ?? '', 'isbn' => $payload['isbn'] ?? '',
'article_author' => $soap_data['ArticleAuthor'] ?? '', 'article_author' => $payload['author'] ?? '',
'volume' => $soap_data['JournalVol'] ?? '', 'volume' => $payload['volume'] ?? '',
'issue' => $soap_data['JournalIssue'] ?? '', 'issue' => $payload['issue'] ?? '',
'pages_requested' => $soap_data['ArticlePages'] ?? '', 'pages_requested' => $payload['pagesRequested'] ?? '',
'time_submitted' => $time_submitted, 'time_submitted' => $time_submitted,
'relais_request_id' => is_array($response) && isset($response['RequestNumber']) ? $response['RequestNumber'] : '-1', 'relais_request_id' => is_array($response) && isset($response['RequestNumber']) ? $response['RequestNumber'] : '-1',
'relais_message' => substr($relais_message, 0, 254), 'relais_message' => substr($relais_message, 0, 254),

206
includes/sharepoint.inc

@ -0,0 +1,206 @@
<?php
/**
* @file
* Contains the SharePointRequestHandler class for submitting ILL requests
* to a SharePoint list via a Power Automate webhook.
*/
use Drupal\Core\Form\FormStateInterface;
/**
* Handles building and submitting ILL requests to SharePoint via Power Automate.
*
* Usage (from the form submit handler):
* @code
* \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/sharepoint');
* $handler = new SharePointRequestHandler();
* $payload = $handler->buildPayload($form_state);
* $response = $handler->submitRequest($payload);
* @endcode
*/
class SharePointRequestHandler
{
/**
* Drupal config object for upei_roblib_ill.settings.
*
* @var \Drupal\Core\Config\ImmutableConfig
*/
protected $config;
/**
* Constructs a new SharePointRequestHandler.
*/
public function __construct()
{
$this->config = \Drupal::config('upei_roblib_ill.settings');
}
/**
* Builds the webhook payload from the Drupal form state.
*
* Reads bibliographic data from form storage (step 1) and patron data
* from the current form values (step 2).
*
* @param \Drupal\Core\Form\FormStateInterface $form_state
* The current form state containing both steps of the ILL form.
*
* @return array
* An associative array matching the Power Automate webhook payload schema.
*/
public function buildPayload(FormStateInterface $form_state): array
{
$storage = $form_state->getStorage();
$biblio = $storage['request'] ?? [];
$patron = $form_state->getValues();
$genre = $biblio['Genre'] ?? 'article';
// Determine the author: use ArticleAuthor for articles/chapters, Author for books.
$author = '';
if ($genre !== 'book' && !empty($biblio['ArticleAuthor'])) {
$author = $biblio['ArticleAuthor'];
}
elseif (!empty($biblio['Author'])) {
$author = $biblio['Author'];
}
// Combine DOI and ISBN into a single field.
$doi_isbn_parts = [];
if (!empty($biblio['doi'])) {
$doi_isbn_parts[] = $biblio['doi'];
}
if (!empty($biblio['ISBN'])) {
$doi_isbn_parts[] = $biblio['ISBN'];
}
if (!empty($biblio['ISSN'])) {
$doi_isbn_parts[] = $biblio['ISSN'];
}
$doi_isbn = implode('; ', $doi_isbn_parts);
// Build patron full name.
$first_name = $patron['FirstName'] ?? '';
$surname = $patron['Surname'] ?? '';
$patron_name = trim("$first_name $surname");
// Build notes — pack all extra info that doesn't have its own schema field.
$notes_parts = [];
if (!empty($genre)) {
$notes_parts[] = 'Format: ' . $genre;
}
if (!empty($biblio['ArticleTitle'])) {
$notes_parts[] = 'Article/Chapter: ' . $biblio['ArticleTitle'];
}
if (!empty($biblio['Date'])) {
$notes_parts[] = 'Year: ' . $biblio['Date'];
}
if (!empty($biblio['Volume'])) {
$notes_parts[] = 'Vol: ' . $biblio['Volume'];
}
if (!empty($biblio['Issue'])) {
$notes_parts[] = 'Issue: ' . $biblio['Issue'];
}
if (!empty($biblio['PagesRequested'])) {
$notes_parts[] = 'Pages: ' . $biblio['PagesRequested'];
}
if (!empty($patron['patron_type'])) {
$notes_parts[] = 'Patron Type: ' . $patron['patron_type'];
}
if (!empty($patron['campus_id'])) {
$notes_parts[] = 'Campus ID: ' . $patron['campus_id'];
}
if (!empty($patron['Department'])) {
$notes_parts[] = 'Dept: ' . $patron['Department'];
}
if (!empty($patron['notes'])) {
$notes_parts[] = $patron['notes'];
}
// All values MUST be strings — Power Automate rejects the request
// if any value type does not match the trigger's JSON schema.
return [
'title' => (string)($biblio['Title'] ?? ''),
'author' => (string)$author,
'doi_isbn' => (string)$doi_isbn,
'callNumber' => (string)($patron['campus_id'] ?? ''),
'year' => '2023',
'instName' => (string)$patron_name,
'instEmail' => (string)($patron['DeliveryAddress'] ?? ''),
'shippingAddr' => (string)($patron['Department'] ?? ''),
'notes' => (string)implode(' | ', $notes_parts),
'submittedAt' => (string)gmdate('Y-m-d\TH:i:s') . '.000Z',
];
}
/**
* Submits the payload to the configured Power Automate webhook.
*
* @param array $payload
* The payload array as built by buildPayload().
*
* @return array
* An associative array with at least a 'ConfirmMessage' key, and
* optionally a 'RequestNumber' key on success.
*/
public function submitRequest(array $payload): array
{
\Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/db');
$webhook_url = $this->config->get('sharepoint_webhook_url');
if (empty($webhook_url)) {
\Drupal::logger('upei_roblib_ill')->error(
'SharePoint webhook URL is not configured. Please set it in the ILL settings.'
);
$error_response = [
'ConfirmMessage' => 'SharePoint integration is not configured. Please contact the library.',
];
upei_roblib_ill_log_request($payload, $error_response);
return $error_response;
}
// Log the outgoing payload for debugging.
\Drupal::logger('upei_roblib_ill')->notice(
'Submitting ILL request to SharePoint webhook. Payload: @payload',
['@payload' => json_encode($payload)]
);
try {
// Use 'json' option which handles JSON encoding and Content-Type automatically.
$response = \Drupal::httpClient()->post($webhook_url, [
'json' => $payload,
'timeout' => 30,
]);
$status_code = $response->getStatusCode();
$body = (string)$response->getBody();
$response_data = json_decode($body, TRUE) ?: [];
if ($status_code >= 200 && $status_code < 300) {
$result = [
'RequestNumber' => $response_data['requestId'] ?? date('YmdHis'),
'ConfirmMessage' => 'Your Interlibrary Loan request has been successfully submitted.',
];
}
else {
$message = $response_data['error'] ?? 'Unexpected response from SharePoint (HTTP ' . $status_code . ').';
$result = [
'ConfirmMessage' => 'SharePoint could not process your request: ' . $message,
];
}
}
catch (\Exception $e) {
\Drupal::logger('upei_roblib_ill')->error(
'SharePoint webhook request failed: @message',
['@message' => $e->getMessage()]
);
$result = [
'ConfirmMessage' => 'Error communicating with SharePoint: ' . $e->getMessage(),
];
}
upei_roblib_ill_log_request($payload, $result);
return $result;
}
}

41
src/Form/RoblibIllLoanForm.php

@ -50,18 +50,31 @@ class RoblibIllLoanForm extends FormBase
$campus_id = $this->ill_form_message_id($values['campus_id']); $campus_id = $this->ill_form_message_id($values['campus_id']);
\Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/rapidill'); // Build the SharePoint payload for logging and later submission.
$handler = new \RapidIllRequestHandler(); \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/sharepoint');
$insert_request = $handler->buildInsertRequest($form_state); $handler = new \SharePointRequestHandler();
$form_state->set('insert_request', $insert_request); $payload = $handler->buildPayload($form_state);
// Add extra fields for DB logging that aren't in the webhook payload.
$payload['patronType'] = $values['patron_type'] ?? '';
$payload['patronDepartment'] = $values['Department'] ?? '';
// Store individual biblio fields for DB logging.
$storage = $form_state->getStorage();
$biblio = $storage['request'] ?? [];
$payload['articleTitle'] = $biblio['ArticleTitle'] ?? '';
$payload['issn'] = $biblio['ISSN'] ?? '';
$payload['isbn'] = $biblio['ISBN'] ?? '';
$payload['volume'] = $biblio['Volume'] ?? '';
$payload['issue'] = $biblio['Issue'] ?? '';
$payload['pagesRequested'] = $biblio['PagesRequested'] ?? '';
$form_state->set('sharepoint_payload', $payload);
$auth = upei_roblib_ill_authenticate($campus_id, $values['Surname']); $auth = upei_roblib_ill_authenticate($campus_id, $values['Surname']);
if (!$auth) { if (!$auth) {
$err = 'UserID or Surname do not match or not found.'; $err = 'UserID or Surname do not match or not found.';
$form_state->setErrorByName('Surname', $err); $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. // Log invalid requests.
\Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/db'); \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/db');
upei_roblib_ill_log_request($insert_request, $err); upei_roblib_ill_log_request($payload, $err);
} }
$trigger = $form_state->getTriggeringElement(); $trigger = $form_state->getTriggeringElement();
@ -121,14 +134,14 @@ class RoblibIllLoanForm extends FormBase
$form_state->setRebuild(TRUE); $form_state->setRebuild(TRUE);
return; return;
} }
//process the form // Submit the request to SharePoint via the Power Automate webhook.
\Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/rapidill'); \Drupal::moduleHandler()->loadInclude('upei_roblib_ill', 'inc', 'includes/sharepoint');
$handler = new \RapidIllRequestHandler(); $handler = new \SharePointRequestHandler();
$insert_request = $form_state->get('insert_request'); $payload = $form_state->get('sharepoint_payload');
if (!$insert_request) { if (!$payload) {
$insert_request = $handler->buildInsertRequest($form_state); $payload = $handler->buildPayload($form_state);
} }
$response = $handler->submitRequest($insert_request); $response = $handler->submitRequest($payload);
$error = isset($response['RequestNumber']) ? 'FALSE' : 'TRUE'; $error = isset($response['RequestNumber']) ? 'FALSE' : 'TRUE';
$parameters = [ $parameters = [
'message' => $response['ConfirmMessage'], 'message' => $response['ConfirmMessage'],
@ -362,8 +375,6 @@ class RoblibIllLoanForm extends FormBase
/** /**
* The patron portion of the ILL form. * The patron portion of the ILL form.
* *
* The campus_id and email portion are also used in a request for an Relais aid.
*
* @param array $form * @param array $form
* An array representing a Drupal form. * An array representing a Drupal form.
* @param array $form_state * @param array $form_state

51
src/Form/RoblibIllSettingsForm.php

@ -35,7 +35,7 @@ class RoblibIllSettingsForm extends FormBase
'#type' => 'textfield', '#type' => 'textfield',
'#size' => 200, '#size' => 200,
'#title' => t('Your ILL Library Symbol'), '#title' => t('Your ILL Library Symbol'),
'#description' => t('Your Relais Library Symbol'), '#description' => t('Your Library Symbol'),
'#default_value' => $config->get('ill_library_symbol'), '#default_value' => $config->get('ill_library_symbol'),
]; ];
@ -93,38 +93,20 @@ class RoblibIllSettingsForm extends FormBase
'#size' => 200, '#size' => 200,
]; ];
// RapidILL settings. // SharePoint Settings.
$form['rapid_ill_fieldset'] = [ $form['sharepoint_fieldset'] = [
'#type' => 'fieldset', '#type' => 'fieldset',
'#title' => t('RapidILL Settings'), '#title' => t('SharePoint Settings'),
'#description' => t('Credentials and configuration for submitting requests via RapidILL.'), '#description' => t('Configuration for submitting ILL requests to SharePoint via Power Automate.'),
]; ];
$form['rapid_ill_fieldset']['rapid_ill_username'] = [ $form['sharepoint_fieldset']['sharepoint_webhook_url'] = [
'#type' => 'textfield', '#type' => 'textfield',
'#title' => t('RapidILL Username'), '#title' => t('Power Automate Webhook URL'),
'#description' => t('Your RapidILL API username.'), '#description' => t('The full URL of the Power Automate HTTP trigger that writes ILL requests to a SharePoint list.'),
'#default_value' => $config->get('rapid_ill_username'), '#default_value' => $config->get('sharepoint_webhook_url'),
'#size' => 200,
];
$form['rapid_ill_fieldset']['rapid_ill_password'] = [
'#type' => 'password',
'#title' => t('RapidILL Password'),
'#description' => t('Your RapidILL API password. Leave blank to keep the existing value.'),
'#size' => 200,
];
$form['rapid_ill_fieldset']['rapid_ill_code'] = [
'#type' => 'textfield',
'#title' => t('RapidILL Rapid Code'),
'#description' => t('Your library Rapid code (e.g. YOUR_RAPID_CODE).'),
'#default_value' => $config->get('rapid_ill_code'),
'#size' => 200,
];
$form['rapid_ill_fieldset']['rapid_ill_branch_name'] = [
'#type' => 'textfield',
'#title' => t('RapidILL Branch Name'),
'#description' => t('Your library branch name (e.g. Main).'),
'#default_value' => $config->get('rapid_ill_branch_name'),
'#size' => 200, '#size' => 200,
'#required' => TRUE,
'#maxlength' => 1024,
]; ];
$form['actions']['submit'] = [ $form['actions']['submit'] = [
@ -163,15 +145,8 @@ class RoblibIllSettingsForm extends FormBase
$config->set('evergreen_password', $eg_password)->save(); $config->set('evergreen_password', $eg_password)->save();
} }
// RapidILL settings. // SharePoint settings.
$config->set('rapid_ill_username', $form_state->getValue('rapid_ill_username'))->save(); $config->set('sharepoint_webhook_url', $form_state->getValue('sharepoint_webhook_url'))->save();
// Only update the password if a new value was entered.
$password = $form_state->getValue('rapid_ill_password');
if (!empty($password)) {
$config->set('rapid_ill_password', $password)->save();
}
$config->set('rapid_ill_code', $form_state->getValue('rapid_ill_code'))->save();
$config->set('rapid_ill_branch_name', $form_state->getValue('rapid_ill_branch_name'))->save();
} }
} }

13
upei_roblib_ill.install

@ -27,6 +27,19 @@ function upei_roblib_ill_update_9301() {
->save(); ->save();
} }
/**
* Remove obsolete RapidILL configuration keys.
*/
function upei_roblib_ill_update_9302() {
\Drupal::configFactory()->getEditable('upei_roblib_ill.settings')
->clear('rapid_ill_username')
->clear('rapid_ill_password')
->clear('rapid_ill_code')
->clear('rapid_ill_branch_name')
->save();
}
/** /**
* Implements hook_schema(). * Implements hook_schema().
*/ */

Loading…
Cancel
Save