Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OCLC Resource Sharing For Groups Integration - Phase one #154

Open
wants to merge 12 commits into
base: DIS-65_resource_sharing_for_groups_phase_one
Choose a base branch
from
Open
Prev Previous commit
Next Next commit
DIS-65 feat: Display RS4G ILL Requests list to patron
  • Loading branch information
Chloe070196 committed Jan 16, 2025
commit fbd026f7446eee3d8e21c86356617f8ae46c2c80
119 changes: 119 additions & 0 deletions code/web/Drivers/OCLCRSFGDriver.php
Original file line number Diff line number Diff line change
@@ -17,6 +17,87 @@ public function __construct() {
}

// Controllers

public function getAccountSummary(User $user): AccountSummary {
[
$existingId,
$summary,
] = $user->getCachedAccountSummary('oclcRSFG');

if ($summary === null || isset($_REQUEST['reload'])) {
//Get account information from api
require_once ROOT_DIR . '/sys/User/AccountSummary.php';
$summary = new AccountSummary();
$summary->userId = $user->id;
$summary->source = 'oclcRSFG';
$summary->resetCounters();

$settings = new OCLCRSFGSetting();
$homeLibrary = Library::getPatronHomeLibrary();
$settings->whereAdd("id={$homeLibrary->oclcRSFGSettingsId}");
if($settings->find()) {
$settings->fetch();
}
$requests = $this->getRequests($user, $settings);
$summary->numUnavailableHolds = count($requests['unavailable']);
$summary->numAvailableHolds = count($requests['available']);
}

return $summary;
}

public function getRequests(User $patron, $setting): array {
if (empty($this->_registryId)) {
return [];
}
try {
if (empty($this->accessToken)) {
$this->setAccessToken($setting);
}
} catch (Exception $e) {
global $logger;
$logger->log("Exception conducting pre-submission checks for an ILL request to the Resource Sharing Requests API: $e", Logger::LOG_ERROR);
return [
'title' => translate([
'text' => 'Request Failed',
'isPublicFacing' => true,
]),
'message' => translate([
'text' => "Could not send request to the Resource Sharing For Groups system.",
'isPublicFacing' => true,
]),
'success' => false,
];
}
$requestsSent = $this->getAllRequestsFromAspenDbForPatron($patron->id);
$openRequests = [];
$processedRequests = [];
foreach ($requestsSent as $requestInAspenDb) {
if ($requestInAspenDb->oclcRequestId) {
$requestInOCLCRSFG = $this->getRequestFromOCLCRSFGWithId($setting, $requestInAspenDb->oclcRequestId);
}
if (!empty($requestInOCLCRSFG)) {
$requestInAspenDb->requestStatus = $requestInOCLCRSFG['illRequest']['requestStatus'];
$requestInAspenDb->update();
if(
$requestInAspenDb->requestStatus == "REVIEW" ||
$requestInAspenDb->requestStatus == "REVIEWING"
){
$openRequests[] = $this->createTemporaryHold($patron->id, $requestInAspenDb);
}
if(
$requestInAspenDb->requestStatus == "RECEIVED"
){
$processedRequests[] = $this->createTemporaryHold($patron->id, $requestInAspenDb);
}
}
}
return [
'unavailable' => $openRequests,
'available' => $processedRequests
];
}

