diff --git a/module/VuFind/tests/fixtures/configs/1.1/config.ini b/module/VuFind/tests/fixtures/configs/1.1/config.ini index 9be711f6feb9e87af8aae16502139cfa099d1374..32fa094f337f58e877d00956b44916af71ec0b7e 100644 --- a/module/VuFind/tests/fixtures/configs/1.1/config.ini +++ b/module/VuFind/tests/fixtures/configs/1.1/config.ini @@ -227,7 +227,7 @@ url = "http://syndetics.com" ; "pw". Note that Content Cafe is a subscription service from Baker & Taylor. [Contentcafe] url = "http://contentcafe2.btol.com" -pw = "Password" +pw = "xxxxxx" ; Web Search is Optional. The Web Search is powered by Google. ; To use enter your Google Web Search key and the domain the of your library @@ -249,7 +249,7 @@ apiKey = mySecretKey ; and the WorldCat searching. ;[WorldCat] ;id = myAccount -;apiKey = ApiKey +;apiKey = YBaFHvqtTHbx7zzgtgAQDr4Rij9OSXBMmfsO1VRXxarmH1JU6neu2w3Lu6Y8OAjN2z5Pkz0M7GQwkwAF ;OCLCCode = MYCODE ;LimitCodes = Comma separated list of OCLC Codes diff --git a/module/VuFind/tests/fixtures/configs/1.2/config.ini b/module/VuFind/tests/fixtures/configs/1.2/config.ini index ddd8dd319978d4034ac3b8038196a2c460f85166..31d2ef0557757a78d56ab73e50c4b5b2c4030d65 100644 --- a/module/VuFind/tests/fixtures/configs/1.2/config.ini +++ b/module/VuFind/tests/fixtures/configs/1.2/config.ini @@ -261,7 +261,7 @@ url = "http://syndetics.com" ; "pw". Note that Content Cafe is a subscription service from Baker & Taylor. [Contentcafe] url = "http://contentcafe2.btol.com" -pw = "Password" +pw = "xxxxxx" ; Web Search is Optional. The Web Search is powered by Google. ; To use enter your Google Web Search key and the domain the of your library @@ -283,7 +283,7 @@ apiKey = mySecretKey ; and the WorldCat searching. ;[WorldCat] ;id = myAccount -;apiKey = ApiKey +;apiKey = YBaFHvqtTHbx7zzgtgAQDr4Rij9OSXBMmfsO1VRXxarmH1JU6neu2w3Lu6Y8OAjN2z5Pkz0M7GQwkwAF ;OCLCCode = MYCODE ;LimitCodes = Comma separated list of OCLC Codes diff --git a/module/VuFind/tests/fixtures/configs/1.3/config.ini b/module/VuFind/tests/fixtures/configs/1.3/config.ini index dca7fc490d91aa736d54a72d70f925092c9652f0..d92486e6b990850d65f1f5e908baa1ca7c7fe777 100644 --- a/module/VuFind/tests/fixtures/configs/1.3/config.ini +++ b/module/VuFind/tests/fixtures/configs/1.3/config.ini @@ -323,7 +323,7 @@ url = "http://syndetics.com" ; "pw". Note that Content Cafe is a subscription service from Baker & Taylor. [Contentcafe] url = "http://contentcafe2.btol.com" -pw = "Password" +pw = "xxxxxx" ; Web Search is Optional. The Web Search is powered by Google. ; To use enter your Google Web Search key and the domain the of your library @@ -345,7 +345,7 @@ apiKey = mySecretKey ; and the WorldCat searching. ;[WorldCat] ;id = myAccount -;apiKey = ApiKey +;apiKey = YBaFHvqtTHbx7zzgtgAQDr4Rij9OSXBMmfsO1VRXxarmH1JU6neu2w3Lu6Y8OAjN2z5Pkz0M7GQwkwAF ;OCLCCode = MYCODE ;LimitCodes = Comma separated list of OCLC Codes diff --git a/module/VuFind/tests/fixtures/configs/1.4/config.ini b/module/VuFind/tests/fixtures/configs/1.4/config.ini index b05c8ab206d268758e3d64b1ff7f91084bf70830..13417f3fae7b9a813136bd5451cdec18e171d2ef 100644 --- a/module/VuFind/tests/fixtures/configs/1.4/config.ini +++ b/module/VuFind/tests/fixtures/configs/1.4/config.ini @@ -399,7 +399,7 @@ url = "http://syndetics.com" ; "pw". Note that Content Cafe is a subscription service from Baker & Taylor. [Contentcafe] url = "http://contentcafe2.btol.com" -pw = "Password" +pw = "xxxxxx" ; Web Search is Optional. The Web Search is powered by Google. ; To use enter your Google Web Search key and the domain the of your library @@ -421,7 +421,7 @@ apiKey = mySecretKey ; and the WorldCat searching. ;[WorldCat] ;id = myAccount -;apiKey = ApiKey +;apiKey = YBaFHvqtTHbx7zzgtgAQDr4Rij9OSXBMmfsO1VRXxarmH1JU6neu2w3Lu6Y8OAjN2z5Pkz0M7GQwkwAF ;OCLCCode = MYCODE ;LimitCodes = Comma separated list of OCLC Codes diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php index 7eb106e97af4eef4ecb46da0467777fba498e959..23a1066abf4b710f6afad3f14581d8ad21b920ba 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/NextPrevNavTest.php @@ -35,9 +35,12 @@ namespace VuFindTest\Mink; * @author Conor Sheehan <csheehan@nli.ie> * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org Main Page + * @retry 4 */ class NextPrevNavTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + /** * if next_prev_navigation and first_last_navigation are set to true * and a search which returns no results is run @@ -62,6 +65,6 @@ class NextPrevNavTest extends \VuFindTest\Unit\MinkTestCase $session->visit($this->getVuFindUrl() . "/Record/geo20001"); // should fail if exception is thrown - $this->assertContains("Test Publication 20001", $this->findCss($page, "div.media-body > h1[property=name]")->getText()); + $this->assertStringContainsString("Test Publication 20001", $this->findCss($page, "div.media-body > h1[property=name]")->getText()); } } diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php index c5a3baa410266c5c11a55a6a3d1f220d31ae92e8..138dc190411b1587b2bc3cf2d9bd4934f1e0e983 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/SearchFacetsTest.php @@ -35,19 +35,30 @@ namespace VuFindTest\Mink; * @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 + * @retry 4 */ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase { + use \VuFindTest\Unit\AutoRetryTrait; + + /** + * CSS selector for finding active filters + * + * @var string + */ + protected $activeFilterSelector = '.active-filters.hidden-xs .filters .filter-value'; + /** * Standard setup method. * * @return void */ - public function setUp() + public function setUp(): void { // Give up if we're not running in CI: if (!$this->continuousIntegrationRunning()) { - return $this->markTestSkipped('Continuous integration not running.'); + $this->markTestSkipped('Continuous integration not running.'); + return; } } @@ -63,6 +74,36 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase return $session->getPage(); } + /** + * Helper function for simple facet application test + * + * @param \Behat\Mink\Element\Element $page Mink page object + * + * @return void + */ + protected function facetApplyProcedure($page) + { + // Confirm that we have 9 results and no filters to begin with: + $time = $this->findCss($page, '.search-query-time'); + $stats = $this->findCss($page, '.search-stats'); + $this->assertEquals("Showing 1 - 9 results of 9 for search 'building:weird_ids.mrc'" . $time->getText(), $stats->getText()); + $items = $page->findAll('css', $this->activeFilterSelector); + $this->assertEquals(0, count($items)); + + // Facet to Fiction (after making sure we picked the right link): + $facetList = $this->findCss($page, '#side-collapse-genre_facet a[data-title="Fiction"]'); + $this->assertEquals('Fiction 7', $facetList->getText()); + $facetList->click(); + $this->snooze(); + + // Check that when the page reloads, we have fewer results and a filter: + $time = $this->findCss($page, '.search-query-time'); + $stats = $this->findCss($page, '.search-stats'); + $this->assertEquals("Showing 1 - 7 results of 7 for search 'building:weird_ids.mrc'" . $time->getText(), $stats->getText()); + $items = $page->findAll('css', $this->activeFilterSelector); + $this->assertEquals(1, count($items)); + } + /** * Helper function for facets lists * @@ -81,7 +122,7 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase ->findAll('css', '#modal #facet-list-count .fa-times'); $this->assertEquals($exclusionActive ? $limit : 0, count($excludes)); // more - $this->findCss($page, '#modal .js-facet-next-page')->click(); + $this->clickCss($page, '#modal .js-facet-next-page'); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-count .js-facet-item'); $this->assertEquals($limit * 2, count($items)); @@ -103,7 +144,7 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $this->assertEquals($exclusionActive ? $limit * 2 : 0, count($excludes)); // sort by title - $this->findCss($page, '[data-sort="index"]')->click(); + $this->clickCss($page, '[data-sort="index"]'); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-index .js-facet-item'); $this->assertEquals($limit, count($items)); // reset number of items @@ -119,7 +160,7 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase ->findAll('css', '#modal #facet-list-index .fa-times'); $this->assertEquals($exclusionActive ? $limit : 0, count($excludes)); // sort by index again - $this->findCss($page, '[data-sort="count"]')->click(); + $this->clickCss($page, '[data-sort="count"]'); $this->snooze(); $items = $page->findAll('css', '#modal #facet-list-count .js-facet-item'); $this->assertEquals($limit * 2, count($items)); // maintain number of items @@ -134,6 +175,49 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $this->snooze(); } + /** + * Test applying a facet to filter results (standard facet sidebar) + * + * @return void + */ + public function testApplyFacet() + { + $page = $this->performSearch('building:weird_ids.mrc'); + + // Confirm that we are NOT using the AJAX sidebar: + $ajaxContainer = $page->findAll('css', '.side-facets-container-ajax'); + $this->assertEquals(0, count($ajaxContainer)); + + // Now run the body of the test procedure: + $this->facetApplyProcedure($page); + } + + /** + * Test applying a facet to filter results (deferred facet sidebar) + * + * @return void + */ + public function testApplyFacetDeferred() + { + $this->changeConfigs( + [ + 'searches' => [ + 'General' => [ + 'default_side_recommend[]' => 'SideFacetsDeferred:Results:CheckboxFacets', + ] + ] + ] + ); + $page = $this->performSearch('building:weird_ids.mrc'); + + // Confirm that we ARE using the AJAX sidebar: + $ajaxContainer = $page->findAll('css', '.side-facets-container-ajax'); + $this->assertEquals(1, count($ajaxContainer)); + + // Now run the body of the test procedure: + $this->facetApplyProcedure($page); + } + /** * Test expanding facets into the lightbox * @@ -158,10 +242,10 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $genreMore->click(); $this->facetListProcedure($page, $limit); $genreMore->click(); - $this->findCss($page, '#modal .js-facet-item.active')->click(); + $this->clickCss($page, '#modal .js-facet-item.active'); // remove facet $this->snooze(); - $this->assertNull($page->find('css', '.active-filters')); + $this->assertNull($page->find('css', $this->activeFilterSelector)); } /** @@ -186,14 +270,14 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase // Open the geographic facet $genreMore = $this->findCss($page, '#more-narrowGroupHidden-genre_facet'); $genreMore->click(); - $this->findCss($page, '.narrowGroupHidden-genre_facet[data-lightbox]')->click(); + $this->clickCss($page, '.narrowGroupHidden-genre_facet[data-lightbox]'); $this->facetListProcedure($page, $limit); $genreMore->click(); - $this->findCss($page, '.narrowGroupHidden-genre_facet[data-lightbox]')->click(); - $this->findCss($page, '#modal .js-facet-item.active')->click(); + $this->clickCss($page, '.narrowGroupHidden-genre_facet[data-lightbox]'); + $this->clickCss($page, '#modal .js-facet-item.active'); // remove facet $this->snooze(); - $this->assertNull($page->find('css', '.active-filters')); + $this->assertNull($page->find('css', $this->activeFilterSelector)); } /** @@ -236,7 +320,7 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase $session = $this->getMinkSession(); $session->executeScript("$('#j1_1.jstree-closed .jstree-icon').click();"); $this->findCss($page, '#j1_1.jstree-open .jstree-icon'); - $this->findCss($page, '#j1_2 a')->click(); + $this->clickCss($page, '#j1_2 a'); $this->snooze(); $filter = $this->findCss($page, $this->activeFilterSelector); $label = $this->findCss($page, '.filters .filters-title'); @@ -292,43 +376,165 @@ class SearchFacetsTest extends \VuFindTest\Unit\MinkTestCase ); $page = $this->performSearch('building:"hierarchy.mrc"'); // Uncollapse format so we can check if it is still open after reload: - $this->findCss($page, '#side-panel-format .collapsed')->click(); + $this->clickCss($page, '#side-panel-format .collapsed'); // Uncollapse hierarchical facet so we can click it: - $this->findCss($page, '#side-panel-hierarchical_facet_str_mv .collapsed')->click(); + $this->clickCss($page, '#side-panel-hierarchical_facet_str_mv .collapsed'); $this->clickHierarchyFacet($page); // We have now reloaded the page. Let's toggle format off and on to confirm // that it was opened, and let's also toggle building on to confirm that // it was not alread opened. - $this->findCss($page, '#side-panel-format .title')->click(); // off + $this->clickCss($page, '#side-panel-format .title'); // off $this->snooze(); // wait for animation - $this->findCss($page, '#side-panel-format .collapsed')->click(); // on - $this->findCss($page, '#side-panel-building .collapsed')->click(); // on + $this->clickCss($page, '#side-panel-format .collapsed'); // on + $this->clickCss($page, '#side-panel-building .collapsed'); // on + } + + /** + * Assert that the filter used by these tests is still applied. + * + * @param \Behat\Mink\Element\Element $page Mink page object + * + * @return void + */ + protected function assertFilterIsStillThere($page) + { + $filter = $this->findCss($page, $this->activeFilterSelector); + $this->assertEquals('weird_ids.mrc', $filter->getText()); } /** - * Test retrain current filters checkbox + * Assert that no filters are applied. + * + * @param \Behat\Mink\Element\Element $page Mink page object * * @return void */ - public function testRetainFilters() + protected function assertNoFilters($page) + { + $items = $page->findAll('css', $this->activeFilterSelector); + $this->assertEquals(0, count($items)); + } + + /** + * Assert that the "reset filters" button is not present. + * + * @param \Behat\Mink\Element\Element $page Mink page object + * + * @return void + */ + protected function assertNoResetFiltersButton($page) + { + $reset = $page->findAll('css', '.reset-filters-btn'); + $this->assertEquals(0, count($reset)); + } + + /** + * Test retain current filters default behavior + * + * @return void + */ + public function testDefaultRetainFiltersBehavior() { $page = $this->getFilteredSearch(); - $this->findCss($page, '.active-filters'); // Make sure we're filtered - // Perform search with retain - $this->findCss($page, '#searchForm .btn.btn-primary')->click(); + $this->assertFilterIsStillThere($page); + // Re-click the search button and confirm that filters are still there + $this->clickCss($page, '#searchForm .btn.btn-primary'); $this->snooze(); - $this->findCss($page, '.active-filters'); - // Perform search double click retain - $this->findCss($page, '.searchFormKeepFilters')->click(); - $this->findCss($page, '.searchFormKeepFilters')->click(); - $this->findCss($page, '#searchForm .btn.btn-primary')->click(); + $this->assertFilterIsStillThere($page); + // Click the "reset filters" button and confirm that filters are gone and + // that the button disappears when no longer needed. + $this->clickCss($page, '.reset-filters-btn'); $this->snooze(); - $this->findCss($page, '.active-filters'); - // Perform search without retain - $this->findCss($page, '.searchFormKeepFilters')->click(); - $this->findCss($page, '#searchForm .btn.btn-primary')->click(); - $items = $page->findAll('css', '.active-filters'); - $this->assertEquals(0, count($items)); + $this->assertNoFilters($page); + $this->assertNoResetFiltersButton($page); + } + + /** + * Test that filters carry over to selected records and are retained + * from there. + * + * @return void + */ + public function testFiltersOnRecord() + { + $page = $this->getFilteredSearch(); + $this->assertFilterIsStillThere($page); + // Now click the first result: + $this->clickCss($page, '.result-body a.title'); + $this->snooze(); + // Confirm that filters are still visible: + $this->assertFilterIsStillThere($page); + // Re-click the search button... + $this->clickCss($page, '#searchForm .btn.btn-primary'); + $this->snooze(); + // Confirm that filter is STILL applied + $this->assertFilterIsStillThere($page); + } + + /** + * Test "never retain filters" configurable behavior + * + * @return void + */ + public function testNeverRetainFiltersBehavior() + { + $this->changeConfigs( + [ + 'searches' => [ + 'General' => ['retain_filters_by_default' => false] + ] + ] + ); + $page = $this->getFilteredSearch(); + $this->assertFilterIsStillThere($page); + // Confirm that there is no reset button: + $this->assertNoResetFiltersButton($page); + // Re-click the search button and confirm that filters go away + $this->clickCss($page, '#searchForm .btn.btn-primary'); + $this->snooze(); + $this->assertNoFilters($page); + } + + /** + * Test resetting to a default filter state + * + * @return void + */ + public function testDefaultFiltersWithResetButton() + { + // Unlike the other tests, which use $this->getFilteredSearch() to set up + // the weird_ids.mrc filter through a URL parameter, this test sets up the + // filter as a default through the configuration. + $this->changeConfigs( + [ + 'searches' => [ + 'General' => ['default_filters' => ['building:weird_ids.mrc']] + ] + ] + ); + + // Do a blank search to confirm that default filter is applied: + $session = $this->getMinkSession(); + $session->visit($this->getVuFindUrl() . '/Search/Results'); + $page = $session->getPage(); + $this->snooze(); + $this->assertFilterIsStillThere($page); + + // Confirm that the reset button is NOT present: + $this->assertNoResetFiltersButton($page); + + // Now manually clear the filter: + $this->clickCss($page, '.search-filter-remove'); + $this->snooze(); + + // Confirm that no filters are displayed: + $this->assertNoFilters($page); + + // Now click the reset button to bring back the default: + $this->clickCss($page, '.reset-filters-btn'); + $this->snooze(); + $this->assertFilterIsStillThere($page); + $this->assertNoResetFiltersButton($page); } } diff --git a/themes/bootstrap3/js/common.js b/themes/bootstrap3/js/common.js index 58716399f9f6e6d0d4bf438515899bd9eb7c3eed..085a3f1be3c3ad1edf6ddec5a1cb294f2abfba7c 100644 --- a/themes/bootstrap3/js/common.js +++ b/themes/bootstrap3/js/common.js @@ -1,5 +1,5 @@ /*global grecaptcha, isPhoneNumberValid */ -/*exported VuFind, htmlEncode, deparam, getUrlRoot, phoneNumberFormHandler, recaptchaOnLoad, resetCaptcha, bulkFormHandler */ +/*exported VuFind, htmlEncode, deparam, getUrlRoot, phoneNumberFormHandler, recaptchaOnLoad, resetCaptcha, bulkFormHandler, setupMultiILSLoginFields */ // IE 9< console polyfill window.console = window.console || { log: function polyfillLog() {} }; @@ -152,13 +152,16 @@ function getUrlRoot(url) { var urlroot = null; var urlParts = url.split(/[?#]/); var urlWithoutFragment = urlParts[0]; - if (VuFind.path === '') { + var slashSlash = urlWithoutFragment.indexOf('//'); + if (VuFind.path === '' || VuFind.path === '/') { // special case -- VuFind installed at site root: var chunks = urlWithoutFragment.split('/'); - urlroot = '/' + chunks[3] + '/' + chunks[4]; + // We need to extract different offsets if this is a full vs. relative URL: + urlroot = slashSlash > -1 + ? ('/' + chunks[3] + '/' + chunks[4]) + : ('/' + chunks[1] + '/' + chunks[2]); } else { // standard case -- VuFind has its own path under site: - var slashSlash = urlWithoutFragment.indexOf('//'); var pathInUrl = slashSlash > -1 ? urlWithoutFragment.indexOf(VuFind.path, slashSlash + 2) : urlWithoutFragment.indexOf(VuFind.path); @@ -437,15 +440,5 @@ $(document).ready(function commonDocReady() { $.getJSON(VuFind.path + '/AJAX/JSON', {method: 'keepAlive'}); } - // retain filter sessionStorage - $('.searchFormKeepFilters').click(function retainFiltersInSessionStorage() { - sessionStorage.setItem('vufind_retain_filters', this.checked ? 'true' : 'false'); - $('.applied-filter').prop('checked', this.checked); - }); - if (sessionStorage.getItem('vufind_retain_filters')) { - var state = (sessionStorage.getItem('vufind_retain_filters') === 'true'); - $('.searchFormKeepFilters,.applied-filter').prop('checked', state); - } - setupIeSupport(); }); diff --git a/themes/bootstrap3/js/lightbox.js b/themes/bootstrap3/js/lightbox.js index bd79c2731f62f11e95479dc9668228a43904f45a..4bfed9b0d69026e6c456d029e8968c25b45b55f8 100644 --- a/themes/bootstrap3/js/lightbox.js +++ b/themes/bootstrap3/js/lightbox.js @@ -142,7 +142,11 @@ VuFind.register('lightbox', function Lightbox() { .done(function lbAjaxDone(content, status, jq_xhr) { var errorMsgs = []; var flashMessages = []; - if (jq_xhr.status !== 205) { + if (jq_xhr.status === 204) { + // No content, close lightbox + close(); + return; + } else if (jq_xhr.status !== 205) { var testDiv = $('<div/>').html(content); errorMsgs = testDiv.find('.flash-message.alert-danger:not([data-lightbox-ignore])'); flashMessages = testDiv.find('.flash-message:not([data-lightbox-ignore])'); diff --git a/themes/bootstrap3/js/record.js b/themes/bootstrap3/js/record.js index edfc6e8670df72204d07249365ccdbc654b72fe5..4d4630339c00937940f546061aace4a5d2e7789c 100644 --- a/themes/bootstrap3/js/record.js +++ b/themes/bootstrap3/js/record.js @@ -128,6 +128,28 @@ function registerAjaxCommentRecord(_context) { return false; } +// Forward declaration +var ajaxLoadTab = function ajaxLoadTabForward() { +}; + +function handleAjaxTabLinks(_context) { + var context = typeof _context === "undefined" ? document : _context; + // Form submission + $(context).find('a').each(function handleLink() { + var $a = $(this); + var href = $a.attr('href'); + if (typeof href !== 'undefined' && href.match(/\/AjaxTab[/?]/)) { + $a.unbind('click').click(function linkClick() { + var tabid = $('.record-tabs .nav-tabs li.active').data('tab'); + var $tab = $('.' + tabid + '-tab'); + $tab.html('<i class="fa fa-spinner fa-spin" aria-hidden="true"></i> ' + VuFind.translate('loading') + '...</div>'); + ajaxLoadTab($tab, '', false, href); + return false; + }); + } + }); +} + function registerTabEvents() { // Logged in AJAX registerAjaxCommentRecord(); @@ -136,6 +158,8 @@ function registerTabEvents() { setUpCheckRequest(); + handleAjaxTabLinks(); + VuFind.lightbox.bind('.tab-pane.active'); if (typeof VuFind.openurl !== 'undefined') { @@ -152,12 +176,21 @@ function removeHashFromLocation() { } } -function ajaxLoadTab($newTab, tabid, setHash) { +ajaxLoadTab = function ajaxLoadTabReal($newTab, tabid, setHash, tabUrl) { // Request the tab via AJAX: + var url = ''; + var postData = {}; + // If tabUrl is defined, it overrides base URL and tabid + if (typeof tabUrl !== 'undefined') { + url = tabUrl; + } else { + url = VuFind.path + getUrlRoot(document.URL) + '/AjaxTab'; + postData.tab = tabid; + } $.ajax({ - url: VuFind.path + getUrlRoot(document.URL) + '/AjaxTab', + url: url, type: 'POST', - data: {tab: tabid} + data: postData }) .always(function ajaxLoadTabDone(data) { if (typeof data === 'object') { @@ -177,7 +210,7 @@ function ajaxLoadTab($newTab, tabid, setHash) { setupJumpMenus($newTab); }); return false; -} +}; function refreshTagList(_target, _loggedin) { var loggedin = !!_loggedin || userIsLoggedIn; diff --git a/themes/bootstrap3/less/components/accessibility.less b/themes/bootstrap3/less/components/accessibility.less index 2b5494ad786eac5d50895c9b1af53c1a0ab75cf7..3fc07c552777af6778962dde5079c43de9e2fe69 100644 --- a/themes/bootstrap3/less/components/accessibility.less +++ b/themes/bootstrap3/less/components/accessibility.less @@ -24,41 +24,3 @@ a { color: @state-danger-text; } } - -/** - * OVERRIDE BS3 COLLAPSE MENU HIDDEN - * - * instead of display: none, keep things sr accessible - * https://tailwindcss.com/docs/screen-readers/ - */ -/* -.collapse.collapse:not(.in) { - position: absolute; - display: block; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - color: #000; - background-color: #fff; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; -} -.long-view.collapse:not(.in) { - display: none; -} -*/ -@media (min-width: 768px) { - .navbar-collapse.collapse:not(.in) { - position: static; - width: auto; - height: auto; - padding: 0; - margin: 0; - overflow: visible; - clip: auto; - white-space: normal; - } -} diff --git a/themes/bootstrap3/less/components/record.less b/themes/bootstrap3/less/components/record.less index 2497a8ff2e491a3c5f6d073ffe2238abca80542d..e356fc1ad27e4b96844c5b39a0907cb571f1c28e 100644 --- a/themes/bootstrap3/less/components/record.less +++ b/themes/bootstrap3/less/components/record.less @@ -135,3 +135,41 @@ /* ------ Relais ------ */ .relaisLink { display: inline-block; } + +/* ------ Collection ------ */ +.collection-list-controls { + display: flex; + flex-flow: row wrap; + + .collection-control { + white-space: nowrap; + margin: 0 0.5rem 0 0; + } +} +.collectionDetails .active-filters .filters { + padding: 0 0 5px 0; +} +.collection-list-results { + margin-top: 0.5rem; +} + +/* ------ Tabs ------ */ +.tab-pane::after { + display: table; + clear: both; + content: ""; +} +.tab-pane .result { + margin-left: 0; +} + +/* ------ OpenURL Links ------ */ +.openurls { + .openurl-notes { + display: block; + font-style: italic; + } + .openurl-authentication { + display: block; + } +} diff --git a/themes/bootstrap3/scss/components/accessibility.scss b/themes/bootstrap3/scss/components/accessibility.scss index ace3cb6062763551b29c5eb7ff8cd50b70c9a328..a987269a49d2b6b67d5b32480ed54e4664283f8b 100644 --- a/themes/bootstrap3/scss/components/accessibility.scss +++ b/themes/bootstrap3/scss/components/accessibility.scss @@ -24,41 +24,3 @@ $state-danger-text: #8a211e; color: $state-danger-text; } } - -/** - * OVERRIDE BS3 COLLAPSE MENU HIDDEN - * - * instead of display: none, keep things sr accessible - * https://tailwindcss.com/docs/screen-readers/ - */ -/* -.collapse.collapse:not(.in) { - position: absolute; - display: block; - width: 1px; - height: 1px; - padding: 0; - margin: -1px; - color: #000; - background-color: #fff; - overflow: hidden; - clip: rect(0, 0, 0, 0); - white-space: nowrap; - border-width: 0; -} -.long-view.collapse:not(.in) { - display: none; -} -*/ -@media (min-width: 768px) { - .navbar-collapse.collapse:not(.in) { - position: static; - width: auto; - height: auto; - padding: 0; - margin: 0; - overflow: visible; - clip: auto; - white-space: normal; - } -} diff --git a/themes/bootstrap3/scss/components/offcanvas.scss b/themes/bootstrap3/scss/components/offcanvas.scss index 39097702eb53caf457f519827025c7c80f6f731c..943ce7bbd656b063ddb509eda1aa607e08c89825 100644 --- a/themes/bootstrap3/scss/components/offcanvas.scss +++ b/themes/bootstrap3/scss/components/offcanvas.scss @@ -2,9 +2,6 @@ $offcanvas-offset: 80vw; // Width of open menu .offcanvas-overlay { display: none; } -// Fix you-cannot-extend-from-inside-media-query error: -// Since .search-filter-toggle is only used on XS devices anyway, -// we can move the @extend outside the media query -- CK #14674 .search-filter-toggle { @extend .btn; @extend .btn-default; diff --git a/themes/bootstrap3/scss/components/record.scss b/themes/bootstrap3/scss/components/record.scss index 63b59030a9a6fda8925cb2160cbdc32ecdc175f3..0375ec79322b20402e9f9b388bb9e152f7abd04e 100644 --- a/themes/bootstrap3/scss/components/record.scss +++ b/themes/bootstrap3/scss/components/record.scss @@ -135,3 +135,41 @@ /* ------ Relais ------ */ .relaisLink { display: inline-block; } + +/* ------ Collection ------ */ +.collection-list-controls { + display: flex; + flex-flow: row wrap; + + .collection-control { + white-space: nowrap; + margin: 0 0.5rem 0 0; + } +} +.collectionDetails .active-filters .filters { + padding: 0 0 5px 0; +} +.collection-list-results { + margin-top: 0.5rem; +} + +/* ------ Tabs ------ */ +.tab-pane::after { + display: table; + clear: both; + content: ""; +} +.tab-pane .result { + margin-left: 0; +} + +/* ------ OpenURL Links ------ */ +.openurls { + .openurl-notes { + display: block; + font-style: italic; + } + .openurl-authentication { + display: block; + } +} diff --git a/themes/bootstrap3/scss/components/search.scss b/themes/bootstrap3/scss/components/search.scss index f5bf702e7258128dd5aa818b236930890028a445..f21d64795b079ceac1c908e05b082585552a35c0 100644 --- a/themes/bootstrap3/scss/components/search.scss +++ b/themes/bootstrap3/scss/components/search.scss @@ -500,8 +500,8 @@ body.rtl { } .search-history-table { - @extend .table; - @extend .table-striped; + @extend .table all; + @extend .table-striped all; } table.search-history-table { table-layout: auto; diff --git a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml index f03f1c355908841e4a81972d417cead92f2a7b93..a19c551c0e73307a4e0c76d3ae278bca7c5c154b 100644 --- a/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml +++ b/themes/bootstrap3/templates/RecordDriver/DefaultRecord/core.phtml @@ -1,9 +1,11 @@ +<?php $this->metadata()->generateMetatags($this->driver);?> <div class="media" vocab="http://schema.org/" resource="#record" typeof="<?=$this->driver->getSchemaOrgFormats()?> Product"> <?php $QRCode = $this->record($this->driver)->getQRCode("core"); $coverDetails = $this->record($this->driver)->getCoverDetails('core', 'medium', $this->record($this->driver)->getThumbnail('large')); $cover = $coverDetails['html']; - $preview = $this->record($this->driver)->getPreviews(); + $preview = ($this->previewOverride ?? false) + ? $this->previewOverride : $this->record($this->driver)->getPreviews(); ?> <?php if ($QRCode || $cover || $preview): ?> <div class="media-left <?=$this->escapeHtmlAttr($coverDetails['size'])?> img-col"> diff --git a/themes/bootstrap3/templates/footer.phtml b/themes/bootstrap3/templates/footer.phtml index 2604172f5de2cc093284cc3ec24ae11fbe5b9999..ce2a6e122b9fa5117ef5da24ac4425de21c47f43 100644 --- a/themes/bootstrap3/templates/footer.phtml +++ b/themes/bootstrap3/templates/footer.phtml @@ -1,29 +1,35 @@ <footer class="hidden-print"> <div class="footer-container"> <div class="footer-column"> - <h2><?=$this->transEsc('Search Options')?></h2> - <ul> - <li><a href="<?=$this->url('search-history')?>"><?=$this->transEsc('Search History')?></a></li> - <li><a href="<?=$this->url('search-advanced')?>"><?=$this->transEsc('Advanced Search')?></a></li> - </ul> + <?php $this->slot('footer-left')->start(); ?> + <h2><?=$this->transEsc('Search Options')?></h2> + <ul> + <li><a href="<?=$this->url('search-history')?>"><?=$this->transEsc('Search History')?></a></li> + <li><a href="<?=$this->url('search-advanced')?>"><?=$this->transEsc('Advanced Search')?></a></li> + </ul> + <?=$this->slot('footer-left')->end(); ?> </div> <div class="footer-column"> - <h2><?=$this->transEsc('Find More')?></h2> - <ul> - <li><a href="<?=$this->url('browse-home')?>"><?=$this->transEsc('Browse the Catalog')?></a></li> - <li><a href="<?=$this->url('alphabrowse-home')?>"><?=$this->transEsc('Browse Alphabetically')?></a></li> - <li><a href="<?=$this->url('channels-home')?>"><?=$this->transEsc('channel_explore')?></a></li> - <li><a href="<?=$this->url('search-reserves')?>"><?=$this->transEsc('Course Reserves')?></a></li> - <li><a href="<?=$this->url('search-newitem')?>"><?=$this->transEsc('New Items')?></a></li> - </ul> + <?php $this->slot('footer-center')->start(); ?> + <h2><?=$this->transEsc('Find More')?></h2> + <ul> + <li><a href="<?=$this->url('browse-home')?>"><?=$this->transEsc('Browse the Catalog')?></a></li> + <li><a href="<?=$this->url('alphabrowse-home')?>"><?=$this->transEsc('Browse Alphabetically')?></a></li> + <li><a href="<?=$this->url('channels-home')?>"><?=$this->transEsc('channel_explore')?></a></li> + <li><a href="<?=$this->url('search-reserves')?>"><?=$this->transEsc('Course Reserves')?></a></li> + <li><a href="<?=$this->url('search-newitem')?>"><?=$this->transEsc('New Items')?></a></li> + </ul> + <?=$this->slot('footer-center')->end(); ?> </div> <div class="footer-column"> - <h2><?=$this->transEsc('Need Help?')?></h2> - <ul> - <li><a href="<?=$this->url('help-home')?>?topic=search&_=<?=time() ?>" data-lightbox class="help-link"><?=$this->transEsc('Search Tips')?></a></li> - <li><a href="<?=$this->url('content-page', ['page' => 'asklibrary']) ?>"><?=$this->transEsc('Ask a Librarian')?></a></li> - <li><a href="<?=$this->url('content-page', ['page' => 'faq']) ?>"><?=$this->transEsc('FAQs')?></a></li> - </ul> + <?php $this->slot('footer-right')->start(); ?> + <h2><?=$this->transEsc('Need Help?')?></h2> + <ul> + <li><a href="<?=$this->url('help-home')?>?topic=search&_=<?=time() ?>" data-lightbox class="help-link"><?=$this->transEsc('Search Tips')?></a></li> + <li><a href="<?=$this->url('content-page', ['page' => 'asklibrary']) ?>"><?=$this->transEsc('Ask a Librarian')?></a></li> + <li><a href="<?=$this->url('content-page', ['page' => 'faq']) ?>"><?=$this->transEsc('FAQs')?></a></li> + </ul> + <?=$this->slot('footer-right')->end(); ?> </div> </div> <div class="poweredby"> diff --git a/themes/bootstrap3/templates/record/checkbox.phtml b/themes/bootstrap3/templates/record/checkbox.phtml index 750c00fea0667df5b8b9cead6dba29d2097fd554..7c37bb0ab2482b8e6e321128dd30ed6fc4f77ac6 100644 --- a/themes/bootstrap3/templates/record/checkbox.phtml +++ b/themes/bootstrap3/templates/record/checkbox.phtml @@ -1,5 +1,5 @@ <label class="record-checkbox hidden-print"> - <input class="checkbox-select-item" type="checkbox" name="ids[]" value="<?=$this->escapeHtmlAttr($this->id)?>"<?php if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<?php endif; ?>/> + <input class="checkbox-select-item" type="checkbox" name="ids[]" value="<?=$this->escapeHtmlAttr($this->id) ?>"<?php if(isset($this->formAttr)): ?> form="<?=$this->formAttr ?>"<?php endif; ?>/> <span class="checkbox-icon"></span> <?php if (strlen($this->number ?? '') > 0): ?><span class="sr-only"><?=$this->transEsc('result_checkbox_label', ['%%number%%' => $this->number]) ?></span><?php endif; ?> </label> diff --git a/themes/bootstrap3/templates/search/facet-list.phtml b/themes/bootstrap3/templates/search/facet-list.phtml index f1fc812acd1467b32c24a1ce4408021e582a7f92..91c577d2df7a5cd7e30ad84188669417a2cd3919 100644 --- a/themes/bootstrap3/templates/search/facet-list.phtml +++ b/themes/bootstrap3/templates/search/facet-list.phtml @@ -5,7 +5,7 @@ $this->sort = 'default'; $this->sortOptions = [ 'default' => 'default' ]; } - $urlBase = $this->url($facetLightbox) . $results->getUrlQuery()->getParams() . '&facet=' . urlencode($this->facet) . '&facetexclude=' . $this->exclude . '&facetop=' . $this->operator; + $urlBase = $this->url($facetLightbox) . $results->getUrlQuery()->getParams() . '&facet=' . urlencode($this->facet) . '&facetexclude=' . urlencode($this->exclude) . '&facetop=' . urlencode($this->operator); $searchAction = $this->url($options->getSearchAction()); if (!empty($this->baseUriExtra)) { $searchAction .= urlencode($this->baseUriExtra);