diff --git a/local/languages/de.ini b/local/languages/de.ini index 6f2899bb1c01b4f0b5df5e8aaceeba57c7d4edeb..c0b5aeabfe3151d51a8468ebacb1bfca6b6267fc 100644 --- a/local/languages/de.ini +++ b/local/languages/de.ini @@ -1954,6 +1954,7 @@ sources_explanatory_line = "Folgende Ressourcen sind im lizenzierten Zeitraum du Filter list = "Liste filtern" Please enter filter term = "Bitte Begriff(e) zum Filtern eingeben" Expand all = "Alle ausklappen" +Collapse all = "Alle einklappen" Expand = "Ausklappen" resources_cannot_received = "Fehler aufgetreten. Resourcen konnten nicht geladen werden" support_by_dfg = "Die Nationallizenzen wurden gefördert durch die" diff --git a/local/languages/en.ini b/local/languages/en.ini index 389553aabc039aac15129e38c4a5b03168b9177a..a725c125450d2988d240d81b76f680721ea397d9 100644 --- a/local/languages/en.ini +++ b/local/languages/en.ini @@ -2058,6 +2058,7 @@ Filter list = "Filter list " Please enter filter term = "Please enter filter term(s)" Expand all = "Expand all" Expand = "Expand" +Collapse all = "Collapse all" resources_cannot_received = "Error occurred. List of resources cannot loaded" support_by_dfg = "National licenses were sponsored by " diff --git a/module/finc/src/finc/Controller/AmslResourceController.php b/module/finc/src/finc/Controller/AmslResourceController.php index 7d36b314fba7bd15369d33ecd536c50df30b1c3b..70b355af1c1ea1dcb7b677d446e24695cee7fa39 100644 --- a/module/finc/src/finc/Controller/AmslResourceController.php +++ b/module/finc/src/finc/Controller/AmslResourceController.php @@ -43,22 +43,40 @@ use Zend\ServiceManager\ServiceLocatorInterface; */ class AmslResourceController extends AbstractBase { + const DEFAULT_TTL = 86400; + /** * Amsl.ini configuration. * - * @var $config - * @access protected + * @var \Zend\Config\Config */ protected $config = []; + /** + * @var mixed|null AMSL API configuration + */ + protected $apiConfig; + /** * HTTP client * * @var \Zend\Http\Client - * @access protected */ protected $httpClient; + /** + * Cache manager + * + * @var \VuFind\Cache\Manager + */ + protected $cacheManager; + + /** + * base url + * + * @var string + */ + protected $baseUrl; /** * Constructor @@ -66,17 +84,20 @@ class AmslResourceController extends AbstractBase * @param ServiceLocatorInterface $sm * @param \Zend\Config\Config $config VuFind configuration * @param \VuFindHttp\HttpService $httpClient HttpClient + * @param \VuFind\Cache\Manager $cacheManager Cache manager (optional) * - * @access public */ public function __construct( ServiceLocatorInterface $sm, \Zend\Config\Config $config, - \VuFindHttp\HttpService $httpClient + \VuFindHttp\HttpService $httpClient, + \VuFind\Cache\Manager $cacheManager = null ) { parent::__construct($sm); $this->config = $config; + $this->apiConfig = $config->API; $this->httpClient = $httpClient; + $this->cacheManager = $cacheManager; } /** @@ -84,42 +105,62 @@ class AmslResourceController extends AbstractBase * * @return \Zend\View\Model\ViewModel * @throws \Exception - * @access public */ public function homeAction() { // Make view - $api_conf = $this->config->get('API'); $view = $this->createViewModel(); - try { - if (null == ($result = $this->httpClient->get($api_conf->url))) { - throw new \Exception( - 'Unexpected value: No api result received' + $view->setTemplate('amsl/sources-list'); + + if ($data = $this->getCacheData('rendered')) { + $view->rendered_html = $data; + } else { + try { + $view->sources = $this->getData(); + } catch (\Exception $e) { + $this->flashMessenger()->addMessage( + 'resources_cannot_received', + 'error' ); } - if ($result->isSuccess()) { - switch ($api_conf->response_type) { - case 'application/json': - $amsl_sources = json_decode($result->getBody(), true); - break; - default: - throw new \Exception( - 'Invalid argument: No valid header scheme defined' - ); - break; - } + $rendered = $this->getViewRenderer()->render($view); + $this->setCacheData($rendered,'rendered'); + $view->rendered_html = $rendered; + } + + return $view; + } + + /** + * Retrieve amsl sources list, prepare and cache it + * + * @return array + */ + protected function getData() + { + if (null == ($result = $this->httpClient->get($this->apiConfig->url))) { + throw new \Exception( + 'Unexpected value: No api result received' + ); + } + if ($result->isSuccess()) { + switch ($this->apiConfig->response_type) { + case 'application/json': + $amsl_sources = json_decode($result->getBody(), true); + break; + default: + throw new \Exception( + 'Invalid argument: No valid header scheme defined' + ); + break; } + if (isset($amsl_sources)) { - $view->sources = $this->createSourceHierarchy($amsl_sources); + $results = $this->createSourceHierarchy($amsl_sources); } - } catch (\Exception $e) { - $this->flashMessenger()->addMessage( - 'resources_cannot_received', - 'error' - ); } - $view->setTemplate('amsl/sources-list'); - return $view; + + return $results; } /** @@ -128,42 +169,44 @@ class AmslResourceController extends AbstractBase * @param array $amsl_sources * * @return array $out - * @access protected */ protected function createSourceHierarchy(array $amsl_sources) { - $struct = $this->config->get('Mapping'); - $main_key = $struct->main_key; - $sub_key = $struct->sub_key; + $mapping = $this->config->get('Mapping'); $sources = []; foreach ($amsl_sources as $source) { - if (isset($source[$main_key])) { - if (isset($source[$sub_key])) { - $label = $this->renderLabel($struct->sub_label, $source); - $sources[$source[$main_key]][$label] = $source; + $this->prepareSourceForView($source); + if (isset($source[$mapping->main_key])) { + if (isset($source[$mapping->sub_key])) { + $label = $this->renderLabel($mapping->sub_label, $source); + $sources[$source[$mapping->main_key]][$label] = $source; } else { - $sources[$source[$main_key]][$struct->default_sub_label] - = $source; + $sources[$source[$mapping->main_key]][$mapping->default_sub_label] = $source; } } else { - if (isset($source[$sub_key])) { - $label = $this->renderLabel($struct->sub_label, $source); + if (isset($source[$mapping->sub_key])) { + $label = $this->renderLabel($mapping->sub_label, $source); $default[$label] = $source; } else { - $default[$struct->default_sub_label] = $source; + $default[$mapping->default_sub_label] = $source; } } } ksort($sources); $out = []; foreach ($sources as $main) { - $label = $this->renderLabel($struct->main_label, current($main)); + $label = $this->renderLabel($mapping->main_label, current($main)); $out[$label] = $main; } if (isset($default)) { - $out[$struct->default_main_label] = $default; + $out[$mapping->default_main_label] = $default; } + + if (!empty($mapping->sortBySourceLabel)) { + ksort($out); + } + return $out; } @@ -174,7 +217,6 @@ class AmslResourceController extends AbstractBase * @param array $input_array Input * * @return mixed - * @access protected */ protected function renderLabel($pattern, $input_array) { @@ -190,4 +232,99 @@ class AmslResourceController extends AbstractBase $pattern ); } + + /** + * Helper function to generate link and description + * + * @param array &$source Input + * + * @return void + */ + protected function prepareSourceForView(&$source) + { + $mapping = $this->config->get('Mapping'); + + if (isset($mapping->sub_description_key) && isset($source[$mapping->sub_description_key])) { + if (!empty($mapping->show_description)) { + $source["desc"] = $source[$mapping->sub_description_key]; + } + unset($source[$mapping->sub_description_key]); + } + + if(!empty($mapping->show_link) && !empty($source[$mapping->sub_key])) { + $misspelled = $this->config->get('MisspelledResources'); + $searchTerm = $source[$mapping->sub_key]; + if (!empty($misspelled)) { + foreach ($misspelled as $wrongLabel => $rightLabel) { + if (strpos($searchTerm, $wrongLabel) !== false) { + $searchTerm = $rightLabel; + break; + } + } + } + + if (!$this->baseUrl) { + $urlHelper = $this->getViewRenderer()->plugin('url'); + $this->baseUrl = $urlHelper('search-results') . '?filter%5B%5D=mega_collection%3A"'; + } + + if (!empty($searchTerm)) { + $source["href"] = $this->baseUrl . urlencode($searchTerm) . '"'; + } + } + } + + /** + * @return string | null + * @throws \Exception + */ + private function getCacheData($tag = ''): ?string + { + $cache = $this->cacheManager->getCache('object', $this->getCacheNamespace()); + + $cacheKey = $this->getCacheKey($tag); + $ttl = $this->apiConfig['ttl'] ?? self::DEFAULT_TTL; + + if($cache) { + $cache->getOptions()->setTtl($ttl); + $cache->clearExpired(); + } + + return $cache->getItem($cacheKey); + } + + /** + * Helper function for writing data to controller specific cache + * + * @param string $value Cache value to write + * @param string $tag optional tag to be used for cache key + * @throws \Exception + */ + private function setCacheData($value, $tag = '') + { + $cache = $this->cacheManager->getCache('object', $this->getCacheNamespace()); + $cacheKey = $this->getCacheKey($tag); + $cache->setItem($cacheKey, $value); + } + + /** + * Get the namespace to use for caching amls responses. + * + * @return string + */ + protected function getCacheNamespace() + { + return 'amsl'; + } + + /** + * Helper function to generate config-specific cache key + * + * @param string $tag optional tag to make cache more specific + * @return string + */ + protected function getCacheKey($tag = '') + { + return md5(json_encode($this->config->toArray()).$tag); + } } diff --git a/module/finc/src/finc/Controller/AmslResourceControllerFactory.php b/module/finc/src/finc/Controller/AmslResourceControllerFactory.php index da63025c1626e3f6c41421a7a6622356ba3f709b..92f8b637cc5f0baaf6062652ec17f75b2f5a46e0 100644 --- a/module/finc/src/finc/Controller/AmslResourceControllerFactory.php +++ b/module/finc/src/finc/Controller/AmslResourceControllerFactory.php @@ -16,7 +16,8 @@ class AmslResourceControllerFactory return new AmslResourceController( $container, $container->get('VuFind\Config\PluginManager')->get('Amsl'), - $container->get('VuFindHttp\HttpService') + $container->get('VuFindHttp\HttpService'), + $container->get('VuFind\Cache\Manager') ); } } \ No newline at end of file diff --git a/themes/finc/js/sources-display.js b/themes/finc/js/sources-display.js index 7fe2902a9d273fb226f231ae235b93a57e1e9227..a625605e06e01dbb1e0b6c3f0d6fbe28d3b0a54c 100644 --- a/themes/finc/js/sources-display.js +++ b/themes/finc/js/sources-display.js @@ -5,8 +5,20 @@ $('.collapse-toggler').click(function () { }); // Collapse all button -$('.collapse-all-toggler').click(function () { - $('#sources-list li ul').collapse('toggle'); +$('#collapse-all-toggler').click(function () { + $(this).prop('disabled', true); + if($(this).hasClass('expanded')) { + // don't collapse already collapsed + $('#sources-list li ul.panel-collapse').filter('.in').collapse('toggle'); + } else { + // don't expand already expanded + $('#sources-list li ul.panel-collapse').filter(':not(.in)').collapse('toggle'); + } + $('#collapse-all-toggler').toggleClass('expanded'); +}); + +$('#sources-list li ul.panel-collapse').on('shown.bs.collapse hidden.bs.collapse', function() { + $('#collapse-all-toggler').prop('disabled', false); }); // toggle chevron diff --git a/themes/finc/scss/compiled.scss b/themes/finc/scss/compiled.scss index f55b13bfe4395c2700b45b6e5858cf7c2e592327..8725d29985655f6ebb932e745b4c1a954f7f25d7 100644 --- a/themes/finc/scss/compiled.scss +++ b/themes/finc/scss/compiled.scss @@ -3071,6 +3071,14 @@ input { } } +// #17407 change text of button when expanded / collapsed +#collapse-all-toggler:not(.expanded) .text-expanded { + display: none; +} +#collapse-all-toggler.expanded .text-collapsed { + display: none; +} + // AMSL - END .template-dir-browse.template-name-home { diff --git a/themes/finc/templates/amsl/sources-list.phtml b/themes/finc/templates/amsl/sources-list.phtml index 7ff901ba4119464430b34a4ad76fed87483f23f4..371f906684de2b40f1af54bc0857935c9662358e 100644 --- a/themes/finc/templates/amsl/sources-list.phtml +++ b/themes/finc/templates/amsl/sources-list.phtml @@ -1,3 +1,10 @@ +<?php + /* cache fully rendered HTML of THIS template result in AmslController at the first call */ + if (isset($this->rendered_html)) { + echo $this->rendered_html; + return; + } +?> <!-- finc: amsl/sources-list - home --> <?php // Set up page title: @@ -22,10 +29,14 @@ $this->layout()->breadcrumbs .= '</li> <li class="active">' . $this->transEsc('L </form> <p> - <button data-toggle="collapse" class="btn btn-default collapse-all-toggler" href="javascript:void(0)"><?=$this->transEsc('Expand all')?></button> + <button id="collapse-all-toggler" class="btn btn-default" href="javascript:void(0)"> + <span class="text-collapsed"><?=$this->transEsc('Expand all')?></span> + <span class="text-expanded"><?=$this->transEsc('Collapse all')?></span> + </button> </p> <ul id="sources-list"> + <?$itemCount = 0;?> <?php foreach ($this->sources as $label => $source): ?> <?php if (!empty($source)): ?> <li> @@ -36,7 +47,24 @@ $this->layout()->breadcrumbs .= '</li> <li class="active">' . $this->transEsc('L </a> <ul class="panel-collapse collapse" aria-expanded="false"> <?php foreach ($source as $sub_label => $collection): ?> - <li><?=$sub_label?></li> + <li> + <?php if (!empty($collection['href'])): ?> + <a title="<?=$this->transEsc("Search For")?> <?=$sub_label?>" href='<?=$collection["href"]?>' target="_blank"> + <?=$sub_label?> + </a> + <?php else: ?> + <div tabindex="0" aria-label="<?=$this->transEsc("Source Title")?>"> + <?=$sub_label?> + </div> + <?php endif; ?> + <?php if (!empty($collection['desc'])): ?> + <div class="margin-t" tabindex="0" aria-label="<?=$this->transEsc("Description")?>"> + <i class="fa fa-info-circle" aria-hidden="true"></i> + <?=$collection['desc']?> + </div> + <br/> + <?php endif; ?> + </li> <?php endforeach; ?> </ul> </li> @@ -59,4 +87,4 @@ $this->layout()->breadcrumbs .= '</li> <li class="active">' . $this->transEsc('L <?php /* run collapse togglers + introduce a case-insensitive filter that is capable of filtering multiple filtering terms */ echo $this->inlineScript(\Zend\View\Helper\HeadScript::FILE, 'sources-display.js', 'SET'); ?> -<!-- finc: amsl/sources-list - home - END --> +<!-- finc: amsl/sources-list - home - END --> \ No newline at end of file