diff --git a/config/vufind/config.ini b/config/vufind/config.ini index 60ded5458f2a7328761a9f86209c5890f355b10b..24f8463989e855921c10d47e00a750c517ef9b8c 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -321,12 +321,12 @@ ils_encryption_key = false ; this). ;minimum_password_length = 4 ;maximum_password_length = 32 -; Specify default limit of accepted characters in the password. Allowed values +; Specify default limit of accepted characters in the password. Allowed values ; are "numeric", "alphanumeric" or a regular expression ;password_pattern = "(?=.*\d)(?=.*[a-z])(?=.*[A-Z])" -; Specify default hint about what the password may contain when using a regexp -; pattern. May be text or a translation key. The "numeric" and "alphanumeric" -; patterns have translated default hints. +; Specify default hint about what the password may contain when using a regexp +; pattern. May be text or a translation key. The "numeric" and "alphanumeric" +; patterns have translated default hints. ;password_hint = "Include both upper and lowercase letters and at least one number." ; Uncomment this line to switch on "privacy mode" in which no user information @@ -1208,6 +1208,12 @@ preferred_service = "loan" ; preferred_service settings to be ignored. show_full_status = false +; You can set this to the name of an alphabetic browse handler (see the +; [AlphaBrowse_Types] section) in order to link call numbers displayed on the +; holdings tab and in status messages to a specific browse list. Set to false +; to disable call number linking. +callnumber_handler = false + ; This section controls the behavior of the Record module. [Record] ; Set this to true in order to enable "next" and "previous" links to navigate diff --git a/module/VuFind/src/VuFind/Controller/AbstractRecord.php b/module/VuFind/src/VuFind/Controller/AbstractRecord.php index 78d2949eeb1ce4b9a3e797b73a483105ce116fbc..dcc7a533362599d5dc3d082a9af6b7308ff2aea3 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractRecord.php +++ b/module/VuFind/src/VuFind/Controller/AbstractRecord.php @@ -707,6 +707,10 @@ class AbstractRecord extends AbstractBase $view->scrollData = $this->resultScroller()->getScrollData($driver); } + $view->callnumberHandler = isset($config->Item_Status->callnumber_handler) + ? $config->Item_Status->callnumber_handler + : false; + $view->setTemplate($ajax ? 'record/ajaxtab' : 'record/view'); return $view; } diff --git a/module/VuFind/src/VuFind/Controller/AjaxController.php b/module/VuFind/src/VuFind/Controller/AjaxController.php index bde75701ac8104aa915f01e90ade21f13659ea97..def4c3902a10b85376e1328fd70815e5ee6085e9 100644 --- a/module/VuFind/src/VuFind/Controller/AjaxController.php +++ b/module/VuFind/src/VuFind/Controller/AjaxController.php @@ -224,7 +224,10 @@ class AjaxController extends AbstractBase // If a full status display has been requested, append the HTML: if ($showFullStatus) { $current['full_status'] = $renderer->render( - 'ajax/status-full.phtml', ['statusItems' => $record] + 'ajax/status-full.phtml', [ + 'statusItems' => $record, + 'callnumberHandler' => $this->getCallnumberHandler() + ] ); } $current['record_number'] = array_search($current['id'], $ids); @@ -295,7 +298,7 @@ class AjaxController extends AbstractBase $list = $transList; } // All values mode? Return comma-separated values: - return implode(', ', $list); + return implode(",\t", $list); } else { // Message mode? Return the specified message, translated to the // appropriate language. @@ -303,6 +306,26 @@ class AjaxController extends AbstractBase } } + /** + * Based on settings and the number of callnumbers, return callnumber handler + * Use callnumbers before pickValue is run. + * + * @param array $list Array of callnumbers. + * @param string $displaySetting config.ini setting -- first, all or msg + * + * @return string + */ + protected function getCallnumberHandler($list = null, $displaySetting = null) + { + if ($displaySetting == 'msg' && count($list) > 1) { + return false; + } + $config = $this->getConfig(); + return isset($config->Item_Status->callnumber_handler) + ? $config->Item_Status->callnumber_handler + : false; + } + /** * Reduce an array of service names to a human-readable string. * @@ -376,6 +399,10 @@ class AjaxController extends AbstractBase } } + $callnumberHandler = $this->getCallnumberHandler( + $callNumbers, $callnumberSetting + ); + // Determine call number string based on findings: $callNumber = $this->pickValue( $callNumbers, $callnumberSetting, 'Multiple Call Numbers' @@ -406,7 +433,8 @@ class AjaxController extends AbstractBase 'reserve_message' => $record[0]['reserve'] == 'Y' ? $this->translate('on_reserve') : $this->translate('Not On Reserve'), - 'callnumber' => htmlentities($callNumber, ENT_COMPAT, 'UTF-8') + 'callnumber' => htmlentities($callNumber, ENT_COMPAT, 'UTF-8'), + 'callnumber_handler' => $callnumberHandler ]; } @@ -449,6 +477,9 @@ class AjaxController extends AbstractBase foreach ($locations as $location => $details) { $locationCallnumbers = array_unique($details['callnumbers']); // Determine call number string based on findings: + $callnumberHandler = $this->getCallnumberHandler( + $locationCallnumbers, $callnumberSetting + ); $locationCallnumbers = $this->pickValue( $locationCallnumbers, $callnumberSetting, 'Multiple Call Numbers' ); @@ -462,7 +493,8 @@ class AjaxController extends AbstractBase 'callnumbers' => htmlentities($locationCallnumbers, ENT_COMPAT, 'UTF-8'), 'status_unknown' => isset($details['status_unknown']) - ? $details['status_unknown'] : false + ? $details['status_unknown'] : false, + 'callnumber_handler' => $callnumberHandler ]; $locationList[] = $locationInfo; } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php new file mode 100644 index 0000000000000000000000000000000000000000..eefd7248af69b9aeae68f95098824b6e2e487d34 --- /dev/null +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/CallnumberBrowseTest.php @@ -0,0 +1,276 @@ +<?php +/** + * Mink search actions test class. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2011. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +namespace VuFindTest\Mink; + +/** + * Mink search actions test class. + * + * @category VuFind + * @package Tests + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org Main Page + */ +class CallnumberBrowseTest extends \VuFindTest\Unit\MinkTestCase +{ + protected $id = 'testdeweybrowse'; + + /** + * Standard setup method. + * + * @return void + */ + public function setUp() + { + // Give up if we're not running in CI: + if (!$this->continuousIntegrationRunning()) { + return $this->markTestSkipped('Continuous integration not running.'); + } + } + + /** + * Search for the specified query. + * + * @param string $query Search term(s) + * + * @return \Behat\Mink\Element\Element + */ + protected function performSearch($query, $page = false) + { + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl() . '/Search/Home'); + $page = $session->getPage(); + $this->findCss($page, '.searchForm [name="lookfor"]')->setValue($query); + $this->findCss($page, '.btn.btn-primary')->click(); + $this->snooze(); + return $page; + } + + /** + * Set config for callnumber tests + * Sets callnumber_handler to false + * + * @param string $nos multiple_call_nos setting + * @param string $locs multiple_locations setting + * + * @return void + */ + protected function changeCallnumberSettings($nos, $locs, $full = false) + { + $this->changeConfigs( + [ + 'config' => [ + 'Catalog' => ['driver' => 'Sample'], + 'Item_Status' => [ + 'multiple_call_nos' => $nos, + 'multiple_locations' => $locs, + 'callnumber_handler' => false, + 'show_full_status' => $full + ] + ] + ] + ); + } + + /** + * Checks if a link has the correct callnumber and setting + * + * @param \Behat\Mink\Element\Element $link link element + * @param string $type dewey or lcc + * + * @return void + */ + protected function checkLink($link, $type) + { + $this->assertTrue(is_object($link)); + $href = $link->getAttribute('href'); + $this->assertContains($type, $href); + $this->assertNotEquals('', $link->getText()); + $this->assertContains($link->getText(), $href); + } + + protected function setupMultipleCallnumbers() + { + $this->changeConfigs([ + 'config' => [ + 'Catalog' => ['driver' => 'Demo'] + ], + 'Demo' => [ + 'Holdings' => [ + $this->id => json_encode([ + ['callnumber' => 'CallNumberOne', 'location' => 'Villanova'], + ['callnumber' => 'CallNumberTwo', 'location' => 'Villanova'], + ['callnumber' => 'CallNumberThree', 'location' => 'Phobos'], + ['callnumber' => 'CallNumberFour', 'location' => 'Phobos'] + ]) + ] + ] + ]); + } + + /** + * Sets callnumber_handler to true + * + * @param string $type dewey or lcc + * @param \Behat\Mink\Element\Element $page page element + * @param boolean $expectLinks links on multiple? + * + * @return void + */ + protected function activateAndTestLinks($type, $page, $expectLinks) + { + // Single callnumbers (Sample) + $this->changeConfigs([ + 'config' => [ + 'Catalog' => ['driver' => 'Sample'], + 'Item_Status' => ['callnumber_handler' => $type] + ] + ]); + $this->getMinkSession()->reload(); + $this->snooze(); + $link = $page->find('css', '.callnumber a,.groupCallnumber a,.fullCallnumber a'); + $this->checkLink($link, $type); + + // Multiple callnumbers + $this->setupMultipleCallnumbers(); + $this->getMinkSession()->reload(); + $this->snooze(); + $link = $page->find('css', '.callnumber a,.groupCallnumber a,.fullCallnumber a'); + if ($expectLinks) { + $this->checkLink($link, $type); + // TODO + // if 'all' + // - refresh until multiple + // - test multiple + // else + } else { + $this->assertTrue(is_null($link)); + } + } + + /** + * Sets callnumber_handler to true + * + * @param string $nos multiple_call_nos setting + * @param string $locs multiple_locations setting + * @param boolean $expectLinks whether or not links are expected for multiple callnumbers in this config + * + * @return void + */ + protected function validateSetting($nos = 'first', $locs = 'msg', $expectLinks = true, $full = false) + { + $this->changeCallnumberSettings($nos, $locs, $full); + $page = $this->performSearch('id:' . $this->id); + // No link + $link = $page->find('css', '.callnumber a,.groupCallnumber a,.fullCallnumber a'); + $this->assertTrue(is_null($link)); + // With dewey links + $this->activateAndTestLinks('dewey', $page, $expectLinks); + // With lcc links + $this->activateAndTestLinks('lcc', $page, $expectLinks); + } + + /** + * Test with multiple_call_nos set to first + * and multiple_locations set to msg + * + * @return void + */ + public function testFirstAndMsg() + { + $this->changeConfigs([ + 'config' => ['Item_Status' => ['show_full_status' => false]] + ]); + $this->validateSetting('first'); + } + + /** + * Test with multiple_call_nos set to first + * and multiple_locations set to msg + * + * @return void + */ + public function testAllAndMsg() + { + $this->validateSetting('all'); + } + + /** + * Test with multiple_call_nos set to first + * and multiple_locations set to msg + * + * @return void + */ + public function testMsgAndMsg() + { + $this->validateSetting('msg', 'msg', false); + } + + /** + * Test with multiple_call_nos set to first + * and multiple_locations set to group + * + * @return void + */ + public function testFirstAndGroup() + { + $this->validateSetting('first', 'group'); + } + + /** + * Test with multiple_call_nos set to all + * and multiple_locations set to msg + * + * @return void + */ + public function testAllAndGroup() + { + $this->validateSetting('all', 'group'); + } + + /** + * Test with multiple_call_nos set to msg + * and multiple_locations set to group + * + * @return void + */ + public function testMsgAndGroup() + { + $this->validateSetting('msg', 'group', false); + } + + /** + * Test with show_full_status set to true + * + * @return void + */ + public function testStatusFull() + { + $this->validateSetting('first', 'msg', true, true); + } +} diff --git a/themes/bootstrap3/js/check_item_statuses.js b/themes/bootstrap3/js/check_item_statuses.js index 985427d4e5a39c9b40a14bb7cbe8b5a935ae4156..48be5222529e5ff50ec73d20fd7c592a3abb9deb 100644 --- a/themes/bootstrap3/js/check_item_statuses.js +++ b/themes/bootstrap3/js/check_item_statuses.js @@ -1,4 +1,14 @@ /*global VuFind */ +function linkCallnumbers(callnumber, callnumber_handler) { + if (callnumber_handler) { + var cns = callnumber.split(',\t'); + for (var i = 0; i < cns.length; i++) { + cns[i] = '<a href="' + VuFind.path + '/Alphabrowse/Home?source=' + encodeURI(callnumber_handler) + '&from=' + encodeURI(cns[i]) + '">' + cns[i] + '</a>'; + } + return cns.join(',\t'); + } + return callnumber; +} function checkItemStatuses(_container) { var container = _container || $('body'); @@ -75,18 +85,18 @@ function checkItemStatuses(_container) { locationListHTML += '</div>'; locationListHTML += '<div class="groupCallnumber">'; locationListHTML += (result.locationList[x].callnumbers) - ? result.locationList[x].callnumbers : ''; + ? linkCallnumbers(result.locationList[x].callnumbers, result.locationList[x].callnumber_handler) : ''; locationListHTML += '</div>'; } item.find('.locationDetails').removeClass('hidden'); item.find('.locationDetails').empty().append(locationListHTML); } else { // Default case -- load call number and location into appropriate containers: - item.find('.callnumber').empty().append(result.callnumber + '<br/>'); + item.find('.callnumber').empty().append(linkCallnumbers(result.callnumber, result.callnumber_handler) + '<br/>'); item.find('.location').empty().append( result.reserve === 'true' - ? result.reserve_message - : result.location + ? result.reserve_message + : result.location ); } }); diff --git a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml index e8fc341cbe7b3720328e92bda515df2c879ebe8a..0501484acce98ca713cb4dca1fd86d5b313c7430 100644 --- a/themes/bootstrap3/templates/RecordTab/holdingsils.phtml +++ b/themes/bootstrap3/templates/RecordTab/holdingsils.phtml @@ -68,7 +68,12 @@ <th><?=$this->transEsc("Call Number")?>: </th> <td width="50%"> <? foreach ($callNos as $callNo): ?> - <?=$this->escapeHtml($callNo)?><br /> + <? if ($this->callnumberHandler): ?> + <a href="<?=$this->url('alphabrowse-home') ?>?source=<?=$this->escapeHtmlAttr($this->callnumberHandler) ?>&from=<?=$this->escapeHtmlAttr($callNo) ?>"><?=$this->escapeHtml($callNo)?></a> + <? else: ?> + <?=$this->escapeHtml($callNo)?> + <? endif; ?> + <br /> <? endforeach; ?> </td> </tr> diff --git a/themes/bootstrap3/templates/ajax/status-full.phtml b/themes/bootstrap3/templates/ajax/status-full.phtml index 992a216479e43f17b9ba980706dcab09304eeb10..a04420f3b63f42ea6a9a6737b5433a825845fadb 100644 --- a/themes/bootstrap3/templates/ajax/status-full.phtml +++ b/themes/bootstrap3/templates/ajax/status-full.phtml @@ -7,7 +7,7 @@ <? $i = 0; foreach ($this->statusItems as $item): ?> <? if (++$i == 5) break; // Show no more than 5 items ?> <tr> - <td> + <td class="fullLocation"> <? $locationText = $this->transEsc('location_' . $item['location'], array(), $item['location']); ?> <? if (isset($item['locationhref']) && $item['locationhref']): ?> <a href="<?=$item['locationhref']?>" target="_blank"><?=$locationText?></a> @@ -15,8 +15,14 @@ <?=$locationText?> <? endif; ?> </td> - <td><?=$this->escapeHtml($item['callnumber'])?></td> - <td> + <td class="fullCallnumber"> + <? if ($this->callnumberHandler): ?> + <a href="<?=$this->url('alphabrowse-home') ?>?source=<?=$this->escapeHtmlAttr($this->callnumberHandler) ?>&from=<?=$this->escapeHtmlAttr($item['callnumber']) ?>"><?=$this->escapeHtml($item['callnumber'])?></a> + <? else: ?> + <?=$this->escapeHtml($item['callnumber'])?> + <? endif; ?> + </td> + <td class="fullAvailability"> <? if (isset($item['use_unknown_message']) && $item['use_unknown_message']): ?> <span><?=$this->transEsc("status_unknown_message")?></span> <? elseif ($item['availability']): ?> diff --git a/themes/jquerymobile/js/check_item_statuses.js b/themes/jquerymobile/js/check_item_statuses.js index 61cbf563ec354d4b33851ff96ff49f4ede90bf73..158e94419bbbeae6a61b82a6a7330fedc5757cc9 100644 --- a/themes/jquerymobile/js/check_item_statuses.js +++ b/themes/jquerymobile/js/check_item_statuses.js @@ -1,4 +1,14 @@ /*global path*/ +function linkCallnumbers(callnumber, callnumber_handler) { + if (callnumber_handler) { + var cns = callnumber.split(',\t'); + for (var i = 0; i < cns.length; i++) { + cns[i] = '<a href="' + path + '/Alphabrowse/Home?source=' + encodeURI(callnumber_handler) + '&from=' + encodeURI(cns[i]) + '">' + cns[i] + '</a>'; + } + return cns.join(',\t'); + } + return callnumber; +} function checkItemStatuses() { var id = $.map($('.ajaxItemId'), function(i) { @@ -27,7 +37,7 @@ function checkItemStatuses() { item.find('.status').hide(); } else { // Default case -- load call number and location into appropriate containers: - item.find('.callnumber').empty().append(result.callnumber); + item.find('.callnumber').empty().append(linkCallnumbers(result.callnumber, result.callnumber_handler)); item.find('.location').empty().append( result.reserve == 'true' ? result.reserve_message diff --git a/themes/jquerymobile/templates/RecordTab/holdingsils.phtml b/themes/jquerymobile/templates/RecordTab/holdingsils.phtml index e9b2f5aa640d22704fec72f049a7942357775a45..3da187abb51a5ea1de8ecc724c674fd13056b832 100644 --- a/themes/jquerymobile/templates/RecordTab/holdingsils.phtml +++ b/themes/jquerymobile/templates/RecordTab/holdingsils.phtml @@ -56,7 +56,12 @@ <th><?=$this->transEsc("Call Number")?>: </th> <td> <? foreach ($callNos as $callNo): ?> - <?=$this->escapeHtml($callNo)?><br /> + <? if ($this->callnumberHandler): ?> + <a href="<?=$this->url('alphabrowse-home') ?>?source=<?=$this->escapeHtmlAttr($this->callnumberHandler) ?>&from=<?=$this->escapeHtmlAttr($callNo) ?>"><?=$this->escapeHtml($callNo)?></a> + <? else: ?> + <?=$this->escapeHtml($callNo)?> + <? endif; ?> + <br /> <? endforeach; ?> </td> </tr>