diff --git a/config/vufind/KohaRest.ini b/config/vufind/KohaRest.ini index 872d7cc31a7..fe853d7ae7d 100644 --- a/config/vufind/KohaRest.ini +++ b/config/vufind/KohaRest.ini @@ -47,6 +47,8 @@ host = "http://koha-server/api" ; - updatecharges ; - payout ; - remaining_permissions +; - recalls +; - manage_recalls ; ; Add an API key to the user and copy the values for Client ID and Secret below. ; To add an API key in Koha, go to the patron screen and click More -> Manage API @@ -125,6 +127,11 @@ updateFields = frozen:frozenThrough:pickUpLocation ; is false. ;allowCancelInTransit = false +; Uncomment the following line to enable recalls (disabled by default). Requires a +; Koha version that includes +; https://bugs.koha-community.org/bugzilla3/show_bug.cgi?id=36075 +;enableRecalls = true + ; This section controls article request behavior. To enable, uncomment (at minimum) ; the HMACKeys and extraFields settings below. [StorageRetrievalRequests] @@ -160,10 +167,10 @@ extraFields = item-issue:acceptTerms:pickUpLocation ; false. Requires Koha REST DI plugin version 23.11.06 or later (earlier versions ; included suspended holds by default). ;includeSuspendedHoldsInQueueLength = false -; This section allows libraries to define different custom itemLimit rules for +; This section allows libraries to define different custom itemLimit rules for ; different biblio-level item types. ; In Koha the biblio-level item type is defined in the 942$c subfield. -; Set 'itemLimit' to set a fallback value that will be used for any item types not given a more +; Set 'itemLimit' to set a fallback value that will be used for any item types not given a more ; specific setting. ; Set 'itemLimitByType' followed by a [] containing a string for the Koha biblio-level item type. ; The string after the equal sign is the number of items to display in the holdings tab. diff --git a/config/vufind/config.ini b/config/vufind/config.ini index bccfdfe4f92..ccfcb445e50 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -55,6 +55,9 @@ theme = sandal ; standard themes since they support responsive design. ;mobile_theme = mobile +; Uncomment the following line to use a different theme for Admin module. +;admin_theme = sandal + ; Automatic asset minification and concatenation setting. When active, HeadScript ; and HeadLink will concatenate and minify all viable files to reduce requests and ; load times. This setting is off by default. @@ -2060,9 +2063,12 @@ hide_holdings[] = "World Wide Web" ; Available options: ; Channels - Display links to channels of content related to record ; Bookplate - Display a bookplate image or something similar +; MoreByAuthorSolr - Display books from the Solr index matching the current +; record's primary author. ; Similar - Similarity based on Solr lookup ; WorldCatSimilar - Similarity based on WorldCat lookup related[] = "Similar" +;related[] = "MoreByAuthorSolr" ; The following settings are for the related Bookplate module. They can be ; enabled here by uncommenting below or from another config file of your choice. diff --git a/languages/en.ini b/languages/en.ini index fdbe67d763f..4cba704fb5e 100644 --- a/languages/en.ini +++ b/languages/en.ini @@ -843,6 +843,7 @@ More options = "More options" More Summon results = "More Summon results…" More Topics = "More Topics" more_authors_abbrev = "et al." +more_by_author = "Also by %%name%%" more_ellipsis = "more…" more_info_toggle = "Show/hide more info." more_options_ellipsis = "More options…" diff --git a/module/VuFind/src/VuFind/ILS/Driver/Folio.php b/module/VuFind/src/VuFind/ILS/Driver/Folio.php index 33a23b0cfcb..81934f11b4b 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/Folio.php +++ b/module/VuFind/src/VuFind/ILS/Driver/Folio.php @@ -935,7 +935,7 @@ protected function getDateTimeFromString(string $str): DateTime */ protected function getDueDate($itemId, $showTime) { - $query = 'itemId==' . $itemId; + $query = 'itemId==' . $itemId . ' AND status.name==Open'; foreach ( $this->getPagedResults( 'loans', diff --git a/module/VuFind/src/VuFind/ILS/Driver/KohaRest.php b/module/VuFind/src/VuFind/ILS/Driver/KohaRest.php index 98654a13236..b8f1871bca4 100644 --- a/module/VuFind/src/VuFind/ILS/Driver/KohaRest.php +++ b/module/VuFind/src/VuFind/ILS/Driver/KohaRest.php @@ -902,6 +902,7 @@ public function getMyHolds($patron) $entry['pickup_library_id'] ?? null ), 'create' => $this->convertDate($entry['hold_date'] ?? null), + '__create' => $entry['hold_date'] ?? null, 'expire' => $available ? null : $expirationDate, 'position' => $entry['priority'], 'available' => $available, @@ -921,6 +922,65 @@ public function getMyHolds($patron) ]; } + if ($this->config['Holds']['enableRecalls'] ?? false) { + $result = $this->makeRequest( + [ + 'path' => 'v1/recalls', + 'query' => [ + 'patron_id' => $patron['id'], + 'completed' => 'false', + '_match' => 'exact', + '_per_page' => -1, + ], + ] + ); + + foreach ($result['data'] as $entry) { + $biblio = $this->getBiblio($entry['biblio_id']); + $volume = ''; + if ($entry['item_id'] ?? null) { + $item = $this->getItem($entry['item_id']); + $volume = $item['serial_issue_number']; + } + $available = !empty($entry['waiting_date']); + $inTransit = !empty($entry['status']) && $entry['status'] == 'in_transit'; + $requestId = $entry['recall_id']; + $cancelDetails = ''; + $updateDetails = ($available || $inTransit) ? '' : $requestId; + // Note: Expiration date is the last interest date until the hold becomes + // available for pickup. Then it becomes the last pickup date. + $expirationDate = $this->convertDate($entry['expiration_date']); + $holds[] = [ + 'id' => $entry['biblio_id'], + 'item_id' => $entry['recall_id'], + 'reqnum' => $requestId, + 'location' => $this->getLibraryName( + $entry['pickup_library_id'] ?? null + ), + 'create' => $this->convertDate($entry['hold_date'] ?? null), + '__create' => $entry['hold_date'] ?? null, + 'expire' => $available ? null : $expirationDate, + 'position' => $entry['priority'], + 'available' => $available, + 'last_pickup_date' => $available ? $expirationDate : null, + 'in_transit' => $inTransit, + 'title' => $this->getBiblioTitle($biblio), + 'isbn' => $biblio['isbn'] ?? '', + 'issn' => $biblio['issn'] ?? '', + 'publication_year' => $biblio['copyright_date'] + ?? $biblio['publication_year'] ?? '', + 'volume' => $volume, + 'cancel_details' => $cancelDetails, + 'updateDetails' => $updateDetails, + ]; + } + } + $callback = function ($a, $b) { + return $a['__create'] === $b['__create'] + ? $a['item_id'] <=> $b['item_id'] + : $a['__create'] <=> $b['__create']; + }; + usort($holds, $callback); return $holds; } diff --git a/module/VuFind/src/VuFind/Related/MoreByAuthorSolr.php b/module/VuFind/src/VuFind/Related/MoreByAuthorSolr.php new file mode 100644 index 00000000000..84bafdba8c7 --- /dev/null +++ b/module/VuFind/src/VuFind/Related/MoreByAuthorSolr.php @@ -0,0 +1,124 @@ + + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:related_records_modules Wiki + */ + +namespace VuFind\Related; + +use VuFindSearch\Command\SearchCommand; +use VuFindSearch\Query\Query; + +use function count; + +/** + * Related Records: Solr-based "more by author" + * + * @category VuFind + * @package Related_Records + * @author Demian Katz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:related_records_modules Wiki + */ +class MoreByAuthorSolr implements RelatedInterface +{ + /** + * Similar records + * + * @var array + */ + protected array $results = []; + + /** + * Author being searched + * + * @var string + */ + protected string $author = ''; + + /** + * Maximum number of titles to suggest + * + * @var int + */ + protected int $maxRecommendations = 5; + + /** + * Constructor + * + * @param \VuFindSearch\Service $searchService Search service + */ + public function __construct(protected \VuFindSearch\Service $searchService) + { + } + + /** + * Establishes base settings for making recommendations. + * + * @param string $settings Settings from config.ini + * @param \VuFind\RecordDriver\AbstractBase $driver Record driver object + * + * @return void + */ + public function init($settings, $driver) + { + $this->results = []; + if ($this->author = $driver->tryMethod('getPrimaryAuthor')) { + $queryStr = '"' . addcslashes($this->author, '"') . '"'; + $query = new Query($queryStr, 'Author'); + $command = new SearchCommand(DEFAULT_SEARCH_BACKEND, $query, 0, $this->maxRecommendations + 1); + foreach ($this->searchService->invoke($command)->getResult() as $result) { + if (count($this->results) >= $this->maxRecommendations) { + break; + } + if ($result->getUniqueID() != $driver->getUniqueID()) { + $this->results[] = $result; + } + } + } + } + + /** + * Get name of author being searched for. + * + * @return string + */ + public function getName(): string + { + return $this->author; + } + + /** + * Get an array of Record Driver objects representing items similar to the one + * passed to the constructor. + * + * @return array + */ + public function getResults(): array + { + return $this->results; + } +} diff --git a/module/VuFind/src/VuFind/Related/PluginManager.php b/module/VuFind/src/VuFind/Related/PluginManager.php index 6f320158433..a1bcb44a024 100644 --- a/module/VuFind/src/VuFind/Related/PluginManager.php +++ b/module/VuFind/src/VuFind/Related/PluginManager.php @@ -51,6 +51,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager 'channels' => Channels::class, 'bookplate' => Bookplate::class, 'editions' => Deprecated::class, + 'morebyauthorsolr' => MoreByAuthorSolr::class, 'similar' => Similar::class, 'worldcateditions' => Deprecated::class, 'worldcatsimilar' => WorldCatSimilar::class, @@ -65,6 +66,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager Channels::class => InvokableFactory::class, Bookplate::class => BookplateFactory::class, Deprecated::class => InvokableFactory::class, + MoreByAuthorSolr::class => SimilarFactory::class, Similar::class => SimilarFactory::class, WorldCatSimilar::class => SimilarFactory::class, ]; diff --git a/module/VuFind/src/VuFind/Role/PermissionManager.php b/module/VuFind/src/VuFind/Role/PermissionManager.php index c9dfae4c0bf..0c0279121e5 100644 --- a/module/VuFind/src/VuFind/Role/PermissionManager.php +++ b/module/VuFind/src/VuFind/Role/PermissionManager.php @@ -90,6 +90,20 @@ public function isAuthorized($permission, $context = null) return false; } + /** + * Get a list of all configured permissions. + * + * @return string[] + */ + public function getAllConfiguredPermissions(): array + { + $permissions = []; + foreach ($this->config as $value) { + $permissions = array_merge($permissions, (array)($value['permission'] ?? [])); + } + return array_values(array_unique($permissions)); + } + /** * Check if a permission rule exists * diff --git a/module/VuFind/tests/fixtures/folio/responses/get-holding-checkedout.json b/module/VuFind/tests/fixtures/folio/responses/get-holding-checkedout.json index cfdf1259c7a..926be5dd59b 100644 --- a/module/VuFind/tests/fixtures/folio/responses/get-holding-checkedout.json +++ b/module/VuFind/tests/fixtures/folio/responses/get-holding-checkedout.json @@ -188,7 +188,7 @@ "expectedMethod": "GET", "expectedPath": "\/circulation\/loans", "expectedParams": { - "query": "itemId==itemid", + "query": "itemId==itemid AND status.name==Open", "offset": 0, "limit": 1000 }, diff --git a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/HoldsTest.php b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/HoldsTest.php index 05afa21e315..e63eb10b141 100644 --- a/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/HoldsTest.php +++ b/module/VuFind/tests/integration-tests/src/VuFindTest/Mink/HoldsTest.php @@ -107,7 +107,7 @@ protected function gotoRecordWithSearch( . urlencode("id:($id)") ); $page = $session->getPage(); - $this->clickCss($page, '#result0 a.record-cover-link'); + $this->clickCss($page, '#result0 a.getFull'); $this->waitForPageLoad($page); return $page; } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/MoreByAuthorSolrTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/MoreByAuthorSolrTest.php new file mode 100644 index 00000000000..aabab328af2 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/MoreByAuthorSolrTest.php @@ -0,0 +1,86 @@ + + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ + +namespace VuFindTest\Related; + +use VuFind\Related\MoreByAuthorSolr; +use VuFindSearch\Query\Query; + +/** + * MoreByAuthorSolr Related Items Test Class + * + * @category VuFind + * @package Tests + * @author Demian Katz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class MoreByAuthorSolrTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test results. + * + * @return void + */ + public function testGetResults() + { + // Similar is really just a thin wrapper around the search service; make + // sure it does its job properly with the help of some mocks. + $driver = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) + ->onlyMethods(['getPrimaryAuthor', 'getUniqueId']) + ->getMock(); + $driver->method('getUniqueId')->willReturn('fakeid'); + $driver->method('getPrimaryAuthor')->willReturn('Smith, John'); + + $driver2 = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) + ->onlyMethods(['getPrimaryAuthor', 'getUniqueId']) + ->getMock(); + $driver2->method('getUniqueId')->willReturn('fakeid2'); + $driver2->method('getPrimaryAuthor')->willReturn('Smith, John'); + + $commandObj = $this->createMock(\VuFindSearch\Command\AbstractBase::class); + $commandObj->expects($this->once())->method('getResult')->willReturn([$driver, $driver2]); + $checkCommand = function ($command) { + $this->assertEquals(\VuFindSearch\Command\SearchCommand::class, $command::class); + $this->assertEquals('Solr', $command->getTargetIdentifier()); + $expectedQuery = new Query('"Smith, John"', 'Author'); + $this->assertEquals($expectedQuery, $command->getArguments()[0]); + return true; + }; + $service = $this->getMockBuilder(\VuFindSearch\Service::class) + ->getMock(); + $service->expects($this->once())->method('invoke') + ->with($this->callback($checkCommand)) + ->willReturn($commandObj); + $related = new MoreByAuthorSolr($service); + $related->init('', $driver); + $this->assertEquals('Smith, John', $related->getName()); + $this->assertEquals([$driver2], $related->getResults()); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php index 5252acb239e..c1840aadbe5 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Related/SimilarTest.php @@ -54,25 +54,23 @@ public function testGetResults() $driver = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) ->onlyMethods(['getUniqueId']) ->getMock(); - $driver->expects($this->once()) - ->method('getUniqueId') - ->will($this->returnValue('fakeid')); + $driver->expects($this->once())->method('getUniqueId')->willReturn('fakeid'); $commandObj = $this->getMockBuilder(\VuFindSearch\Command\AbstractBase::class) ->disableOriginalConstructor() ->getMock(); $commandObj->expects($this->once())->method('getResult') - ->will($this->returnValue(['fakeresponse'])); + ->willReturn(['fakeresponse']); $checkCommand = function ($command) { - return $command::class === \VuFindSearch\Command\SimilarCommand::class - && $command->getTargetIdentifier() === 'Solr' - && $command->getArguments()[0] === 'fakeid'; + $this->assertEquals(\VuFindSearch\Command\SimilarCommand::class, $command::class); + $this->assertEquals('Solr', $command->getTargetIdentifier()); + $this->assertEquals('fakeid', $command->getArguments()[0]); + return true; }; - $service = $this->getMockBuilder(\VuFindSearch\Service::class) - ->getMock(); + $service = $this->createMock(\VuFindSearch\Service::class); $service->expects($this->once())->method('invoke') ->with($this->callback($checkCommand)) - ->will($this->returnValue($commandObj)); + ->willReturn($commandObj); $similar = new Similar($service); $similar->init('', $driver); $this->assertEquals(['fakeresponse'], $similar->getResults()); diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php index ef591550ea3..1151312c07e 100644 --- a/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Role/PermissionManagerTest.php @@ -47,7 +47,7 @@ class PermissionManagerTest extends \PHPUnit\Framework\TestCase * * @var array */ - protected $permissionConfig = [ + protected array $permissionConfig = [ 'permission.all' => [ 'permission' => 'everyone', ], @@ -61,12 +61,26 @@ class PermissionManagerTest extends \PHPUnit\Framework\TestCase ], ]; + /** + * Test getAllConfiguredPermissions() + * + * @return void + */ + public function testGetAllConfiguredPermissions(): void + { + $pm = new PermissionManager($this->permissionConfig); + $this->assertEquals( + ['everyone', 'nobody', 'everyoneArray', 'everyoneArray2'], + $pm->getAllConfiguredPermissions() + ); + } + /** * Test a non existent permission section * * @return void */ - public function testNonExistentPermission() + public function testNonExistentPermission(): void { $pm = new PermissionManager($this->permissionConfig); @@ -78,7 +92,7 @@ public function testNonExistentPermission() * * @return void */ - public function testExistentPermission() + public function testExistentPermission(): void { $pm = new PermissionManager($this->permissionConfig); @@ -90,7 +104,7 @@ public function testExistentPermission() * * @return void */ - public function testExistentPermissionInArray() + public function testExistentPermissionInArray(): void { $pm = new PermissionManager($this->permissionConfig); @@ -102,14 +116,11 @@ public function testExistentPermissionInArray() * * @return void */ - public function testGrantedPermission() + public function testGrantedPermission(): void { $pm = new PermissionManager($this->permissionConfig); - $mockAuth = $this->getMockBuilder(\LmcRbacMvc\Service\AuthorizationService::class) - ->disableOriginalConstructor() - ->getMock(); - $mockAuth->expects($this->any())->method('isGranted') - ->will($this->returnValue(true)); + $mockAuth = $this->createMock(\LmcRbacMvc\Service\AuthorizationService::class); + $mockAuth->expects($this->any())->method('isGranted')->willReturn(true); $pm->setAuthorizationService($mockAuth); $this->assertEquals(true, $pm->isAuthorized('permission.everyone')); @@ -123,11 +134,8 @@ public function testGrantedPermission() public function testDeniedPermission() { $pm = new PermissionManager($this->permissionConfig); - $mockAuth = $this->getMockBuilder(\LmcRbacMvc\Service\AuthorizationService::class) - ->disableOriginalConstructor() - ->getMock(); - $mockAuth->expects($this->any())->method('isGranted') - ->will($this->returnValue(false)); + $mockAuth = $this->createMock(\LmcRbacMvc\Service\AuthorizationService::class); + $mockAuth->expects($this->any())->method('isGranted')->willReturn(false); $pm->setAuthorizationService($mockAuth); $this->assertEquals(false, $pm->isAuthorized('permission.nobody')); diff --git a/module/VuFindAdmin/config/module.config.php b/module/VuFindAdmin/config/module.config.php index 9188ef6306e..4bdb9bf1a5b 100644 --- a/module/VuFindAdmin/config/module.config.php +++ b/module/VuFindAdmin/config/module.config.php @@ -33,6 +33,7 @@ 'defaults' => [ 'controller' => 'Admin', 'action' => 'Home', + 'admin_route' => true, ], ], 'may_terminate' => true, diff --git a/module/VuFindDevTools/config/module.config.php b/module/VuFindDevTools/config/module.config.php index e08eb4c7d05..2c308fc1298 100644 --- a/module/VuFindDevTools/config/module.config.php +++ b/module/VuFindDevTools/config/module.config.php @@ -24,9 +24,9 @@ ], ], 'devtools-home' => [ - 'type' => 'Laminas\Router\Http\Literal', + 'type' => 'Laminas\Router\Http\Segment', 'options' => [ - 'route' => '/devtools/home', + 'route' => '/devtools[/home]', 'defaults' => [ 'controller' => 'DevTools', 'action' => 'Home', @@ -53,6 +53,16 @@ ], ], ], + 'devtools-permissions' => [ + 'type' => 'Laminas\Router\Http\Literal', + 'options' => [ + 'route' => '/devtools/permissions', + 'defaults' => [ + 'controller' => 'DevTools', + 'action' => 'Permissions', + ], + ], + ], ], ], ]; diff --git a/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php b/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php index 2a271a79e8d..2d470a0bb44 100644 --- a/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php +++ b/module/VuFindDevTools/src/VuFindDevTools/Controller/DevtoolsController.php @@ -32,6 +32,7 @@ use VuFind\I18n\Locale\LocaleSettings; use VuFind\I18n\Translator\Loader\ExtendedIni; +use VuFind\Role\PermissionManager; use VuFind\Search\Results\PluginManager as ResultsManager; use VuFindDevTools\LanguageHelper; @@ -140,4 +141,20 @@ public function languageAction() (bool)$this->params()->fromQuery('includeOptional', 1) ); } + + /** + * Permissions action + * + * @return array + */ + public function permissionsAction() + { + $manager = $this->getService(PermissionManager::class); + $permissions = []; + foreach ($manager->getAllConfiguredPermissions() as $permission) { + $permissions[$permission] = $manager->isAuthorized($permission); + } + ksort($permissions); + return compact('permissions'); + } } diff --git a/module/VuFindTheme/src/VuFindTheme/Initializer.php b/module/VuFindTheme/src/VuFindTheme/Initializer.php index d90ca0524bb..202f7d8adeb 100644 --- a/module/VuFindTheme/src/VuFindTheme/Initializer.php +++ b/module/VuFindTheme/src/VuFindTheme/Initializer.php @@ -201,6 +201,21 @@ public function init() */ protected function pickTheme(?Request $request) { + // The admin theme should always be picked if + // - the Admin module is enabled AND + // - an admin theme is set AND + // - an admin route is requested (route configuration has an + // 'admin_route' => true default parameter). + if ( + isset($this->event) + && ($routeMatch = $this->event->getRouteMatch()) + && $routeMatch->getParam('admin_route') + && ($this->config->admin_enabled ?? false) + && ($adminTheme = ($this->config->admin_theme ?? false)) + ) { + return $adminTheme; + } + // Load standard configuration options: $standardTheme = $this->config->theme; if (PHP_SAPI == 'cli') { diff --git a/themes/bootstrap3/templates/Related/MoreByAuthorSolr.phtml b/themes/bootstrap3/templates/Related/MoreByAuthorSolr.phtml new file mode 100644 index 00000000000..80678288d9b --- /dev/null +++ b/themes/bootstrap3/templates/Related/MoreByAuthorSolr.phtml @@ -0,0 +1,9 @@ +related->getResults(); ?> + +