public function submitRequest(OCLCRSFGSetting $setting, User $patron, $requestFormData): array {
global $logger;
if (empty($this->_registryId)) {
@@ -269,6 +350,22 @@ private function getAllRequestsFromOCLCRSFGForPatron(OCLCRSFGSetting $setting, I
return json_decode(json_encode(simplexml_load_string($response)), true)['responses'];
}

private function getRequestFromOCLCRSFGWithId(OCLCRSFGSetting $setting, string $oclcRequestId): array|null {
require_once ROOT_DIR . '/sys/CurlWrapper.php';
$url = $setting->serviceBaseUrl . "/requests" . "/" . $oclcRequestId;
$curl = new CurlWrapper();
$customHeaders = [
"Authorization" => "Authorization: Bearer " . $this->accessToken->getToken(),
];
$curl->addCustomHeaders($customHeaders, false);
$curl->curl_connect($url);
$response = $curl->curlGetPage($url);
if (!$response) {
throw new Exception("No requests found with id $oclcRequestId");
}
return json_decode(json_encode(simplexml_load_string($response)), true)['responses'];
}

private function postToOCLCRSFG(string $serviceBaseUrl, OCLCRSFGRequest $newRequest): array {
require_once ROOT_DIR . '/sys/CurlWrapper.php';
$url = $serviceBaseUrl . "/requests";
@@ -309,6 +406,28 @@ private function setAccessToken(OCLCRSFGSetting $setting): void {
}

// Helpers

private function createTemporaryHold($patronId, $request): Hold {
require_once ROOT_DIR . '/sys/User/Hold.php';
$curRequest = new Hold();
$curRequest->userId = $patronId;
$curRequest->type = 'interlibrary_loan';
$curRequest->isIll = true;
$curRequest->source = 'oclcRSFG';
$curRequest->sourceId = $request->catalogKey;
$curRequest->recordId = $request->catalogKey;
$curRequest->title = $request->title;
$curRequest->author = $request->author;
$curRequest->status = $request->requestStatus;
$curRequest->pickupLocationName = $request->pickupLocation;
$curRequest->cancelId = $request->oclcRequestId;
$curRequest->cancelable = false;
if ($request->requestStatus == 'REVIEW' || $request->requestStatus == 'REVIEWING') {
$curRequest->cancelable = true;
}
return $curRequest;
}

private function formatRequestBody(OCLCRSFGRequest $newRequest): object {
$illRequest = [];
$illRequest["requestStatus"] = "PROFILING";
Original file line number Diff line number Diff line change
@@ -95,7 +95,7 @@
</a>
</li>
{/if}
{if $user->getInterlibraryLoanType() == 'vdx'}
{if $user->getInterlibraryLoanType() == 'vdx' || $user->getInterlibraryLoanType() == 'oclc_resource_sharing_for_groups'}
<li class="myAccountLink">
&nbsp;&nbsp;&raquo;&nbsp;
<a href="/MyAccount/Holds?tab=interlibrary_loan" id="holdsInterlibraryLoan" title="View Interlibrary Loan Requests">
4 changes: 2 additions & 2 deletions code/web/interface/themes/responsive/MyAccount/holds.tpl
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@
<li role="presentation"{if $tab=='all'} class="active"{/if}><a href="#all" aria-controls="all" role="tab" data-toggle="tab">{translate text="All" isPublicFacing=true} <span class="badge"><span class="holds-placeholder">&nbsp;</span></span></a></li>
<li role="presentation"{if $tab=='ils'} class="active"{/if}><a href="#ils" aria-controls="ils" role="tab" data-toggle="tab">{translate text="Physical Materials" isPublicFacing=true} <span class="badge"><span class="ils-holds-placeholder">&nbsp;</span></span></a></li>
{/if}
{if $user->getInterlibraryLoanType() == 'vdx'}
{if $user->getInterlibraryLoanType() == 'vdx' || $user->getInterlibraryLoanType() == 'oclc_resource_sharing_for_groups'}
<li role="presentation"{if $tab=='interlibrary_loan'} class="active"{/if}><a href="#interlibrary_loan" aria-controls="interlibrary_loan" role="tab" data-toggle="tab">{translate text="Interlibrary Loan Requests" isPublicFacing=true} <span class="badge"><span class="interlibrary-loan-requests-placeholder">&nbsp;</span></span></a></li>
{/if}
{if $user->isValidForEContentSource('overdrive')}
@@ -52,7 +52,7 @@
<div role="tabpanel" class="tab-pane{if $tab=='all'} active{/if}" id="all"><div id="allHoldsPlaceholder" aria-label="All Holds List">{translate text="Loading holds from all sources" isPublicFacing=true}</div></div>
<div role="tabpanel" class="tab-pane{if $tab=='ils'} active{/if}" id="ils"><div id="ilsHoldsPlaceholder" aria-label="List of Holds on Physical Materials">{translate text="Loading holds of physical materials" isPublicFacing=true}</div></div>
{/if}
{if $user->getInterlibraryLoanType() == 'vdx'}
{if $user->getInterlibraryLoanType() == 'vdx' || $user->getInterlibraryLoanType() == 'oclc_resource_sharing_for_groups'}
<div role="tabpanel" class="tab-pane{if $tab=='interlibrary_loan'} active{/if}" id="interlibrary_loan" aria-label="List of Interlibrary Loan Requests"><div id="interlibrary_loanHoldsPlaceholder">{translate text="Loading Interlibrary Loan Requests" isPublicFacing=true}</div></div>
{/if}
{if $user->isValidForEContentSource('overdrive')}
2 changes: 2 additions & 0 deletions code/web/interface/themes/responsive/MyAccount/holdsList.tpl
Original file line number Diff line number Diff line change
@@ -52,6 +52,8 @@
{include file="MyAccount/palaceProjectHold.tpl" record=$record section=$sectionKey resultIndex=$smarty.foreach.recordLoop.iteration}
{elseif $record->type == 'interlibrary_loan' && $record->source == 'vdx'}
{include file="MyAccount/vdxRequest.tpl" record=$record section=$sectionKey resultIndex=$smarty.foreach.recordLoop.iteration}
{elseif $record->type == 'interlibrary_loan' && $record->source == 'oclcRSFG'}
{include file="MyAccount/oclc-rsfg-request.tpl" record=$record section=$sectionKey resultIndex=$smarty.foreach.recordLoop.iteration}
{else}
<div class="row">
Unknown record type {$record->type}
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{strip}
<div class="result row" id="oclcRSFGHold_{$record->sourceId|escapeCSS}_{$record->cancelId|escapeCSS}">
{if !empty($showCovers)}
<div class="{if $section == 'available'}col-xs-4 col-sm-3{else}col-xs-3 col-sm-2{/if}">
<div class="text-center">
{if !empty($record->getCoverUrl())}
{if !empty($record->getLinkUrl())}
<a href="{$record->getLinkUrl()}" id="descriptionTrigger{$record->recordId|escape:"url"}" aria-hidden="true">
<img src="{$record->getCoverUrl()}" class="listResultImage img-thumbnail img-responsive {$coverStyle}" alt="{translate text='Cover Image' inAttribute=true isPublicFacing=true}">
</a>
{else}
<img src="{$record->getCoverUrl()}" class="listResultImage img-thumbnail img-responsive {$coverStyle}" alt="{translate text='Cover Image' inAttribute=true isPublicFacing=true}" aria-hidden="true">
{/if}
{/if}

</div>
</div>
{/if}
<div class="{if !empty($showCovers)}col-xs-8 col-sm-9{else}{if $section != 'available'}col-xs-11{else}col-xs-12{/if}{/if}">
<div class="row">
<div class="col-xs-12">
<span class="result-index">{$resultIndex}</span>&nbsp;
<a href="/OCLCRSFG/OCLCRSFGRequestDetails?requestId={$record->cancelId}" class="result-title notranslate">
{if !$record->getTitle()|removeTrailingPunctuation} {translate text='Title not available' isPublicFacing=true}{else}{$record->getTitle()|removeTrailingPunctuation|truncate:180:"..."|highlight}{/if}
</a>
</div>
</div>
<div class="row">
<div class="resultDetails col-xs-12 col-md-8 col-lg-9">
{if !empty($record->getAuthor())}
<div class="row">
<div class="result-label col-tn-4">{translate text='Author' isPublicFacing=true}</div>
<div class="col-tn-8 result-value">
{if is_array($record->getAuthor())}
{foreach from=$record->getAuthor() item=author}
<a href='/Author/Home?"author={$author|escape:"url"}"'>{$author|highlight}</a>
{/foreach}
{else}
<a href='/Author/Home?author="{$record->getAuthor()|escape:"url"}"'>{$record->getAuthor()|highlight}</a>
{/if}
</div>
</div>
{/if}
{if !empty($hasLinkedUsers)}
<div class="row">
<div class="result-label col-tn-4">{translate text='On Hold For' isPublicFacing=true}</div>
<div class="col-tn-8 result-value">
{$record->getUserName()|escape}
</div>
</div>
{/if}
<div class="row">
<div class="result-label col-tn-4">{translate text='Pickup Location' isPublicFacing=true}</div>
<div class="col-tn-8 result-value">
{$record->pickupLocationName|escape}
</div>
</div>
{if !empty($showPlacedColumn) && $record->createDate}
<div class="row">
<div class="result-label col-tn-4">{translate text='Date Placed' isPublicFacing=true}</div>
<div class="col-tn-8 result-value">
{$record->createDate|date_format:"%b %d, %Y"}
</div>
</div>
{/if}
<div class="row">
<div class="result-label col-tn-4">{translate text='Status' isPublicFacing=true}</div>
<div class="col-tn-8 result-value">
{if $record->frozen}
<span class="frozenHold label label-warning">
{/if}
{translate text=$record->status isPublicFacing=true}
</div>
</div>
<div class="row">
<div class="col-tn-8 result-value">
<span class="label label-primary">
{translate text='This is an interlibrary loan request' isPublicFacing=true}
</span>
</div>
</div>
</div>
<div class="col-xs-9 col-sm-8 col-md-4 col-lg-3">
{if !empty($showWhileYouWait)}
<div class="btn-group btn-group-vertical btn-block">
{if !empty($record->getGroupedWorkId())}
<button onclick="return AspenDiscovery.GroupedWork.getWhileYouWait('{$record->getGroupedWorkId()}');" class="btn btn-sm btn-default btn-wrap">{translate text="While You Wait" isPublicFacing=true}</button>
{/if}
</div>
{/if}
</div>
</div>
</div>
</div>
{/strip}
18 changes: 17 additions & 1 deletion code/web/services/MyAccount/AJAX.php
Original file line number Diff line number Diff line change
@@ -2845,7 +2845,7 @@ function getMenuDataInterlibraryLoan() {
];
if (UserAccount::isLoggedIn()) {
$user = UserAccount::getActiveUserObj();
if ($user->hasInterlibraryLoan()) {
if ($user->hasVDXInterlibraryLoan()) {
require_once ROOT_DIR . '/Drivers/VdxDriver.php';
$driver = new VdxDriver();
$vdxSummary = $driver->getAccountSummary($user);
@@ -2861,6 +2861,22 @@ function getMenuDataInterlibraryLoan() {
'success' => true,
'summary' => $vdxSummary->toArray(),
];
} elseif ($user->hasOCLCRSFGInterlibraryLoan()) {
require_once ROOT_DIR . '/Drivers/OCLCRSFGDriver.php';
$driver = new OCLCRSFGDriver();
$oclcRSFGSummary = $driver->getAccountSummary($user);
if ($user->getLinkedUsers() != null) {
/** @var User $user */
foreach ($user->getLinkedUsers() as $linkedUser) {
$linkedUserSummary = $driver->getAccountSummary($linkedUser);
$oclcRSFGSummary->numUnavailableHolds += $linkedUserSummary->numUnavailableHolds;
}
}
$result = [
'success' => true,
'summary' => $oclcRSFGSummary->toArray(),
];

} else {
$result['message'] = 'Invalid for VDX';
}
37 changes: 34 additions & 3 deletions code/web/sys/Account/User.php
Original file line number Diff line number Diff line change
@@ -890,6 +890,10 @@ function getAvailableOverDriveSettings(string $readerName): array {
}

function hasInterlibraryLoan(): bool {
return $this->hasVDXInterlibraryLoan() || $this->hasOCLCRSFGInterlibraryLoan();
}

function hasVDXInterlibraryLoan(): bool {
try {
$homeLocation = Location::getDefaultLocationForUser();
if ($homeLocation != null) {
@@ -919,6 +923,22 @@ function hasInterlibraryLoan(): bool {
return false;
}

function hasOCLCRSFGInterlibraryLoan(): bool {
try {
require_once ROOT_DIR . '/sys/OCLCRSFG/OCLCRSFGSetting.php';
require_once ROOT_DIR . '/sys/OCLCRSFG/OCLCRSFGForm.php';
$OCLCRSFGSettings = new OCLCRSFGSetting();
$homeLibrary = Library::getPatronHomeLibrary();
$OCLCRSFGSettings->whereAdd("id={$homeLibrary->oclcRSFGSettingsId}");
if ($OCLCRSFGSettings->find(true)) {
return true;
}
} catch (Exception $e) {
//This happens if the tables are not installed yet
}
return false;
}

function getInterlibraryLoanType(): string {
$homeLocation = Location::getDefaultLocationForUser();
if ($homeLocation != null) {
@@ -1896,14 +1916,25 @@ public function getHolds($includeLinkedUsers = true, $unavailableSort = 'sortTit
}

if ($source == 'all' || $source == 'interlibrary_loan') {
if ($this->hasInterlibraryLoan()) {
//For now, this is just VDX
if ($this->hasVDXInterlibraryLoan()) {
require_once ROOT_DIR . '/Drivers/VdxDriver.php';
$driver = new VdxDriver();
$vdxRequests = $driver->getRequests($this);
$allHolds = array_merge_recursive($allHolds, $vdxRequests);
$holdsToReturn = array_merge_recursive($holdsToReturn, $vdxRequests);
}
if ($this->hasOCLCRSFGInterlibraryLoan()) {
require_once ROOT_DIR . '/Drivers/OCLCRSFGDriver.php';
require_once ROOT_DIR . '/sys/OCLCRSFG/OCLCRSFGSetting.php';
$OCLCRSFGSettings = new OCLCRSFGSetting();
$OCLCRSFGSettings->whereAdd("id=" . Library::getActiveLibrary()->oclcRSFGSettingsId);
if ($OCLCRSFGSettings->find(true)) {
$driver = new OCLCRSFGDriver();
$oclcRSFGRequests = $driver->getRequests($this, $OCLCRSFGSettings);
$allHolds = array_merge_recursive($allHolds, $oclcRSFGRequests);
$holdsToReturn = array_merge_recursive($holdsToReturn, $oclcRSFGRequests);
}
}
}
//Delete all existing holds
$hold = new Hold();
@@ -2061,7 +2092,7 @@ public function getHolds($includeLinkedUsers = true, $unavailableSort = 'sortTit
uasort($holdsToReturn['unavailable'], $holdSort);
}

if ($source == 'interlibrary_loan') {
if ($source == 'interlibrary_loan' && !$this->hasOCLCRSFGInterlibraryLoan()) {
unset($holdsToReturn['available']);
}