diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a463609f1f..31e617a3c2 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -3,179 +3,158 @@ on:
push
jobs:
- push_aspen_to_dockerhub:
- name: Push Aspen image to Docker Hub
- if: github.repository == 'Aspen-Discovery/aspen-discovery'
+ build_aspen_docker_images:
+ name: Push Aspen image to GitHub Container Registry
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- - name: Log in to Docker Hub
- uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
- with:
- username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_PASSWORD }}
+ - name: Extract GitHub Tag
+ if: ${{ startsWith(github.ref, 'refs/tags/') }}
+ id: extract_tag
+ run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
+
+ - name: Add placeholder if this is not a tag
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ id: extract_tag_dummy
+ run: echo "GIT_TAG=NOTHING" >> $GITHUB_ENV
- name: Extract branch name
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch
- - name: Build and push Docker image
- uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
+ - name: Log in to Docker Hub
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
+ uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
- context: .
- file: docker/Dockerfile
- push: true
- tags: |
- aspendiscovery/aspen:latest
- aspendiscovery/aspen:${{ steps.extract_branch.outputs.branch }}
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
- push_aspen_to_quay:
- name: Push Aspen image to Quay.io
- if: github.repository == 'Aspen-Discovery/aspen-discovery'
- runs-on: ubuntu-latest
- steps:
- - name: Check out the repo
- uses: actions/checkout@v4
+ - name: Log in to GHCR
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
+ run: echo "${{ secrets.GHCR_PASSWORD }}" | docker login ghcr.io -u ${{ secrets.GHCR_USERNAME }} --password-stdin
- name: Log in to Quay.io
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- - name: Extract branch name
- shell: bash
- run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
- id: extract_branch
-
- name: Build and push Docker image
- uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
+ uses: docker/build-push-action@v5
with:
context: .
file: docker/Dockerfile
- push: true
+ push: ${{ startsWith(github.ref, 'refs/tags/') }}
tags: |
- quay.io/aspen-discovery/aspen:latest
- quay.io/aspen-discovery/aspen:${{ steps.extract_branch.outputs.branch }}
+ aspendiscovery/aspen:${{ env.GIT_TAG }}
+ ghcr.io/aspen-discovery/aspen:${{ env.GIT_TAG }}
+ quay.io/aspen-discovery/aspen:${{ env.GIT_TAG }}
- push_solr_to_dockerhub:
- name: Push Solr image to Docker Hub
- if: github.repository == 'Aspen-Discovery/aspen-discovery'
+ build_solr_docker_images:
+ name: Push Aspen image to GitHub Container Registry
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- - name: Log in to Docker Hub
- uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
- with:
- username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_PASSWORD }}
+ - name: Extract GitHub Tag
+ if: ${{ startsWith(github.ref, 'refs/tags/') }}
+ id: extract_tag
+ run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
+
+ - name: Add placeholder if this is not a tag
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ id: extract_tag_dummy
+ run: echo "GIT_TAG=NOTHING" >> $GITHUB_ENV
- name: Extract branch name
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch
- - name: Build and push Docker image
- uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
+ - name: Log in to Docker Hub
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
+ uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
- context: .
- file: docker/files/solr/Dockerfile
- push: true
- tags: |
- aspendiscovery/solr:latest
- aspendiscovery/solr:${{ steps.extract_branch.outputs.branch }}
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
- push_solr_to_quay:
- name: Push Solr image to Quay.io
- if: github.repository == 'Aspen-Discovery/aspen-discovery'
- runs-on: ubuntu-latest
- steps:
- - name: Check out the repo
- uses: actions/checkout@v4
+ - name: Log in to GHCR
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
+ run: echo "${{ secrets.GHCR_PASSWORD }}" | docker login ghcr.io -u ${{ secrets.GHCR_USERNAME }} --password-stdin
- name: Log in to Quay.io
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- - name: Extract branch name
- shell: bash
- run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
- id: extract_branch
-
- name: Build and push Docker image
- uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
+ uses: docker/build-push-action@v5
with:
context: .
file: docker/files/solr/Dockerfile
- push: true
+ push: ${{ startsWith(github.ref, 'refs/tags/') }}
tags: |
- quay.io/aspen-discovery/solr:latest
- quay.io/aspen-discovery/solr:${{ steps.extract_branch.outputs.branch }}
+ aspendiscovery/solr:${{ env.GIT_TAG }}
+ ghcr.io/aspen-discovery/solr:${{ env.GIT_TAG }}
+ quay.io/aspen-discovery/solr:${{ env.GIT_TAG }}
- push_tunnel_to_dockerhub:
- name: Push Tunnel image to Docker Hub
- if: github.repository == 'Aspen-Discovery/aspen-discovery'
+ build_tunnel_docker_images:
+ name: Push Aspen image to GitHub Container Registry
runs-on: ubuntu-latest
steps:
- name: Check out the repo
uses: actions/checkout@v4
- - name: Log in to Docker Hub
- uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
- with:
- username: ${{ secrets.DOCKER_USERNAME }}
- password: ${{ secrets.DOCKER_PASSWORD }}
+ - name: Extract GitHub Tag
+ if: ${{ startsWith(github.ref, 'refs/tags/') }}
+ id: extract_tag
+ run: echo "GIT_TAG=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
+
+ - name: Add placeholder if this is not a tag
+ if: ${{ !startsWith(github.ref, 'refs/tags/') }}
+ id: extract_tag_dummy
+ run: echo "GIT_TAG=NOTHING" >> $GITHUB_ENV
- name: Extract branch name
shell: bash
run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
id: extract_branch
- - name: Build and push Docker image
- uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
+ - name: Log in to Docker Hub
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
+ uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
- context: .
- file: docker/files/tunnel/Dockerfile
- push: true
- tags: |
- aspendiscovery/tunnel:latest
- aspendiscovery/tunnel:${{ steps.extract_branch.outputs.branch }}
+ username: ${{ secrets.DOCKER_USERNAME }}
+ password: ${{ secrets.DOCKER_PASSWORD }}
- push_tunnel_to_quay:
- name: Push Tunnel image to Quay.io
- if: github.repository == 'Aspen-Discovery/aspen-discovery'
- runs-on: ubuntu-latest
- steps:
- - name: Check out the repo
- uses: actions/checkout@v4
+ - name: Log in to GHCR
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
+ run: echo "${{ secrets.GHCR_PASSWORD }}" | docker login ghcr.io -u ${{ secrets.GHCR_USERNAME }} --password-stdin
- name: Log in to Quay.io
+ if: github.repository == 'Aspen-Discover/aspen-discovery'
uses: docker/login-action@f4ef78c080cd8ba55a85445d5b36e214a81df20a
with:
registry: quay.io
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}
- - name: Extract branch name
- shell: bash
- run: echo "branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT
- id: extract_branch
-
- name: Build and push Docker image
- uses: docker/build-push-action@3b5e8027fcad23fda98b2e3ac259d8d67585f671
+ uses: docker/build-push-action@v5
with:
context: .
file: docker/files/tunnel/Dockerfile
- push: true
+ push: ${{ startsWith(github.ref, 'refs/tags/') }}
tags: |
- quay.io/aspen-discovery/tunnel:latest
- quay.io/aspen-discovery/tunnel:${{ steps.extract_branch.outputs.branch }}
+ aspendiscovery/tunnel:${{ env.GIT_TAG }}
+ ghcr.io/aspen-discovery/tunnel:${{ env.GIT_TAG }}
+ quay.io/aspen-discovery/tunnel:${{ env.GIT_TAG }}
diff --git a/.github/workflows/pull_request_qa.yml b/.github/workflows/pull_request_qa.yml
new file mode 100644
index 0000000000..144c3ef67d
--- /dev/null
+++ b/.github/workflows/pull_request_qa.yml
@@ -0,0 +1,73 @@
+name: Check Pull Request for QA Issues
+
+on:
+ pull_request:
+
+jobs:
+ check_release_notes:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout pull request branch
+ uses: actions/checkout@v4
+
+ - name: Add official Aspen Discovery repo as another remote
+ run: git remote add official https://github.com/Aspen-Discovery/aspen-discovery.git && git fetch official
+
+ - name: Get default branch
+ id: get_default_branch
+ run: |
+ default_branch=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/Aspen-Discovery/aspen-discovery | jq -r .default_branch)
+ echo "Default branch is $default_branch"
+ echo "DEFAULT_BRANCH=$default_branch" >> $GITHUB_ENV
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Check for changes in release notes
+ run: |
+ if git diff --name-only official/$DEFAULT_BRANCH HEAD | grep -q 'code/web/release_notes/'; then
+ echo "Release notes have been modified."
+ else
+ echo "No changes detected in the release notes."
+ exit 1
+ fi
+
+ check_tabs_vs_spaces:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout pull request branch
+ uses: actions/checkout@v4
+
+ - name: Add official Aspen Discovery repo as another remote
+ run: git remote add official https://github.com/Aspen-Discovery/aspen-discovery.git && git fetch official
+
+ - name: Get default branch
+ id: get_default_branch
+ run: |
+ default_branch=$(curl -s -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/Aspen-Discovery/aspen-discovery | jq -r .default_branch)
+ echo "Default branch is $default_branch"
+ echo "DEFAULT_BRANCH=$default_branch" >> $GITHUB_ENV
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Check for spaces instead of tabs
+ run: |
+ # Find files that are modified in the pull request
+ MODIFIED_FILES=$(git diff --name-only official/$DEFAULT_BRANCH HEAD)
+
+ # Loop through each file and check for spaces used instead of tabs
+ EXIT_CODE=0
+ for file in $MODIFIED_FILES; do
+ echo "Found modified file: $file";
+ if [[ $file == *.php || $file == *.js || $file == *.java ]]; then
+ #echo "Checking $file for whitespace issues"
+ DIFF=$(git diff official/$DEFAULT_BRANCH HEAD -- $file)
+ #echo "DIFF: $DIFF"
+ if [[ $DIFF =~ " " ]]; then
+ echo "Detected spaces instead of tabs in $file"
+ EXIT_CODE=1
+ fi
+ fi
+ done
+ exit $EXIT_CODE
diff --git a/.gitignore b/.gitignore
index 3eefdd1e4e..e0d2bbd60e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -229,3 +229,5 @@ code/aspen_app/updateTestApps.sh
code/cron/.idea/misc.xml
tests/phpunit/.phpunit.result.cache
install/unit_tests_local.ini
+intellij_project
+phpstorm_project
diff --git a/code/aspen_app/package.json b/code/aspen_app/package.json
index 89f6ee3088..739b097864 100644
--- a/code/aspen_app/package.json
+++ b/code/aspen_app/package.json
@@ -28,7 +28,7 @@
"@sentry/react-native": "~5.22.0",
"@tanstack/react-query": "^4.33.0",
"apisauce": "^2.1.6",
- "axios": "^1.6.0",
+ "axios": "^1.7.4",
"babel-preset-expo": "^11.0.10",
"chroma-js": "~2.4.2",
"compare-versions": "^6.1.0",
diff --git a/code/aspen_app/yarn.lock b/code/aspen_app/yarn.lock
index 60462b8838..7fe537a275 100644
--- a/code/aspen_app/yarn.lock
+++ b/code/aspen_app/yarn.lock
@@ -7219,10 +7219,10 @@ axios@^0.21.4:
dependencies:
follow-redirects "^1.14.0"
-axios@^1.6.0:
- version "1.6.8"
- resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.8.tgz#66d294951f5d988a00e87a0ffb955316a619ea66"
- integrity sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==
+axios@^1.7.4:
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/axios/-/axios-1.7.4.tgz#4c8ded1b43683c8dd362973c393f3ede24052aa2"
+ integrity sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==
dependencies:
follow-redirects "^1.15.6"
form-data "^4.0.0"
diff --git a/code/koha_export/koha_export.jar b/code/koha_export/koha_export.jar
index 469ddcc859..5620021d39 100644
Binary files a/code/koha_export/koha_export.jar and b/code/koha_export/koha_export.jar differ
diff --git a/code/koha_export/src/com/turning_leaf_technologies/koha_export/KohaExportMain.java b/code/koha_export/src/com/turning_leaf_technologies/koha_export/KohaExportMain.java
index 0018cbe7fe..436b5c5e4d 100644
--- a/code/koha_export/src/com/turning_leaf_technologies/koha_export/KohaExportMain.java
+++ b/code/koha_export/src/com/turning_leaf_technologies/koha_export/KohaExportMain.java
@@ -529,16 +529,11 @@ private static void exportVolumes(Connection dbConn, Connection kohaConn) {
float kohaVersion = getKohaVersion(kohaConn);
PreparedStatement getVolumeInfoStmt;
PreparedStatement getItemsForVolumeStmt;
- if (kohaVersion < 22.11) {
- //Arlington's code for volumes
- getVolumeInfoStmt = kohaConn.prepareStatement("SELECT * from volumes", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
- getItemsForVolumeStmt = kohaConn.prepareStatement("SELECT * from volume_items", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
- }else {
- //community code for
- getVolumeInfoStmt = kohaConn.prepareStatement("SELECT * from item_groups", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
- getItemsForVolumeStmt = kohaConn.prepareStatement("SELECT * from item_group_items", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
- }
- PreparedStatement addVolumeStmt = dbConn.prepareStatement("INSERT INTO ils_volume_info (recordId, volumeId, displayLabel, relatedItems, displayOrder) VALUES (?,?,?,?, ?) ON DUPLICATE KEY update recordId = VALUES(recordId), displayLabel = VALUES(displayLabel), relatedItems = VALUES(relatedItems), displayOrder = VALUES(displayOrder)");
+ // Filter out any item groups that do not currently contain items
+ getVolumeInfoStmt = kohaConn.prepareStatement("SELECT item_groups.* FROM item_groups LEFT JOIN item_group_items USING (item_group_id) GROUP BY item_group_id HAVING COUNT(item_id) > 0", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+ getItemsForVolumeStmt = kohaConn.prepareStatement("SELECT * from item_group_items", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+ PreparedStatement addVolumeStmt = dbConn.prepareStatement("INSERT INTO ils_volume_info (recordId, volumeId, displayLabel, relatedItems, displayOrder) VALUES (?,?,?,?, ?) ON DUPLICATE KEY update recordId = VALUES(recordId), displayLabel = VALUES(displayLabel), relatedItems = VALUES(relatedItems), displayOrder = VALUES(displayOrder)");
PreparedStatement deleteVolumeStmt = dbConn.prepareStatement("DELETE from ils_volume_info where volumeId = ?");
ResultSet volumeInfoRS = null;
diff --git a/code/web/CatalogConnection.php b/code/web/CatalogConnection.php
index 1517f5c9fd..01538d893a 100644
--- a/code/web/CatalogConnection.php
+++ b/code/web/CatalogConnection.php
@@ -1790,8 +1790,12 @@ public function checkoutBySip(User $patron, $barcode, $currentLocationId): array
return $this->driver->checkoutBySip($patron, $barcode, $currentLocationId);
}
- public function checkoutByAPI(User $patron, $barcode, $currentLocationId): array {
- return $this->driver->checkoutByAPI($patron, $barcode, $currentLocationId);
+ public function hasAPICheckout() : bool {
+ return $this->driver->hasAPICheckout();
+ }
+
+ public function checkoutByAPI(User $patron, $barcode, Location $currentLocation): array {
+ return $this->driver->checkoutByAPI($patron, $barcode, $currentLocation);
}
public function allowUpdatesOfPreferredName(User $patron) : bool {
diff --git a/code/web/Drivers/AbstractIlsDriver.php b/code/web/Drivers/AbstractIlsDriver.php
index 6469fc4f54..9978a5d46b 100644
--- a/code/web/Drivers/AbstractIlsDriver.php
+++ b/code/web/Drivers/AbstractIlsDriver.php
@@ -721,6 +721,10 @@ public function bypassReadingHistoryUpdate($patron, $isNightlyUpdate) : bool {
return false;
}
+ public function hasAPICheckout() : bool {
+ return false;
+ }
+
public function checkoutBySip(User $patron, $barcode, $currentLocationId) {
$checkout_result = [];
$success = false;
@@ -753,54 +757,60 @@ public function checkoutBySip(User $patron, $barcode, $currentLocationId) {
$mySip = new sip2();
$mySip->hostname = $this->accountProfile->sipHost;
$mySip->port = $this->accountProfile->sipPort;
- if ($mySip->connect($this->accountProfile->sipUser, $this->accountProfile->sipPassword)) {
- //send self check status message
- $in = $mySip->msgSCStatus();
- $msg_result = $mySip->get_message($in);
- // Make sure the response is 98 as expected
- if (preg_match('/^98/', $msg_result)) {
- $result = $mySip->parseACSStatusResponse($msg_result);
-
- // Use result to populate SIP2 settings
- $mySip->AO = $result['variable']['AO'][0]; /* set AO to value returned */
- if (!empty($result['variable']['AN'])) {
- $mySip->AN = $result['variable']['AN'][0]; /* set AN to value returned */
- }
-
- $mySip->patron = $patron->getBarcode();
- $mySip->patronpwd = $patron->getPasswordOrPin();
-
- $in = $mySip->msgCheckout($barcode, '', 'N', '', 'N', 'N', 'N', $checkoutLocation);
+ if (empty($mySip->hostname) || empty($mySip->sipPort)){
+ $success = false;
+ $message = 'The SIP server is not properly configured for this account profile.';
+ $title = 'An Error Occurred';
+ } else{
+ if ($mySip->connect($this->accountProfile->sipUser, $this->accountProfile->sipPassword)) {
+ //send self check status message
+ $in = $mySip->msgSCStatus();
$msg_result = $mySip->get_message($in);
+ // Make sure the response is 98 as expected
+ if (preg_match('/^98/', $msg_result)) {
+ $result = $mySip->parseACSStatusResponse($msg_result);
+
+ // Use result to populate SIP2 settings
+ $mySip->AO = $result['variable']['AO'][0]; /* set AO to value returned */
+ if (!empty($result['variable']['AN'])) {
+ $mySip->AN = $result['variable']['AN'][0]; /* set AN to value returned */
+ }
- $checkoutResponse = null;
- $item = [];
- if (str_starts_with($msg_result, '64') || str_starts_with($msg_result, '12')) {
- $checkoutResponse = $mySip->parseCheckoutResponse($msg_result);
- if($checkoutResponse['fixed']['Ok'][0]) {
- $success = true;
- $title = translate(['text' => 'Checkout successful', 'isPublicFacing' => true]);
- $message = translate(['text' => 'You have successfully checked out this title.', 'isPublicFacing' => true]);
- if(isset($checkoutResponse['variable']['AF'][0])) {
- $message .= ' ' . $checkoutResponse['variable']['AF'][0];
- }
- $dueDate = explode(" ", $checkoutResponse['variable']['AH'][0]);
- if($this->accountProfile->ils == 'sierra') {
- $dueDate = str_replace('-', '/', $dueDate[0]);
- $dueDate = date_create($dueDate);
+ $mySip->patron = $patron->getBarcode();
+ $mySip->patronpwd = $patron->getPasswordOrPin();
+
+ $in = $mySip->msgCheckout($barcode, '', 'N', '', 'N', 'N', 'N', $checkoutLocation);
+ $msg_result = $mySip->get_message($in);
+
+ $checkoutResponse = null;
+ $item = [];
+ if (str_starts_with($msg_result, '64') || str_starts_with($msg_result, '12')) {
+ $checkoutResponse = $mySip->parseCheckoutResponse($msg_result);
+ if($checkoutResponse['fixed']['Ok'][0]) {
+ $success = true;
+ $title = translate(['text' => 'Checkout successful', 'isPublicFacing' => true]);
+ $message = translate(['text' => 'You have successfully checked out this title.', 'isPublicFacing' => true]);
+ if(isset($checkoutResponse['variable']['AF'][0])) {
+ $message .= ' ' . $checkoutResponse['variable']['AF'][0];
+ }
+ $dueDate = explode(" ", $checkoutResponse['variable']['AH'][0]);
+ if($this->accountProfile->ils == 'sierra') {
+ $dueDate = str_replace('-', '/', $dueDate[0]);
+ $dueDate = date_create($dueDate);
+ } else {
+ $dueDate = date_create($dueDate[0]);
+ }
+ $dueDate = date_format($dueDate, 'm/d/Y');
+ $item['due'] = $dueDate;
} else {
- $dueDate = date_create($dueDate[0]);
+ $message .= ' ' . $checkoutResponse['variable']['AF'][0];
+ $item['due'] = null;
}
- $dueDate = date_format($dueDate, 'm/d/Y');
- $item['due'] = $dueDate;
+ $item['title'] = $checkoutResponse['variable']['AJ'][0] ?? 'Unknown title';
+ $item['barcode'] = $barcode;
} else {
- $message .= ' ' . $checkoutResponse['variable']['AF'][0];
- $item['due'] = null;
+ $message = $checkoutResponse;
}
- $item['title'] = $checkoutResponse['variable']['AJ'][0] ?? 'Unknown title';
- $item['barcode'] = $barcode;
- } else {
- $message = $checkoutResponse;
}
}
}
@@ -816,7 +826,25 @@ public function checkoutBySip(User $patron, $barcode, $currentLocationId) {
];
}
- public function checkoutByAPI(User $patron, $barcode, $currentLocationId): array {
+ public function checkoutByAPI(User $patron, $barcode, Location $currentLocation): array {
+ return [
+ 'success' => false,
+ 'message' => 'This functionality has not been implemented for this ILS',
+ ];
+ }
+
+ public function hasAPICheckIn() {
+ return false;
+ }
+
+ public function checkInByAPI(User $patron, $barcode, Location $currentLocation): array {
+ return [
+ 'success' => false,
+ 'message' => 'This functionality has not been implemented for this ILS',
+ ];
+ }
+
+ public function checkInBySIP(User $patron, $barcode, Location $currentLocation): array {
return [
'success' => false,
'message' => 'This functionality has not been implemented for this ILS',
diff --git a/code/web/Drivers/CloudLibraryDriver.php b/code/web/Drivers/CloudLibraryDriver.php
index 8e761190dc..0c541141f9 100644
--- a/code/web/Drivers/CloudLibraryDriver.php
+++ b/code/web/Drivers/CloudLibraryDriver.php
@@ -722,14 +722,12 @@ public function getSettings(User $user = null) {
global $library;
$activeLibrary = $library;
}
- $scopes = $activeLibrary->getCloudLibraryScopes();
- if (count($scopes) > 0) {
- foreach ($scopes as $libraryScope) {
- $scope = new CloudLibraryScope();
- $scope->id = $libraryScope->scopeId;
- if ($scope->find(true)) {
- return $scope->getSetting();
- }
+ $scopeId = $activeLibrary->getCloudLibraryScope();
+ if($scopeId > 0) {
+ $scope = new CloudLibraryScope();
+ $scope->id = $scopeId;
+ if ($scope->find(true)) {
+ return $scope->getSetting();
}
}
return false;
diff --git a/code/web/Drivers/Koha.php b/code/web/Drivers/Koha.php
index fe53a7a8f3..18e97a2a02 100644
--- a/code/web/Drivers/Koha.php
+++ b/code/web/Drivers/Koha.php
@@ -46,6 +46,7 @@ function updateHomeLibrary(User $patron, string $homeLibraryCode) {
$curRow = $results->fetch_assoc();
$address = $curRow['address'];
$city = $curRow['city'];
+ $results->close();
}
$postVariables = [
@@ -142,6 +143,7 @@ function updatePatronInfo($patron, $canUpdateContactInfo, $fromMasquerade = fals
$city = $curRow['city'];
$lastname = $curRow['surname'];
}
+ $results->close();
}
$postVariables = [
@@ -437,7 +439,6 @@ public function getCheckouts(User $patron): array {
if ($renewPrefRow = $renewPrefResults->fetch_assoc()) {
$renewPref = $renewPrefRow['autorenew_checkouts'];
}
-
$renewPrefResults->close();
}
$timer->logTime("Loaded borrower preference for autorenew_checkouts");
@@ -455,7 +456,6 @@ public function getCheckouts(User $patron): array {
$patronIsExpired = true;
}
}
-
$patronExpirationResults->close();
}
$timer->logTime("Loaded patron expiration date");
@@ -539,12 +539,17 @@ public function getCheckouts(User $patron): array {
$circulationRulesForCheckout = [];
/** @noinspection SqlResolve */
/** @noinspection SqlDialectInspection */
- $circulationRulesSql = "SELECT * FROM circulation_rules where (categorycode IN ('$patronType', '*') OR categorycode IS NULL) and (itemtype IN('$itemType', '*') OR itemtype is null) and (branchcode IN ('$checkoutBranch', '*') OR branchcode IS NULL) order by branchcode desc, categorycode desc, itemtype desc";
+ $circulationRulesSql = "
+ SELECT * FROM circulation_rules
+ WHERE (categorycode IN ('$patronType', '*') OR categorycode IS NULL)
+ AND (itemtype IN('$itemType', '*') OR itemtype is null)
+ AND (branchcode IN ('$checkoutBranch', '*') OR branchcode IS NULL)
+ ORDER BY branchcode desc, categorycode desc, itemtype desc LIMIT 1
+ ";
$circulationRulesRS = mysqli_query($this->dbConnection, $circulationRulesSql);
if ($circulationRulesRS !== false) {
- while ($circulationRulesRow = $circulationRulesRS->fetch_assoc()) {
- $circulationRulesForCheckout[] = $circulationRulesRow;
- }
+ $circulationRulesRow = $circulationRulesRS->fetch_assoc();
+ $circulationRulesForCheckout[] = $circulationRulesRow;
$circulationRulesRS->close();
}
$timer->logTime("Load circulation rules for checkout");
@@ -709,6 +714,7 @@ public function getCheckouts(User $patron): array {
$checkouts[$curCheckout->source . $curCheckout->sourceId . $curCheckout->userId] = $curCheckout;
}
+ $results->close();
//Check to see if any checkouts are Claims Returned
$allIssueIdsAsString = implode(',', $allIssueIds);
@@ -897,9 +903,11 @@ public function patronLogin($username, $password, $validatedViaSSO) {
$patronId = $lookupUserRow['borrowernumber'];
$newUser = $this->loadPatronInfoFromDB($patronId, null, $barcode);
if (!empty($newUser) && !($newUser instanceof AspenError)) {
+ $lookupUserResult->close();
return $newUser;
}
}
+ $lookupUserResult->close();
} else if ($this->getKohaVersion() >= 22.1110) {
//Authenticate the user using KOHA API
$oauthToken = $this->getOAuthToken();
@@ -944,9 +952,11 @@ public function patronLogin($username, $password, $validatedViaSSO) {
$expiredPasswordResult = $this->processExpiredPassword($lookupUserRow['borrowernumber'], $barcode);
if ($expiredPasswordResult != null) {
+ $lookupUserResult->close();
return $expiredPasswordResult;
}
}
+ $lookupUserResult->close();
}
$result['messages'][] = translate([
'text' => 'Unable to authenticate with the ILS. Please try again later or contact the library.',
@@ -1015,6 +1025,7 @@ public function patronLogin($username, $password, $validatedViaSSO) {
if ($lookupUserResult->num_rows > 0) {
$userExistsInDB = true;
$lookupUserRow = $lookupUserResult->fetch_assoc();
+ $lookupUserResult->close();
if (UserAccount::isUserMasquerading()) {
$patronId = $lookupUserRow['borrowernumber'];
$newUser = $this->loadPatronInfoFromDB($patronId, null, $barcode);
@@ -1035,6 +1046,8 @@ public function patronLogin($username, $password, $validatedViaSSO) {
return new AspenError('Maximum number of failed login attempts reached, your account has been locked.');
}
}
+ } else {
+ $lookupUserResult->close();
}
}
} else {
@@ -1087,6 +1100,7 @@ public function processExpiredPassword($borrowernumber, $barcode) : ?ExpiredPass
}
}
}
+ $passwordExpirationResult->close();
} catch (Exception $e) {
//This happens if password expiration is not enabled
}
@@ -1209,6 +1223,7 @@ private function loadPatronInfoFromDB($patronId, $password, $suppliedUsernameOrB
global $logger;
$logger->log("Could not get information about patron category", Logger::LOG_ERROR);
}
+ $patronCategoryResult->close();
} else {
global $logger;
$logger->log("Could not get information about patron category", Logger::LOG_ERROR);
@@ -1324,6 +1339,7 @@ private function loadPatronInfoFromDB($patronId, $password, $suppliedUsernameOrB
return $user;
}
+
return $userExistsInDB;
}
@@ -1434,6 +1450,7 @@ public function doReadingHistoryAction(User $patron, string $action, array $sele
$address = $curRow['address'];
$city = $curRow['city'];
}
+ $results->close();
} else {
//We could not connect to the database, don't update, so we don't corrupt the DB
global $logger;
@@ -1602,6 +1619,8 @@ public function getReadingHistory($patron, $page = 1, $recordsPerPage = -1, $sor
}
$readingHistoryTitles[] = $curTitle;
}
+
+ $readingHistoryTitleRS->close();
}
}
@@ -2528,7 +2547,7 @@ public function getHolds($patron, $page = 1, $recordsPerPage = -1, $sortOption =
$holds['available'][$curHold->source . $curHold->cancelId . $curHold->userId] = $curHold;
}
}
-
+ $results->close();
//Load additional ILL Requests that are not shipped
$oauthToken = $this->getOAuthToken();
@@ -2969,7 +2988,6 @@ public function renewCheckout($patron, $recordId, $itemId = null, $itemIndex = n
$renewSql = "SELECT issues.*, items.biblionumber, items.itype, items.itemcallnumber, items.enumchron, title, author, issues.renewals from issues left join items on items.itemnumber = issues.itemnumber left join biblio ON items.biblionumber = biblio.biblionumber where borrowernumber = '" . mysqli_escape_string($this->dbConnection, $patron->unique_ils_id) . "' AND issues.itemnumber = $itemId limit 1";
}
- $renewResults = mysqli_query($this->dbConnection, $renewSql);
$maxRenewals = 0;
$params = [
@@ -2985,15 +3003,16 @@ public function renewCheckout($patron, $recordId, $itemId = null, $itemIndex = n
//Parse the result
if (isset($renewResponse->success) && ($renewResponse->success == 1)) {
+ $renewResults = mysqli_query($this->dbConnection, $renewSql);
while ($curRow = mysqli_fetch_assoc($renewResults)) {
$patronType = $patron->patronType;
$itemType = $curRow['itype'];
$checkoutBranch = $curRow['branchcode'];
if ($this->getKohaVersion() >= 22.11) {
- $renewCount = $curRow['renewals_count'] + 1;
+ $renewCount = $curRow['renewals_count'];
} else {
- $renewCount = $curRow['renewals'] + 1;
+ $renewCount = $curRow['renewals'];
}
/** @noinspection SqlResolve */
$issuingRulesSql = "SELECT * FROM circulation_rules where rule_name = 'renewalsallowed' AND (categorycode IN ('$patronType', '*') OR categorycode IS NULL) and (itemtype IN('$itemType', '*') OR itemtype is null) and (branchcode IN ('$checkoutBranch', '*') OR branchcode IS NULL) order by branchcode desc, categorycode desc, itemtype desc limit 1";
@@ -3005,6 +3024,7 @@ public function renewCheckout($patron, $recordId, $itemId = null, $itemIndex = n
$issuingRulesRS->close();
}
}
+
$renewResults->close();
$renewsRemaining = ($maxRenewals - $renewCount);
@@ -3726,6 +3746,7 @@ function getSelfRegistrationFields($type = 'selfReg') {
while ($curRow = $results->fetch_assoc()) {
$kohaPreferences[$curRow['variable']] = $curRow['value'];
}
+ $results->close();
if ($type == 'selfReg') {
$unwantedFields = explode('|', $kohaPreferences['PatronSelfRegistrationBorrowerUnwantedField']);
@@ -4845,6 +4866,7 @@ function getNewMaterialsRequestForm(User $user) {
while ($curRow = $results->fetch_assoc()) {
$kohaPreferences[$curRow['variable']] = $curRow['value'];
}
+ $results->close();
if (isset($kohaPreferences['OPACSuggestionMandatoryFields'])) {
$mandatoryFields = array_flip(explode('|', $kohaPreferences['OPACSuggestionMandatoryFields']));
@@ -5177,6 +5199,7 @@ function getNumMaterialsRequests(User $user) {
if ($curRow = $results->fetch_assoc()) {
$numRequests = $curRow['numRequests'];
}
+ $results->close();
return $numRequests;
}
@@ -5301,6 +5324,7 @@ function getMaterialsRequests(User $user) {
}
$allRequests[] = $request;
}
+ $results->close();
return $allRequests;
}
@@ -5406,6 +5430,7 @@ function getPatronUpdateForm($user) {
}
}
}
+ $results->close();
//Set default values for extended patron attributes
if ($this->getKohaVersion() > 21.05) {
@@ -5719,6 +5744,7 @@ function importListsFromIls($patron) {
}
$results['totalLists']++;
}
+ $listResults->close();
return $results;
}
@@ -5917,6 +5943,7 @@ public function findNewUser($patronBarcode, $patronUsername) {
return $newUser;
}
}
+ $lookupUserResult->close();
}else{
//search by username
/** @noinspection SqlResolve */
@@ -5931,6 +5958,7 @@ public function findNewUser($patronBarcode, $patronUsername) {
return $newUser;
}
}
+ $lookupUserResult->close();
}
return false;
@@ -5955,6 +5983,7 @@ public function findNewUserByEmail($patronEmail): mixed {
} else if ($lookupUserResult->num_rows > 1) {
return 'Found more than one user.';
}
+ $lookupUserResult->close();
return false;
}
@@ -5969,18 +5998,21 @@ public function findUserByField($field, $value) {
$sql = "SELECT borrowernumber, cardnumber, " . mysqli_escape_string($this->dbConnection, $field) . " from borrowers where " . mysqli_escape_string($this->dbConnection, $field) . " = '" . mysqli_escape_string($this->dbConnection, $value) . "'";
$lookupUserResult = mysqli_query($this->dbConnection, $sql);
+ $return_value = false;
if ($lookupUserResult->num_rows == 1) {
$lookupUserRow = $lookupUserResult->fetch_assoc();
$patronId = $lookupUserRow['borrowernumber'];
$newUser = $this->loadPatronInfoFromDB($patronId, null, $lookupUserRow['cardnumber']);
if (!empty($newUser) && !($newUser instanceof AspenError)) {
- return $newUser;
+ $return_value = $newUser;
}
} else if ($lookupUserResult->num_rows > 1) {
- return 'Found more than one user.';
+ $return_value = 'Found more than one user.';
}
- return false;
+ $lookupUserResult->close();
+
+ return $return_value;
}
/**
@@ -5997,6 +6029,7 @@ public function showMessagingSettings(): bool {
$allowed = false;
}
}
+ $preferenceRS->close();
return $allowed;
}
@@ -6030,6 +6063,7 @@ public function getMessagingSettingsTemplate(User $patron): ?string {
$enablePhoneMessaging |= !empty($systemPreference['value']);
}
}
+ $systemPreferencesRS->close();
$interface->assign('enablePhoneMessaging', $enablePhoneMessaging);
/** @noinspection SqlResolve */
@@ -6039,6 +6073,7 @@ public function getMessagingSettingsTemplate(User $patron): ?string {
$interface->assign('smsAlertNumber', $borrowerRow['smsalertnumber']);
$interface->assign('smsProviderId', $borrowerRow['sms_provider_id']);
}
+ $borrowerRS->close();
//Lookup which transports are allowed
/** @noinspection SqlResolve */
@@ -6057,6 +6092,7 @@ public function getMessagingSettingsTemplate(User $patron): ?string {
}
$messagingSettings[$transportId]['allowableTransports'][$transportSetting['message_transport_type']] = $transportSetting['message_transport_type'];
}
+ $transportSettingRS->close();
//Get the list of notices to display information for
/** @noinspection SqlResolve */
@@ -6100,6 +6136,7 @@ public function getMessagingSettingsTemplate(User $patron): ?string {
}
$messageAttributes[] = $messageType;
}
+ $messageAttributesRS->close();
$interface->assign('messageAttributes', $messageAttributes);
//Get messaging settings for the user
@@ -6125,6 +6162,7 @@ public function getMessagingSettingsTemplate(User $patron): ?string {
$messagingSettings[$messageType]['selectedTransports'][$userMessagingSetting['message_transport_type']] = $userMessagingSetting['message_transport_type'];
}
}
+ $userMessagingSettingsRS->close();
$interface->assign('messagingSettings', $messagingSettings);
$validNoticeDays = [];
@@ -6154,6 +6192,7 @@ public function getMessagingSettingsTemplate(User $patron): ?string {
} else {
$noticeLanguages[$language] = $language;
}
+ $languageRS->close();
}
/** @noinspection SqlResolve */
$borrowerLanguageSql = "SELECT lang FROM borrowers where borrowernumber = '" . mysqli_escape_string($this->dbConnection, $patron->unique_ils_id) . "'";
@@ -6161,8 +6200,9 @@ public function getMessagingSettingsTemplate(User $patron): ?string {
if ($borrowerLanguageRow = $borrowerLanguageRS->fetch_assoc()) {
$preferredNoticeLanguage = $borrowerLanguageRow['lang'];
}
-
+ $borrowerLanguageRS->close();
}
+
$interface->assign('canTranslateNotices', $canTranslateNotices);
$interface->assign('noticeLanguages', $noticeLanguages);
$interface->assign('preferredNoticeLanguage', $preferredNoticeLanguage);
@@ -6568,6 +6608,7 @@ public function getShowAutoRenewSwitch(User $patron) {
while ($curRow = $results->fetch_assoc()) {
$showAutoRenew = $curRow['value'];
}
+ $results->close();
return $showAutoRenew;
}
@@ -6583,6 +6624,7 @@ public function isAutoRenewalEnabledForUser(User $patron) {
$autoRenewEnabled = $curRow['autorenew_checkouts'];
break;
}
+ $results->close();
}
return $autoRenewEnabled;
}
@@ -6605,6 +6647,7 @@ public function updateAutoRenewal(User $patron, bool $allowAutoRenewal) {
$address = $curRow['address'];
$city = $curRow['city'];
}
+ $results->close();
}
$postVariables = [
@@ -6687,6 +6730,7 @@ function getPasswordRecoveryTemplate() {
$uniqueKeyValid = true;
}
}
+ $lookupResult->close();
if (!$uniqueKeyValid) {
$error = translate([
'text' => 'The link you clicked is either invalid, or expired.
Be sure you used the link from the email, or contact library staff for assistance.
Please contact the library if you need further assistance.',
@@ -6728,6 +6772,8 @@ function processPasswordRecovery() {
$uniqueKeyValid = true;
}
}
+ $lookupResult->close();
+
if (!$uniqueKeyValid) {
$error = translate([
'text' => 'The link you clicked is either invalid, or expired.
Be sure you used the link from the email, or contact library staff for assistance.
Please contact the library if you need further assistance.',
@@ -6841,6 +6887,8 @@ function setExtendedAttributes() {
$extendedAttributes[] = $attribute;
}
+ $borrowerAttributeTypesRS->close();
+
return $extendedAttributes;
}
@@ -7028,6 +7076,7 @@ public function getEditableUsername(User $user) {
if ($curRow = $results->fetch_assoc()) {
return $curRow['userId'];
}
+ $results->close();
}
return null;
}
@@ -7049,6 +7098,7 @@ public function updateEditableUsername(User $patron, string $username): array {
'message' => 'Sorry, that username is not available.',
];
}
+ $results->close();
}
//Load required fields from Koha here to make sure we don't wipe them out
/** @noinspection SqlResolve */
@@ -7061,6 +7111,7 @@ public function updateEditableUsername(User $patron, string $username): array {
$address = $curRow['address'];
$city = $curRow['city'];
}
+ $results->close();
}
$postVariables = [
@@ -7143,6 +7194,7 @@ public function getILSMessages(User $user) {
'messageStyle' => 'info',
];
}
+ $results->close();
}
}
@@ -7186,6 +7238,7 @@ public function getILSMessages(User $user) {
];
}
}
+ $results->close();
}
return $messages;
@@ -7424,6 +7477,7 @@ public function getCurbsidePickupSettings($locationCode) {
}
}
}
+
$results->close();
}
} else {
@@ -7680,6 +7734,7 @@ function validateUniqueId(User $user) {
$lookupUserResult = mysqli_query($this->dbConnection, $sql);
if ($lookupUserResult->num_rows > 0) {
$lookupUserRow = $lookupUserResult->fetch_assoc();
+ $lookupUserResult->close();
if ($lookupUserRow['borrowernumber'] != $user->unique_ils_id) {
global $logger;
$logger->log("Updating unique id for user from $user->unique_ils_id to {$lookupUserRow['borrowernumber']}", Logger::LOG_WARNING);
@@ -7912,7 +7967,9 @@ public function lookupAccountByEmail(string $email) : array {
'name' => trim(trim($curRow['firstname'] . ' ' . $curRow['middle_name']) . ' ' . $curRow['surname']),
];
}
+ $results->close();
}
+
if (count($cardNumbers) == 0) {
return [
'success' => false,
@@ -7947,6 +8004,7 @@ public function lookupAccountByPhoneNumber(string $phone) : array {
'patronId' => $curRow['borrowernumber']
];
}
+ $results->close();
}
if (count($cardNumbers) == 0) {
return [
@@ -7977,6 +8035,7 @@ public function getBasicRegistrationForm() : array {
while ($curRow = $results->fetch_assoc()) {
$kohaPreferences[$curRow['variable']] = $curRow['value'];
}
+ $results->close();
$unwantedFields = explode('|', $kohaPreferences['PatronSelfRegistrationBorrowerUnwantedField']);
$requiredFields = explode('|', $kohaPreferences['PatronSelfRegistrationBorrowerMandatoryField']);
@@ -8185,6 +8244,8 @@ public function bypassReadingHistoryUpdate($patron, $isNightlyUpdate) : bool {
$expirationDate = strtotime($curRow['dateexpiry']);
}
}
+
+ $results->close();
}
//Don't update reading history if we've never seen the patron or the patron was last seen before we last updated reading history
@@ -8199,7 +8260,15 @@ public function bypassReadingHistoryUpdate($patron, $isNightlyUpdate) : bool {
}
}
- public function checkoutByAPI(User $patron, $barcode, $currentLocationId): array {
+ public function hasAPICheckout() : bool {
+ if($this->getKohaVersion() >= 23.11) {
+ return true;
+ }else {
+ return false;
+ }
+ }
+
+ public function checkoutByAPI(User $patron, $barcode, Location $currentLocation): array {
if($this->getKohaVersion() >= 23.11) {
$item = [];
$result = [
@@ -8238,7 +8307,7 @@ public function checkoutByAPI(User $patron, $barcode, $currentLocationId): array
} else {
require_once ROOT_DIR . '/sys/AspenLiDA/SelfCheckSetting.php';
$scoSettings = new AspenLiDASelfCheckSetting();
- $checkoutLocationSetting = $scoSettings->getCheckoutLocationSetting($currentLocationId);
+ $checkoutLocationSetting = $scoSettings->getCheckoutLocationSetting($currentLocation->code);
$this->initDatabaseConnection();
/** @noinspection SqlResolve */
@@ -8254,7 +8323,7 @@ public function checkoutByAPI(User $patron, $barcode, $currentLocationId): array
];
$postParams = json_encode($checkoutParams);
- $checkoutLocation = $currentLocationId; // assign checkout to current location logged into (default)
+ $checkoutLocation = $currentLocation->code; // assign checkout to current location logged into (default)
if($checkoutLocationSetting == 1) {
// assign checkout to user home location
$checkoutLocation = $patron->getHomeLocationCode();
@@ -8265,7 +8334,7 @@ public function checkoutByAPI(User $patron, $barcode, $currentLocationId): array
$this->apiCurlWrapper->addCustomHeaders([
'Authorization: Bearer ' . $oAuthToken,
- 'x-koha-library: ' . $checkoutLocation,
+ 'x-koha-library: ' . $checkoutLocation->code,
'User-Agent: Aspen Discovery',
'Accept: */*',
'Cache-Control: no-cache',
@@ -8375,6 +8444,8 @@ public function checkoutByAPI(User $patron, $barcode, $currentLocationId): array
'isPublicFacing' => true,
]);
}
+
+ $lookupItemResult->close();
}
return $result;
@@ -8406,6 +8477,7 @@ public function getMessageTypes(): array {
$transports[$curRow['module']][$i]['name'] = $curRow['name'];
$i++;
}
+ $results->close();
}
return $transports;
@@ -8415,7 +8487,7 @@ public function updateMessageQueue(): array {
$this->initDatabaseConnection();
/** @noinspection SqlResolve */
- $sql = "SELECT * FROM message_queue where message_transport_type like 'email'";
+ $sql = "SELECT * FROM message_queue where message_transport_type like 'email' and time_queue < DATE_SUB(NOW(), INTERVAL 24 HOUR)";
$results = mysqli_query($this->dbConnection, $sql);
if($results) {
$numAdded = 0;
@@ -8465,6 +8537,8 @@ public function updateMessageQueue(): array {
}
}
+ $results->close();
+
return [
'success' => true,
'message' => 'Added ' . $numAdded . ' to message queue'
@@ -8481,7 +8555,7 @@ public function updateUserMessageQueue(User $patron): array {
$this->initDatabaseConnection();
/** @noinspection SqlResolve */
- $sql = "SELECT * FROM message_queue where message_transport_type like 'email' and borrowernumber = '" . mysqli_escape_string($this->dbConnection, $patron->unique_ils_id) . "'";
+ $sql = "SELECT * FROM message_queue where message_transport_type like 'email' and time_queue < DATE_SUB(NOW(), INTERVAL 24 HOUR) and borrowernumber = '" . mysqli_escape_string($this->dbConnection, $patron->unique_ils_id) . "'";
$results = mysqli_query($this->dbConnection, $sql);
if($results) {
require_once ROOT_DIR . '/sys/Account/UserILSMessage.php';
@@ -8524,6 +8598,8 @@ public function updateUserMessageQueue(User $patron): array {
}
+ $results->close();
+
return [
'success' => true,
'message' => 'Updated user message queue'
@@ -8556,4 +8632,4 @@ protected function getUserMessageTranslation($code, User $patron): array {
return $result;
}
-}
\ No newline at end of file
+}
diff --git a/code/web/Drivers/Nashville.php b/code/web/Drivers/Nashville.php
index fa982df6e7..76d2fc81e0 100644
--- a/code/web/Drivers/Nashville.php
+++ b/code/web/Drivers/Nashville.php
@@ -560,7 +560,6 @@ public function getHoldsReportData($location): array {
SHELF_LOCATION
, CALL_NUMBER
, TITLE
- ;
EOT;
$stid = oci_parse($this->dbConnection, $sql);
// consider using oci_set_prefetch to improve performance
@@ -613,7 +612,7 @@ public function getStudentBarcodeData($location, $homeroom): array {
and patronbranch.branchcode = '$location'
and p.street2 is not null
order by
- 1, 3, 5, 7
+ 1, 3, 7, 8, 9
EOT;
}
// If homeroom is ALL_*, then we are looking for all students in a grade level
@@ -623,7 +622,7 @@ public function getStudentBarcodeData($location, $homeroom): array {
select
patronbranch.branchcode
, patronbranch.branchname
- , p.bty.btynumber as bty
+ , p.bty as bty
, case
when p.bty < 21 or p.bty > 34
then null
@@ -653,9 +652,7 @@ public function getStudentBarcodeData($location, $homeroom): array {
and patronbranch.branchcode = '$location'
and p.street2 is not null
order by
- patronbranch.branchcode
- , p.sponsor
- , p.name
+ p.name
EOT;
// If homeroom is a specific homeroom, then we are looking for students -- and staff -- in that homeroom
} else {
diff --git a/code/web/Drivers/Sierra.php b/code/web/Drivers/Sierra.php
index 2b783cd98a..7b80c35a9a 100644
--- a/code/web/Drivers/Sierra.php
+++ b/code/web/Drivers/Sierra.php
@@ -1,11 +1,9 @@
-_connectToAPI();
if ($tokenData) {
@@ -454,11 +452,11 @@ public function getHolds($patron): array {
// for item level holds we need to grab the bib id.
$id = $recordId; //$m[1];
if ($recordType == 'i') {
- $itemId = ".i{$id}" . $this->getCheckDigit($id);
+ $itemId = ".i$id" . $this->getCheckDigit($id);
$id = $this->getBibIdForItem($itemId, $id);
} else {
$recordXD = $this->getCheckDigit($id);
- $id = ".b{$id}{$recordXD}";
+ $id = ".b$id$recordXD";
}
if ($id != false) {
@@ -767,11 +765,48 @@ function renewCheckout($patron, $recordId, $itemId = null, $itemIndex = null) {
return $return;
}
+ private function getTitleFromItemLink(string $itemLink) {
+ $bibId = $this->getBibIdFromItemLink($itemLink);
+ $title = '';
+ if ($bibId != false) {
+ require_once ROOT_DIR . '/RecordDrivers/MarcRecordDriver.php';
+ $recordDriver = new MarcRecordDriver((string)$bibId);
+ if ($recordDriver->isValid()) {
+ $title = $recordDriver->getTitle();
+ } else {
+ $bibIdShort = substr(str_replace('.b', '', $bibId), 0, -1);
+ $getBibResponse = $this->_callUrl('sierra.getBib', $this->accountProfile->vendorOpacUrl . "/iii/sierra-api/v{$this->accountProfile->apiVersion}/bibs/{$bibIdShort}");
+ if ($getBibResponse) {
+ $title = $getBibResponse->title;
+ }
+ }
+ }
+ return $title;
+ }
+ private function getTitleByItemId(string $itemId, string $itemShortId){
+ $bibId = $this->getBibIdForItem($itemId, $itemShortId);
+ $title = '';
+ if ($bibId != false) {
+ require_once ROOT_DIR . '/RecordDrivers/MarcRecordDriver.php';
+ $recordDriver = new MarcRecordDriver((string)$bibId);
+ if ($recordDriver->isValid()) {
+ $title = $recordDriver->getTitle();
+ } else {
+ $bibIdShort = substr(str_replace('.b', 'b', $bibId), 0, -1);
+ $getBibResponse = $this->_callUrl('sierra.getBib', $this->accountProfile->vendorOpacUrl . "/iii/sierra-api/v{$this->accountProfile->apiVersion}/bibs/{$bibIdShort}");
+ if ($getBibResponse) {
+ $title = $getBibResponse->title;
+ }
+ }
+ }
+ return $title;
+ }
+
/**
* @param string $itemId
* @return string|false
*/
- private function getBibIdForItem(string $itemId, $shortId) {
+ private function getBibIdForItem(string $itemId, ?string $shortId) {
require_once ROOT_DIR . '/sys/Grouping/GroupedWorkItem.php';
require_once ROOT_DIR . '/sys/Grouping/GroupedWorkRecord.php';
$groupedWorkItem = new GroupedWorkItem();
@@ -784,26 +819,31 @@ private function getBibIdForItem(string $itemId, $shortId) {
$id = $groupedWorkRecord->recordIdentifier;
}
}
- if ($id == false) {
+ if ($id == false && !empty($shortId)) {
//Lookup the bib id from the Sierra APIs
$sierraUrl = $this->accountProfile->vendorOpacUrl;
$sierraUrl .= "/iii/sierra-api/v{$this->accountProfile->apiVersion}/items/$shortId";
- $itemInfo = $this->_callUrl('sierra.getItemInfo', $sierraUrl);
- if (!empty($itemInfo)) {
- if (empty($itemInfo->bibIds)) {
- $id = false;
- }else if (is_array($itemInfo->bibIds)) {
- $id = reset($itemInfo->bibIds);
- $id = '.b' . $id . $this->getCheckDigit($id);
- }else if (is_string($itemInfo->bibIds)) {
- $id = $itemInfo->bibIds;
- $id = '.b' . $id . $this->getCheckDigit($id);
- }else{
- $id = false;
- }
- } else {
+ $id = $this->getBibIdFromItemLink($sierraUrl);
+ }
+ return $id;
+ }
+
+ private function getBibIdFromItemLink(string $itemLink) {
+ $itemInfo = $this->_callUrl('sierra.getItemInfo', $itemLink);
+ if (!empty($itemInfo)) {
+ if (empty($itemInfo->bibIds)) {
+ $id = false;
+ }else if (is_array($itemInfo->bibIds)) {
+ $id = reset($itemInfo->bibIds);
+ $id = '.b' . $id . $this->getCheckDigit($id);
+ }else if (is_string($itemInfo->bibIds)) {
+ $id = $itemInfo->bibIds;
+ $id = '.b' . $id . $this->getCheckDigit($id);
+ }else{
$id = false;
}
+ } else {
+ $id = false;
}
return $id;
}
@@ -1519,14 +1559,282 @@ public function updatePatronInfo($patron, $canUpdateContactInfo, $fromMasquerade
return $result;
}
+ public function getSelfRegistrationTerms() {
+ global $library;
+
+ if (!empty($library->selfRegistrationFormId)) {
+ require_once ROOT_DIR . '/sys/SelfRegistrationForms/SierraSelfRegistrationForm.php';
+ $selfRegistrationForm = new SierraSelfRegistrationForm();
+ $selfRegistrationForm->id = $library->selfRegistrationFormId;
+ if ($selfRegistrationForm->find(true)) {
+ $tosId = $selfRegistrationForm->termsOfServiceSetting;
+ require_once ROOT_DIR . '/sys/SelfRegistrationForms/SelfRegistrationTerms.php';
+ $tos = new SelfRegistrationTerms();
+ $tos->id = $tosId;
+ if ($tosId != -1){
+ if ($tos->find(true)) {
+ return $tos;
+ }
+ }
+ }
+ return null;
+ }
+ return null;
+ }
+
public function getSelfRegistrationFields() {
- return parent::getSelfRegistrationFields();
- // TODO: Use Sierra APIs to get Self Registration fields
+ global $library;
+
+ $pickupLocations = [];
+ $location = new Location();
+ //0 = no restrictions (ignore location setting)
+ if ($library->selfRegistrationLocationRestrictions == 1) {
+ //All Library Locations (ignore location setting)
+ $location->libraryId = $library->libraryId;
+ } elseif ($library->selfRegistrationLocationRestrictions == 2) {
+ //Valid pickup locations
+ $location->whereAdd('validSelfRegistrationBranch <> 2');
+ $location->orderBy('isMainBranch DESC, displayName');
+ } elseif ($library->selfRegistrationLocationRestrictions == 3) {
+ //Valid pickup locations
+ $location->libraryId = $library->libraryId;
+ $location->whereAdd('validSelfRegistrationBranch <> 2');
+ $location->orderBy('isMainBranch DESC, displayName');
+ }
+ if ($location->find()) {
+ while ($location->fetch()) {
+ $pickupLocations[$location->code] = $location->displayName;
+ }
+ if (count($pickupLocations) > 1) {
+ array_unshift($pickupLocations, translate([
+ 'text' => 'Please select a location',
+ 'isPublicFacing' => true,
+ ]));
+ }
+ }
+
+ global $library;
+ $hasCustomSelfRegistrationFrom = false;
+
+ if (!empty($library->selfRegistrationFormId)) {
+ require_once ROOT_DIR . '/sys/SelfRegistrationForms/SierraSelfRegistrationForm.php';
+ $selfRegistrationForm = new SierraSelfRegistrationForm();
+ $selfRegistrationForm->id = $library->selfRegistrationFormId;
+ if ($selfRegistrationForm->find(true)) {
+ $customFields = $selfRegistrationForm->getFields();
+ if ($customFields != null && count($customFields) > 0) {
+ $hasCustomSelfRegistrationFrom = true;
+ }
+ }
+ }
+
+ $pickupLocationField = [
+ 'property' => 'pickupLocation',
+ 'type' => 'enum',
+ 'label' => 'Home Library',
+ 'description' => 'Please choose the Library location you would prefer to use',
+ 'values' => $pickupLocations,
+ 'required' => true,
+ ];
+
+ $fields = [];
+ if ($hasCustomSelfRegistrationFrom) {
+ $hiddenDefault = false;
+ $fields['librarySection'] = [
+ 'property' => 'librarySection',
+ 'type' => 'section',
+ 'label' => 'Library',
+ 'hideInLists' => true,
+ 'expandByDefault' => true,
+ 'properties' => [],
+ ];
+ $fields['identitySection'] = [
+ 'property' => 'identitySection',
+ 'type' => 'section',
+ 'label' => 'Identity',
+ 'hideInLists' => true,
+ 'expandByDefault' => true,
+ 'properties' => [],
+ ];
+ $fields['mainAddressSection'] = [
+ 'property' => 'mainAddressSection',
+ 'type' => 'section',
+ 'label' => 'Main Address',
+ 'hideInLists' => true,
+ 'expandByDefault' => true,
+ 'properties' => [],
+ ];
+ $fields['contactInformationSection'] = [
+ 'property' => 'contactInformationSection',
+ 'type' => 'section',
+ 'label' => 'Contact Information',
+ 'hideInLists' => true,
+ 'expandByDefault' => true,
+ 'properties' => [],
+ ];
+ //Use self registration fields
+ /** @var SelfRegistrationFormValues $customField */
+ foreach ($customFields as $customField) {
+ if ($customField->ilsName == 'library') {
+ if (count($pickupLocations) == 1) {
+ $fields['librarySection'] = [
+ 'property' => 'librarySection',
+ 'type' => 'section',
+ 'label' => 'Library',
+ 'hideInLists' => true,
+ 'expandByDefault' => true,
+ 'properties' => [
+ $customField->ilsName => $pickupLocationField,
+ ],
+ 'hiddenByDefault' => true,
+ ];
+ } else {
+ $fields['librarySection'] = [
+ 'property' => 'librarySection',
+ 'type' => 'section',
+ 'label' => 'Library',
+ 'hideInLists' => true,
+ 'expandByDefault' => true,
+ 'properties' => [
+ $customField->ilsName => $pickupLocationField,
+ ],
+ ];
+ }
+ } elseif ($customField->ilsName == 'zip' && !empty($library->validSelfRegistrationZipCodes)) {
+ $fields[$customField->section]['properties'][] = [
+ 'property' => $customField->ilsName,
+ 'type' => $customField->fieldType,
+ 'label' => $customField->displayName,
+ 'required' => $customField->required,
+ 'note' => $customField->note,
+ 'validationPattern' => $library->validSelfRegistrationZipCodes,
+ 'validationMessage' => translate([
+ 'text' => 'Please enter a valid zip code',
+ 'isPublicFacing' => true,
+ ]),
+ ];
+ } elseif ($customField->ilsName == 'state') {
+ if (!empty($library->validSelfRegistrationStates)){
+ $validStates = explode('|', $library->validSelfRegistrationStates);
+ $validStates = array_combine($validStates, $validStates);
+ $fields[$customField->section]['properties'][] = [
+ 'property' => $customField->ilsName,
+ 'type' => 'enum',
+ 'values' => $validStates,
+ 'label' => $customField->displayName,
+ 'required' => $customField->required,
+ 'note' => $customField->note,
+ ];
+ } else {
+ $fields[$customField->section]['properties'][] = [
+ 'property' => $customField->ilsName,
+ 'type' => $customField->fieldType,
+ 'label' => $customField->displayName,
+ 'required' => $customField->required,
+ 'note' => $customField->note,
+ 'maxLength' => 2,
+ ];
+ }
+ } else {
+ $fields[$customField->section]['properties'][] = [
+ 'property' => $customField->ilsName,
+ 'type' => $customField->fieldType,
+ 'label' => $customField->displayName,
+ 'required' => $customField->required,
+ 'note' => $customField->note
+ ];
+ }
+ }
+ foreach ($fields as $section) {
+ if ($section['type'] == 'section') {
+ if (empty($section['properties'])) {
+ unset ($fields[$section['property']]);
+ }
+ }
+ }
+ }
+ return $fields;
}
public function selfRegister(): array {
- return parent::selfRegister();
- // TODO: Use Sierra APIs to self register
+ global $library;
+ $selfRegResult = [
+ 'success' => false,
+ 'message' => 'Unknown Error while registering your account'
+ ];
+
+ $selfRegistrationForm = null;
+ $formFields = null;
+ if ($library->selfRegistrationFormId > 0){
+ $selfRegistrationForm = new SierraSelfRegistrationForm();
+ $selfRegistrationForm->id = $library->selfRegistrationFormId;
+ if ($selfRegistrationForm->find(true)) {
+ $formFields = $selfRegistrationForm->getFields();
+ }else {
+ $selfRegistrationForm = null;
+ }
+ }
+
+ $params = [];
+
+ if ($formFields != null) {
+ foreach ($formFields as $fieldObj){
+ $field = $fieldObj->ilsName;
+ if ($field == 'firstName') {
+ if (isset($_REQUEST['middleName'])) {
+ $fullName = $_REQUEST['firstName'] . ' ' . $_REQUEST['middleName'] . ' ' . $_REQUEST['lastName'];
+ } else {
+ $fullName = $_REQUEST['firstName'] . ' ' . $_REQUEST['lastName'];
+ }
+ $params['names'] = [$fullName];
+ }
+ elseif ($field == 'birthDate') {
+ $params['birthDate'] = $_REQUEST['birthDate'];
+ }
+ elseif ($field == 'email') {
+ $params['emails'] = [$_REQUEST['email']];
+ if ($selfRegistrationForm->selfRegEmailBarcode) {
+ $params['barcodes'] = [$_REQUEST['email']];
+ }
+ }
+ elseif ($field == 'phone') {
+ $tmpPhone = new stdClass();
+ $tmpPhone->type = 'p';
+ $tmpPhone->number = $_REQUEST['phone'];
+ $params['phones'][] = $tmpPhone;
+ }
+ elseif ($field == 'street') {
+ $params['addresses'] = [];
+ $address = new stdClass();
+ $address->lines = [];
+ $address->type = 'a';
+ $address->lines[] = $_REQUEST['street'];
+ $cityStateZip = $_REQUEST['city'] . ', ' . $_REQUEST['state'] . ' ' . $_REQUEST['zip'];
+ $address->lines[] = $cityStateZip;
+
+ $params['addresses'][] = $address;
+ }
+ elseif ($field == 'barcode') {
+ $params['barcodes'] = [$_REQUEST['barcode']];
+ }
+ elseif ($field == 'pin') {
+ $params['pin'] = $_REQUEST['pin'];
+ }
+ }
+ }
+
+ $sierraUrl = $this->accountProfile->vendorOpacUrl . "/iii/sierra-api/v{$this->accountProfile->apiVersion}/patrons/";
+ $this->_postPage('sierra.createPatron', $sierraUrl, json_encode($params));
+
+ if ($this->lastResponseCode == 200) {
+ $selfRegResult = [
+ 'success' => true,
+ 'barcode' => $params['barcodes'][0],
+ 'password' => $params['pin']
+ ];
+ }
+
+ return $selfRegResult;
}
public function getFines($patron = null, $includeMessages = false): array {
@@ -1555,20 +1863,7 @@ public function getFines($patron = null, $includeMessages = false): array {
preg_match($this->urlIdRegExp, $fineEntry->item, $m);
$itemIdShort = $m[1];
$itemId = ".i" . $itemIdShort . $this->getCheckDigit($itemIdShort);
- $bibId = $this->getBibIdForItem($itemId, $itemIdShort);
- if ($bibId != false) {
- require_once ROOT_DIR . '/RecordDrivers/MarcRecordDriver.php';
- $recordDriver = new MarcRecordDriver((string)$bibId);
- if ($recordDriver->isValid()) {
- $message = $recordDriver->getTitle();
- } else {
- $bibIdShort = substr(str_replace('.b', 'b', $bibId), 0, -1);
- $getBibResponse = $this->_callUrl('sierra.getBib', $this->accountProfile->vendorOpacUrl . "/iii/sierra-api/v{$this->accountProfile->apiVersion}/bibs/{$bibIdShort}");
- if ($getBibResponse) {
- $message = $getBibResponse->title;
- }
- }
- }
+ $message = $this->getTitleByItemId($itemId, $itemIdShort);
}
}
$fines[] = [
@@ -1985,6 +2280,7 @@ public function getUsernameValidationRules(): array {
public function updateEditableUsername(User $patron, string $username): array {
global $library;
+ /** @noinspection PhpArrayIndexImmediatelyRewrittenInspection */
$result = [
'success' => false,
'message' => 'Unknown error updating username',
@@ -1999,6 +2295,7 @@ public function updateEditableUsername(User $patron, string $username): array {
$sierraUrl = $this->accountProfile->vendorOpacUrl;
$sierraUrl = $sierraUrl . "/iii/sierra-api/v{$this->accountProfile->apiVersion}/patrons/" . $patron->unique_ils_id;
+ /** @noinspection PhpUnusedLocalVariableInspection */
$updatePatronResponse = $this->_sendPage('sierra.updateEditableUsername', 'PUT', $sierraUrl, json_encode($params));
if ($this->lastResponseCode == 204) {
@@ -2012,4 +2309,381 @@ public function updateEditableUsername(User $patron, string $username): array {
return $result;
}
+
+ public function hasAPICheckout() : bool {
+ return true;
+ }
+
+ public function checkoutByAPI(User $patron, $barcode, Location $currentLocation): array {
+ $result = [
+ 'success' => false,
+ 'message' => translate([
+ 'text' => 'There was an error checking out this title.',
+ 'isPublicFacing' => true,
+ ]),
+ 'title' => translate([
+ 'text' => 'Unable to checkout title',
+ 'isPublicFacing' => true,
+ ]),
+ 'api' => [
+ 'title' => translate([
+ 'text' => 'Unable to checkout title',
+ 'isPublicFacing' => true,
+ ]),
+ 'message' => translate([
+ 'text' => 'There was an error checking out this title.',
+ 'isPublicFacing' => true,
+ ]),
+ ],
+ 'itemData' => []
+ ];
+
+ //Find the correct stat group to use
+ $doCheckout = false;
+ require_once ROOT_DIR . '/sys/AspenLiDA/SelfCheckSetting.php';
+ $scoSettings = new AspenLiDASelfCheckSetting();
+ $checkoutLocationSetting = $scoSettings->getCheckoutLocationSetting($currentLocation->code);
+ if ($checkoutLocationSetting == 0) {
+ //Use the active location, no change needed
+ $doCheckout = true;
+ }elseif ($checkoutLocationSetting == 1) {
+ //Use home location for the user
+ $currentLocation = $patron->getHomeLocation();
+ $doCheckout = true;
+ }else {
+ //Use the current location for the item
+ //To get the current location, we need to determine if the item is already on hold.
+ //If it is, make sure it is on hold for the active user and use the pickup_location
+ //If it is not on hold, use the current location for the item
+ $sierraDnaConnection = $this->connectToSierraDNA();
+
+ $getItemIdByBarcodeStmt = "SELECT * from sierra_view.item_view where barcode = $1";
+
+ //Lookup the item by barcode
+ $getItemRS = pg_query_params($sierraDnaConnection, $getItemIdByBarcodeStmt, [$barcode]);
+ if ($getItemRS === false || pg_num_rows($getItemRS) === 0) {
+ $result['message'] = translate([
+ 'text' => 'Unable to checkout this item. Cannot find item for barcode %1%.',
+ 1 => $barcode,
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'Unable to checkout this item. Cannot find item for barcode %1%.',
+ 1 => $barcode,
+ 'isPublicFacing' => true,
+ ]);
+ }else{
+ if (pg_num_rows($getItemRS) > 1) {
+ $result['message'] = translate([
+ 'text' => 'Unable to complete checkout because more than one item was found for barcode %1%.',
+ 1 => $barcode,
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'Unable to complete checkout because more than one item was found for barcode %1%.',
+ 1 => $barcode,
+ 'isPublicFacing' => true,
+ ]);
+ }else{
+ $itemInfo = pg_fetch_array($getItemRS, 0);
+ $itemRecordIdentifier = $itemInfo['id'];
+ $itemHomeLocation = $itemInfo['location_code'];
+
+ //Check to see if the record has a hold
+ $getItemHoldStmt = "SELECT * from sierra_view.hold where record_id = $1 and status IN ('b', 'j', 'i')";
+ $getHoldRS = pg_query_params($sierraDnaConnection, $getItemHoldStmt, [$itemRecordIdentifier]);
+ if ($getHoldRS !== false && pg_num_rows($getHoldRS) == 1) {
+ $holdInfo = pg_fetch_array($getHoldRS, 0);
+
+ //Get the patron_record_id
+ $getPatronIdStmt = "SELECT id from sierra_view.record_metadata where record_type_code = 'p' and record_num = $1";
+ $getPatronIdRS = pg_query_params($sierraDnaConnection, $getPatronIdStmt, [$patron->unique_ils_id]);
+ if ($getPatronIdRS !== false && pg_num_rows($getPatronIdRS) == 1) {
+ $patronInfo = pg_fetch_array($getPatronIdRS, 0);
+ $patronRecordId = $patronInfo['id'];
+ if ($holdInfo['patron_record_id'] == $patronRecordId) {
+ $checkoutLocationCode = $holdInfo['pickup_location_code'];
+ $doCheckout = true;
+ }else{
+ $result['message'] = translate([
+ 'text' => 'Unable to complete checkout, this title is on hold for another patron.',
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'Unable to complete checkout, this title is on hold for another patron.',
+ 'isPublicFacing' => true,
+ ]);
+ }
+ }else{
+ $result['message'] = translate([
+ 'text' => 'Unable to complete checkout, could not find the patron id.',
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'Unable to complete checkout, could not find the patron id.',
+ 'isPublicFacing' => true,
+ ]);
+ }
+ }else{
+ //Use the home location for the item
+ $checkoutLocationCode = $itemHomeLocation;
+ $doCheckout = true;
+ }
+
+ if ($doCheckout && !empty($checkoutLocationCode)) {
+ //Find the appropriate branch in the branches table. This requires a bit of finesse since the
+ //Sierra code in Aspen may not match the code in the home location exactly
+ $tmpCurrentLocation = $this->getAspenLocationForSierraLocationCode($checkoutLocationCode);
+ if ($tmpCurrentLocation != null) {
+ $currentLocation = $tmpCurrentLocation;
+ }
+ }
+ }
+ }
+
+ $this->closeSierraDNAConnection();
+ }
+
+ if ($doCheckout) {
+ $sierraUrl = $this->accountProfile->vendorOpacUrl . "/iii/sierra-api/v{$this->accountProfile->apiVersion}/patrons/checkout";
+
+ $params = [];
+ $params['patronBarcode'] = $patron->ils_barcode;
+ $params['itemBarcode'] = $barcode;
+ if (!empty($patron->ils_password)) {
+ $params['patronPin'] = $patron->ils_password;
+ }
+ if (!empty($currentLocation->statGroup) && $currentLocation->statGroup != -1) {
+ $params['statgroup'] = $currentLocation->statGroup;
+ }
+
+ $checkoutResult = $this->_postPage('sierra.checkout', $sierraUrl, json_encode($params));
+ if ($this->lastResponseCode == 200) {
+ $checkoutLink = $checkoutResult->id;
+
+ $result['success'] = true;
+ $result['message'] = translate([
+ 'text' => 'You have successfully checked out this title.',
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['title'] = translate([
+ 'text' => 'Checkout successful',
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'You have successfully checked out this title.',
+ 'isPublicFacing' => true,
+ ]);
+
+ //Get information about the checkout
+ $checkout = $this->getCheckoutDataFromLink($checkoutLink);
+ if ($checkout != null) {
+ $itemUrl = $checkout->item;
+ $title = $this->getTitleFromItemLink($itemUrl);
+
+ $result['itemData'] = [
+ 'title' => $title,
+ 'due' => $checkout->dueDate ?? null,
+ 'barcode' => $barcode,
+ ];
+ }
+ } elseif ($this->lastResponseCode == 405) {
+ $result['message'] = translate([
+ 'text' => 'Checkouts cannot be performed by the provided API user.',
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['title'] = translate([
+ 'text' => 'Checkouts cannot be performed by the provided API user.',
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'Checkouts cannot be performed by the provided API user.',
+ 'isPublicFacing' => true,
+ ]);
+ } elseif ($this->lastResponseCode == 500) {
+ $result['message'] = translate([
+ 'text' => $checkoutResult->name,
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => $checkoutResult->name,
+ 'isPublicFacing' => true,
+ ]);
+ } elseif (!empty($checkoutResult)) {
+ $result['title'] = translate([
+ 'text' => $checkoutResult->name,
+ 'isPublicFacing' => true,
+ ]);
+ $result['message'] = translate([
+ 'text' => $checkoutResult->description,
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['title'] = translate([
+ 'text' => $checkoutResult->name,
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => $checkoutResult->description,
+ 'isPublicFacing' => true,
+ ]);
+ }
+ }
+
+ return $result;
+ }
+
+ public function hasAPICheckIn() {
+ return true;
+ }
+
+ public function checkInByAPI(User $patron, $barcode, Location $currentLocation): array {
+ $result = [
+ 'success' => false,
+ 'message' => translate([
+ 'text' => 'There was an error checking in this title.',
+ 'isPublicFacing' => true,
+ ]),
+ 'title' => translate([
+ 'text' => 'Unable to check in title',
+ 'isPublicFacing' => true,
+ ]),
+ 'api' => [
+ 'title' => translate([
+ 'text' => 'Unable to check in title',
+ 'isPublicFacing' => true,
+ ]),
+ 'message' => translate([
+ 'text' => 'There was an error checking in this title.',
+ 'isPublicFacing' => true,
+ ]),
+ ],
+ 'itemData' => []
+ ];
+
+ //Find the correct stat group to use
+ $doCheckout = false;
+ require_once ROOT_DIR . '/sys/AspenLiDA/SelfCheckSetting.php';
+ $scoSettings = new AspenLiDASelfCheckSetting();
+ $checkInLocationSetting = $scoSettings->getCheckoutLocationSetting($currentLocation->code);
+ if ($checkInLocationSetting == 0) {
+ //Use the active location, no change needed
+ $doCheckIn = true;
+ }elseif ($checkInLocationSetting == 1) {
+ //Use home location for the user
+ $currentLocation = $patron->getHomeLocation();
+ $doCheckIn = true;
+ }else {
+ //Use the current location for the item
+ //To get the current location, we need to determine if the item is already on hold.
+ //If it is, make sure it is on hold for the active user and use the pickup_location
+ //If it is not on hold, use the current location for the item
+ $sierraDnaConnection = $this->connectToSierraDNA();
+
+ $getItemIdByBarcodeStmt = "SELECT * from sierra_view.item_view where barcode = $1";
+
+ //Lookup the item by barcode
+ $getItemRS = pg_query_params($sierraDnaConnection, $getItemIdByBarcodeStmt, [$barcode]);
+ if ($getItemRS === false || pg_num_rows($getItemRS) === 0) {
+ $result['message'] = translate([
+ 'text' => 'Unable to check in this item. Cannot find item for barcode %1%.',
+ 1 => $barcode,
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'Unable to check in this item. Cannot find item for barcode %1%.',
+ 1 => $barcode,
+ 'isPublicFacing' => true,
+ ]);
+ }else{
+ if (pg_num_rows($getItemRS) > 1) {
+ $result['message'] = translate([
+ 'text' => 'Unable to complete check in because more than one item was found for barcode %1%.',
+ 1 => $barcode,
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'Unable to complete check in because more than one item was found for barcode %1%.',
+ 1 => $barcode,
+ 'isPublicFacing' => true,
+ ]);
+ }else{
+ //For check in, we don't need to check holds since it is already in possession of the patron
+ $itemInfo = pg_fetch_array($getItemRS, 0);
+ $itemHomeLocation = $itemInfo['location_code'];
+
+ $tmpCurrentLocation = $this->getAspenLocationForSierraLocationCode($itemHomeLocation);
+ if ($tmpCurrentLocation != null) {
+ $currentLocation = $tmpCurrentLocation;
+ }
+ $doCheckIn = true;
+ }
+ }
+
+ $this->closeSierraDNAConnection();
+ }
+
+ if ($doCheckIn) {
+ $sierraUrl = $this->accountProfile->vendorOpacUrl . "/iii/sierra-api/v{$this->accountProfile->apiVersion}/items/checkouts/{$barcode}";
+ if (!empty($currentLocation->statGroup) && $currentLocation->statGroup != -1) {
+ $sierraUrl .= '?statgroup=' . $currentLocation->statGroup;
+ }
+
+ $checkoutResult = $this->_sendPage( 'sierra.checkin', 'DELETE', $sierraUrl);
+ if ($this->lastResponseCode >= 200 && $this->lastResponseCode < 300) {
+ $result['success'] = true;
+ $result['message'] = translate([
+ 'text' => 'You have successfully checked in this title.',
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['title'] = translate([
+ 'text' => 'Check in successful',
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => 'You have successfully checked in this title.',
+ 'isPublicFacing' => true,
+ ]);
+ } elseif (!empty($checkoutResult)) {
+ $result['message'] = translate([
+ 'text' => $checkoutResult->name,
+ 'isPublicFacing' => true,
+ ]);
+ $result['api']['message'] = translate([
+ 'text' => $checkoutResult->name,
+ 'isPublicFacing' => true,
+ ]);
+ }
+ }
+
+ return $result;
+ }
+
+ public function getCheckoutDataFromLink($checkoutLink) {
+ $checkout = $this->_callUrl('sierra.getCheckouts', $checkoutLink);
+
+ if ($this->lastResponseCode == 200) {
+ return $checkout;
+ }else{
+ return null;
+ }
+ }
+
+ public function getCheckoutDataById($checkoutId) {
+ $sierraUrl = $this->accountProfile->vendorOpacUrl . "/iii/sierra-api/v{$this->accountProfile->apiVersion}/patrons/checkouts/$checkoutId";
+ return $this->getCheckoutDataFromLink($sierraUrl);
+ }
+
+ private function getAspenLocationForSierraLocationCode(string $locationCode) : ?Location {
+ $locationFound = false;
+ $tmpLocationCode = $locationCode;
+ while (!$locationFound && !empty($tmpLocationCode)) {
+ $location = new Location();
+ $location->whereAdd("code LIKE " . $location->escape($tmpLocationCode . '%'));
+ if ($location->find(true)) {
+ return $location;
+ }
+ $tmpLocationCode = substr($tmpLocationCode, 0, strlen($tmpLocationCode) -1);
+ }
+ return null;
+ }
}
\ No newline at end of file
diff --git a/code/web/Drivers/SirsiDynixROA.php b/code/web/Drivers/SirsiDynixROA.php
index ff962e04cf..347566e438 100644
--- a/code/web/Drivers/SirsiDynixROA.php
+++ b/code/web/Drivers/SirsiDynixROA.php
@@ -570,7 +570,7 @@ function selfRegister(): array {
if ($formFields != null) {
foreach ($formFields as $fieldObj){
- $field = $fieldObj->symphonyName;
+ $field = $fieldObj->ilsName;
//General Info
if ($field == 'firstName' && (!empty($_REQUEST['firstName'])) ) {
$createPatronInfoParameters['fields']['firstName'] = $this->getPatronFieldValue(trim($_REQUEST['firstName']), $library->useAllCapsWhenSubmittingSelfRegistration);
@@ -3108,6 +3108,7 @@ public function getSelfRegistrationTerms() {
}
return null;
}
+ return null;
}
public function getSelfRegistrationFields() {
global $library;
@@ -3215,7 +3216,7 @@ public function getSelfRegistrationFields() {
//Use self registration fields
/** @var SelfRegistrationFormValues $customField */
foreach ($customFields as $customField) {
- if ($customField->symphonyName == 'library') {
+ if ($customField->ilsName == 'library') {
if (count($pickupLocations) == 1) {
$fields['librarySection'] = [
'property' => 'librarySection',
@@ -3224,7 +3225,7 @@ public function getSelfRegistrationFields() {
'hideInLists' => true,
'expandByDefault' => true,
'properties' => [
- $customField->symphonyName => $pickupLocationField,
+ $customField->ilsName => $pickupLocationField,
],
'hiddenByDefault' => true,
];
@@ -3236,22 +3237,22 @@ public function getSelfRegistrationFields() {
'hideInLists' => true,
'expandByDefault' => true,
'properties' => [
- $customField->symphonyName => $pickupLocationField,
+ $customField->ilsName => $pickupLocationField,
],
];
}
- } elseif (($customField->symphonyName == 'parentname' || $customField->symphonyName == 'guardian' || $customField->symphonyName == 'care_of' || $customField->symphonyName == 'careof')) {
+ } elseif (($customField->ilsName == 'parentname' || $customField->ilsName == 'guardian' || $customField->ilsName == 'care_of' || $customField->ilsName == 'careof')) {
$fields[$customField->section]['properties'][] = [
- 'property' => $customField->symphonyName,
+ 'property' => $customField->ilsName,
'type' => $customField->fieldType,
'label' => $customField->displayName,
'required' => $customField->required,
'note' => $customField->note,
'hiddenByDefault' => $hiddenDefault,
];
- } elseif ($customField->symphonyName == 'cellPhone' && $selfRegistrationForm->promptForSMSNoticesInSelfReg) {
+ } elseif ($customField->ilsName == 'cellPhone' && $selfRegistrationForm->promptForSMSNoticesInSelfReg) {
$fields[$customField->section]['properties'][] = [
- 'property' => $customField->symphonyName,
+ 'property' => $customField->ilsName,
'type' => $customField->fieldType,
'label' => $customField->displayName,
'required' => $customField->required,
@@ -3264,9 +3265,9 @@ public function getSelfRegistrationFields() {
'label' => 'Receive notices via text',
'onchange' => 'AspenDiscovery.Account.updateSelfRegistrationFields()',
];
- } elseif ($customField->symphonyName == "email"){
+ } elseif ($customField->ilsName == "email"){
$fields[$customField->section]['properties'][] = [
- 'property' => $customField->symphonyName,
+ 'property' => $customField->ilsName,
'type' => 'email',
'label' => $customField->displayName,
'maxLength' => 128,
@@ -3282,9 +3283,9 @@ public function getSelfRegistrationFields() {
'required' => $customField->required,
'autocomplete' => false,
];
- } elseif ($customField->symphonyName == 'zip' && !empty($library->validSelfRegistrationZipCodes)) {
+ } elseif ($customField->ilsName == 'zip' && !empty($library->validSelfRegistrationZipCodes)) {
$fields[$customField->section]['properties'][] = [
- 'property' => $customField->symphonyName,
+ 'property' => $customField->ilsName,
'type' => $customField->fieldType,
'label' => $customField->displayName,
'required' => $customField->required,
@@ -3295,12 +3296,12 @@ public function getSelfRegistrationFields() {
'isPublicFacing' => true,
]),
];
- } elseif ($customField->symphonyName == 'state') {
+ } elseif ($customField->ilsName == 'state') {
if (!empty($library->validSelfRegistrationStates)){
$validStates = explode('|', $library->validSelfRegistrationStates);
$validStates = array_combine($validStates, $validStates);
$fields[$customField->section]['properties'][] = [
- 'property' => $customField->symphonyName,
+ 'property' => $customField->ilsName,
'type' => 'enum',
'values' => $validStates,
'label' => $customField->displayName,
@@ -3309,7 +3310,7 @@ public function getSelfRegistrationFields() {
];
} else {
$fields[$customField->section]['properties'][] = [
- 'property' => $customField->symphonyName,
+ 'property' => $customField->ilsName,
'type' => $customField->fieldType,
'label' => $customField->displayName,
'required' => $customField->required,
@@ -3319,7 +3320,7 @@ public function getSelfRegistrationFields() {
}
} else {
$fields[$customField->section]['properties'][] = [
- 'property' => $customField->symphonyName,
+ 'property' => $customField->ilsName,
'type' => $customField->fieldType,
'label' => $customField->displayName,
'required' => $customField->required,
diff --git a/code/web/bootstrap.php b/code/web/bootstrap.php
index 5df94a64ab..2a9a2d6d82 100644
--- a/code/web/bootstrap.php
+++ b/code/web/bootstrap.php
@@ -408,32 +408,22 @@ function getValidServerNames(): array {
function getGitBranch() {
global $interface;
- global $configArray;
- $gitName = $configArray['System']['gitVersionFile'];
- $branchName = 'Unknown';
- $branchNameWithCommit = 'Unknown';
- if ($gitName == 'HEAD') {
- $stringFromFile = file(ROOT_DIR . '/../../.git/HEAD');
- $stringFromFile = $stringFromFile[0]; //get the string from the array
- $explodedString = explode("/", $stringFromFile); //separate out by the "/" in the string
- $branchName = trim($explodedString[2]); //get the one that is always the branch name
- $branchNameWithCommit = $branchName;
- } else {
- if (file_exists(ROOT_DIR . '/../../.git/FETCH_HEAD')) {
- $stringFromFile = file(ROOT_DIR . '/../../.git/FETCH_HEAD');
- if (!empty($stringFromFile)) {
- $stringFromFile = $stringFromFile[0]; //get the string from the array
- if (preg_match('/(.*?)\s+branch\s+\'(.*?)\'.*/', $stringFromFile, $matches)) {
- $branchName = $matches[2]; //get the branch name
- $branchNameWithCommit = $matches[2] . ' (' . substr($matches[1], 0, 7) . ')'; //get the branch name
- }
- }
- } else {
- $branchName = 'Unknown';
- $branchNameWithCommit = 'Unknown';
+ $branchName = '';
+ $branchNameWithCommit = '';
+
+ $files = [];
+ foreach (glob('release_notes/*.MD') as $filename) {
+ if (preg_match('/\d{2}\.\d{2}\.\d{2}\.MD/', $filename)) {
+ $tmp = str_replace('.MD', '', $filename);
+ $tmp = str_replace('release_notes/', '', $tmp);
+ $files[] = $tmp;
}
}
+ asort($files);
+
+ $branchName = end($files);
+ $branchNameWithCommit = end($files);
if (!empty($interface)) {
$interface->assign('gitBranch', $branchName);
diff --git a/code/web/cron/fetchILSMessages.php b/code/web/cron/fetchILSMessages.php
index 92afc1480d..bcbdbba848 100644
--- a/code/web/cron/fetchILSMessages.php
+++ b/code/web/cron/fetchILSMessages.php
@@ -7,6 +7,7 @@
global $library;
$accountProfile = $library->getAccountProfile();
+// Temporary disabling to re-evaluate how to handle large server queries
/*if ($accountProfile) {
$catalogDriver = trim($accountProfile->driver);
if (!empty($catalogDriver)) {
diff --git a/code/web/interface/themes/responsive/Admin/usage_by_user_agent.tpl b/code/web/interface/themes/responsive/Admin/usage_by_user_agent.tpl
index 1403ce3ffb..4603d205cf 100644
--- a/code/web/interface/themes/responsive/Admin/usage_by_user_agent.tpl
+++ b/code/web/interface/themes/responsive/Admin/usage_by_user_agent.tpl
@@ -2,6 +2,17 @@