transEsc('more_by_author', ['%%name%%' => $this->related->getName()])?>

+ + diff --git a/themes/bootstrap3/templates/Related/Similar.phtml b/themes/bootstrap3/templates/Related/Similar.phtml index a30d6354628..7d1773ccb7d 100644 --- a/themes/bootstrap3/templates/Related/Similar.phtml +++ b/themes/bootstrap3/templates/Related/Similar.phtml @@ -3,33 +3,7 @@ diff --git a/themes/bootstrap3/templates/Related/Similar/item.phtml b/themes/bootstrap3/templates/Related/Similar/item.phtml new file mode 100644 index 00000000000..9bc365e6585 --- /dev/null +++ b/themes/bootstrap3/templates/Related/Similar/item.phtml @@ -0,0 +1,25 @@ + 'related__icon']; + + $formats = $data->getFormats(); + $format = $formats[0] ?? null; + $icon = $format + ? preg_replace('/[^a-z0-9]/', '', strtolower($format)) + : 'default'; + + if ($format) { + $attrs['title'] = $format; + } +?> + + icon('format-' . $icon, $attrs) ?> + escapeHtml($data->getTitle())?> + +getPrimaryAuthors(); ?> + +
transEsc('by')?>: escapeHtml($authors[0]);?> 1): ?>, transEsc('more_authors_abbrev')?> + +getPublicationDates(); ?> + +
transEsc('Published')?>: (escapeHtml($pubDates[0])?>) + diff --git a/themes/bootstrap3/templates/devtools/deminify.phtml b/themes/bootstrap3/templates/devtools/deminify.phtml index cb73fe5ee6a..34cd74a1cca 100644 --- a/themes/bootstrap3/templates/devtools/deminify.phtml +++ b/themes/bootstrap3/templates/devtools/deminify.phtml @@ -1,8 +1,10 @@ headTitle('Deminifier'); + $this->layout()->breadcrumbs = '
  • Development Tools' + . '
  • Deminifier
  • '; ?> -

    Deminifier

    +

    Deminifier

    diff --git a/themes/bootstrap3/templates/devtools/home.phtml b/themes/bootstrap3/templates/devtools/home.phtml index 8d9e74a2323..c5d6944641b 100644 --- a/themes/bootstrap3/templates/devtools/home.phtml +++ b/themes/bootstrap3/templates/devtools/home.phtml @@ -1,11 +1,13 @@ headTitle('Development Tools'); + $this->layout()->breadcrumbs = '
  • Development Tools
  • '; ?> -

    Development Tools

    +

    Development Tools

    • Deminifier - Examine minified search data copied from the search database table.
    • Icon List - Show all configured icons.
    • Language Details - Summarize status of translations in language files.
    • +
    • Permissions - List configured permissions and test access.
    diff --git a/themes/bootstrap3/templates/devtools/icon.phtml b/themes/bootstrap3/templates/devtools/icon.phtml index 57ab914c34e..ff08564428f 100644 --- a/themes/bootstrap3/templates/devtools/icon.phtml +++ b/themes/bootstrap3/templates/devtools/icon.phtml @@ -1,9 +1,13 @@ headTitle($this->translate('Icons')); + $title = $this->translate('Icons'); + $titleEsc = $this->escapeHtml($title); + $this->headTitle($title); + $this->layout()->breadcrumbs = '
  • Development Tools' + . '
  • ' . $titleEsc . '
  • '; ?> - - +

    +
    icon($icon)?>: escapeHtml($icon)?>
    -
    + diff --git a/themes/bootstrap3/templates/devtools/language.phtml b/themes/bootstrap3/templates/devtools/language.phtml index 76591f134c8..885d80eab80 100644 --- a/themes/bootstrap3/templates/devtools/language.phtml +++ b/themes/bootstrap3/templates/devtools/language.phtml @@ -2,6 +2,9 @@ $pageTitle = 'Comparing Languages Against ' . $mainName; $this->headTitle($pageTitle); + $this->layout()->breadcrumbs = '
  • Development Tools' + . '
  • ' . $this->escapeHtml($pageTitle) . '
  • '; + $uLangs = []; foreach ($this->layout()->allLangs ?? [] as $c => $n) { $uLangs[] = $c; diff --git a/themes/bootstrap3/templates/devtools/permissions.phtml b/themes/bootstrap3/templates/devtools/permissions.phtml new file mode 100644 index 00000000000..075868cd700 --- /dev/null +++ b/themes/bootstrap3/templates/devtools/permissions.phtml @@ -0,0 +1,15 @@ +translate('Permissions'); + $titleEsc = $this->escapeHtml($title); + $this->headTitle($title); + $this->layout()->breadcrumbs = '
  • Development Tools' + . '
  • ' . $titleEsc . '
  • '; +?> +

    + + + + $status): ?> + + +
    PermissionGranted?
    escapeHtml($permission)?>
    diff --git a/themes/bootstrap3/templates/myresearch/profile.phtml b/themes/bootstrap3/templates/myresearch/profile.phtml index a1ed5fd56ba..f988175431f 100644 --- a/themes/bootstrap3/templates/myresearch/profile.phtml +++ b/themes/bootstrap3/templates/myresearch/profile.phtml @@ -104,7 +104,7 @@ $token): ?> transEsc($token->getPlatform())?> / transEsc($token->getBrowser())?> - dateTime()->convertToDisplayDateAndTime('U', $token->getLastLogin()->getTimestamp())?> + getLastLogin()->format($this->config()->dateTimeFormat())?> diff --git a/themes/bootstrap5/templates/Related/MoreByAuthorSolr.phtml b/themes/bootstrap5/templates/Related/MoreByAuthorSolr.phtml new file mode 100644 index 00000000000..80678288d9b --- /dev/null +++ b/themes/bootstrap5/templates/Related/MoreByAuthorSolr.phtml @@ -0,0 +1,9 @@ +related->getResults(); ?> + +

    transEsc('more_by_author', ['%%name%%' => $this->related->getName()])?>

    +
      + +
    • render('Related/Similar/item.phtml', compact('data'))?>
    • + +
    + diff --git a/themes/bootstrap5/templates/Related/Similar.phtml b/themes/bootstrap5/templates/Related/Similar.phtml index a30d6354628..7d1773ccb7d 100644 --- a/themes/bootstrap5/templates/Related/Similar.phtml +++ b/themes/bootstrap5/templates/Related/Similar.phtml @@ -3,33 +3,7 @@
      -
    • - 'related__icon']; - - $formats = $data->getFormats(); - $format = $formats[0] ?? null; - $icon = $format - ? preg_replace('/[^a-z0-9]/', '', strtolower($format)) - : 'default'; - - if ($format) { - $attrs['title'] = $format; - } - ?> - - icon('format-' . $icon, $attrs) ?> - escapeHtml($data->getTitle())?> - - getPrimaryAuthors(); ?> - -
      transEsc('by')?>: escapeHtml($authors[0]);?> 1): ?>, transEsc('more_authors_abbrev')?> - - getPublicationDates(); ?> - -
      transEsc('Published')?>: (escapeHtml($pubDates[0])?>) - -
    • +
    • render('Related/Similar/item.phtml', compact('data'))?>
    diff --git a/themes/bootstrap5/templates/Related/Similar/item.phtml b/themes/bootstrap5/templates/Related/Similar/item.phtml new file mode 100644 index 00000000000..9bc365e6585 --- /dev/null +++ b/themes/bootstrap5/templates/Related/Similar/item.phtml @@ -0,0 +1,25 @@ + 'related__icon']; + + $formats = $data->getFormats(); + $format = $formats[0] ?? null; + $icon = $format + ? preg_replace('/[^a-z0-9]/', '', strtolower($format)) + : 'default'; + + if ($format) { + $attrs['title'] = $format; + } +?> + + icon('format-' . $icon, $attrs) ?> + escapeHtml($data->getTitle())?> + +getPrimaryAuthors(); ?> + +
    transEsc('by')?>: escapeHtml($authors[0]);?> 1): ?>, transEsc('more_authors_abbrev')?> + +getPublicationDates(); ?> + +
    transEsc('Published')?>: (escapeHtml($pubDates[0])?>) + diff --git a/themes/bootstrap5/templates/devtools/deminify.phtml b/themes/bootstrap5/templates/devtools/deminify.phtml index cb73fe5ee6a..34cd74a1cca 100644 --- a/themes/bootstrap5/templates/devtools/deminify.phtml +++ b/themes/bootstrap5/templates/devtools/deminify.phtml @@ -1,8 +1,10 @@ headTitle('Deminifier'); + $this->layout()->breadcrumbs = '
  • Development Tools' + . '
  • Deminifier
  • '; ?> -

    Deminifier

    +

    Deminifier

    diff --git a/themes/bootstrap5/templates/devtools/home.phtml b/themes/bootstrap5/templates/devtools/home.phtml index 8d9e74a2323..c5d6944641b 100644 --- a/themes/bootstrap5/templates/devtools/home.phtml +++ b/themes/bootstrap5/templates/devtools/home.phtml @@ -1,11 +1,13 @@ headTitle('Development Tools'); + $this->layout()->breadcrumbs = '
  • Development Tools
  • '; ?> -

    Development Tools

    +

    Development Tools

    • Deminifier - Examine minified search data copied from the search database table.
    • Icon List - Show all configured icons.
    • Language Details - Summarize status of translations in language files.
    • +
    • Permissions - List configured permissions and test access.
    diff --git a/themes/bootstrap5/templates/devtools/icon.phtml b/themes/bootstrap5/templates/devtools/icon.phtml index 57ab914c34e..ff08564428f 100644 --- a/themes/bootstrap5/templates/devtools/icon.phtml +++ b/themes/bootstrap5/templates/devtools/icon.phtml @@ -1,9 +1,13 @@ headTitle($this->translate('Icons')); + $title = $this->translate('Icons'); + $titleEsc = $this->escapeHtml($title); + $this->headTitle($title); + $this->layout()->breadcrumbs = '
  • Development Tools' + . '
  • ' . $titleEsc . '
  • '; ?> - - +

    +
    icon($icon)?>: escapeHtml($icon)?>
    -
    + diff --git a/themes/bootstrap5/templates/devtools/language.phtml b/themes/bootstrap5/templates/devtools/language.phtml index 531372d8e67..38486a93b12 100644 --- a/themes/bootstrap5/templates/devtools/language.phtml +++ b/themes/bootstrap5/templates/devtools/language.phtml @@ -2,6 +2,9 @@ $pageTitle = 'Comparing Languages Against ' . $mainName; $this->headTitle($pageTitle); + $this->layout()->breadcrumbs = '
  • Development Tools' + . '
  • ' . $this->escapeHtml($pageTitle) . '
  • '; + $uLangs = []; foreach ($this->layout()->allLangs ?? [] as $c => $n) { $uLangs[] = $c; diff --git a/themes/bootstrap5/templates/devtools/permissions.phtml b/themes/bootstrap5/templates/devtools/permissions.phtml new file mode 100644 index 00000000000..075868cd700 --- /dev/null +++ b/themes/bootstrap5/templates/devtools/permissions.phtml @@ -0,0 +1,15 @@ +translate('Permissions'); + $titleEsc = $this->escapeHtml($title); + $this->headTitle($title); + $this->layout()->breadcrumbs = '
  • Development Tools' + . '
  • ' . $titleEsc . '
  • '; +?> +

    + + + + $status): ?> + + +
    PermissionGranted?
    escapeHtml($permission)?>
    diff --git a/themes/bootstrap5/templates/myresearch/profile.phtml b/themes/bootstrap5/templates/myresearch/profile.phtml index a1ed5fd56ba..f988175431f 100644 --- a/themes/bootstrap5/templates/myresearch/profile.phtml +++ b/themes/bootstrap5/templates/myresearch/profile.phtml @@ -104,7 +104,7 @@ $token): ?> transEsc($token->getPlatform())?> / transEsc($token->getBrowser())?> - dateTime()->convertToDisplayDateAndTime('U', $token->getLastLogin()->getTimestamp())?> + getLastLogin()->format($this->config()->dateTimeFormat())?>