diff --git a/config/vufind/CollectionTabs.ini b/config/vufind/CollectionTabs.ini new file mode 100644 index 0000000000000000000000000000000000000000..6d9c078964602648efcc2fe09b2a9596f31c31f3 --- /dev/null +++ b/config/vufind/CollectionTabs.ini @@ -0,0 +1,7 @@ +; This file controls the tab display in the Collection view (as opposed to the +; single record view). The structure is identical to RecordTabs.ini; see the +; comments in that file for details on the meanings of the various settings. +[VuFind\RecordDriver\AbstractBase] +tabs[CollectionList] = CollectionList +tabs[HierarchyTree] = CollectionHierarchyTree +defaultTab = null diff --git a/config/vufind/RecordTabs.ini b/config/vufind/RecordTabs.ini new file mode 100644 index 0000000000000000000000000000000000000000..bfd3a2fb57ccdb2390ff5a62012eb3664c0386ef --- /dev/null +++ b/config/vufind/RecordTabs.ini @@ -0,0 +1,98 @@ +; This file controls the display of tabs on the Record page for various types of +; records. Each section name matches a record driver class name, and those settings +; will be used when displaying that type of record. If no settings are found for a +; particular class, its parent classes will be checked in turn; thus, you could set +; up global defaults using a [VuFind\RecordDriver\AbstractBase] section. +; +; Within each section, the following settings are supported: +; +; tabs[X] = Y -- This activates a tab, using "X" to identify that tab in the URL, +; and using a service named "Y" loaded from the RecordTab plugin +; manager. The order of tabs entries controls display order. +; defaultTab -- This matches an "X" value from a tabs setting, and controls which +; tab is active by default. If empty, the global default tab setting +; (defaultRecordTab) from config.ini will be used. +; backgroundLoadedTabs[] -- This repeatable setting can be used to identify tabs +; that should be asynchronously loaded in the background to improve +; performance. Use the "X" value from the tabs setting as the id. +[VuFind\RecordDriver\EDS] +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[Details] = StaffViewArray +defaultTab = null + +[VuFind\RecordDriver\Pazpar2] +tabs[Details] = StaffViewMARC +defaultTab = null + +[VuFind\RecordDriver\Primo] +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[Details] = StaffViewArray +defaultTab = null + +[VuFind\RecordDriver\SolrAuthDefault] +tabs[Details] = StaffViewArray +defaultTab = null + +[VuFind\RecordDriver\SolrAuthMarc] +tabs[Details] = StaffViewMARC +defaultTab = null + +[VuFind\RecordDriver\DefaultRecord] +tabs[Holdings] = HoldingsILS +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +tabs[Map] = Map +tabs[Similar] = SimilarItemsCarousel +tabs[Details] = StaffViewArray +defaultTab = null +;backgroundLoadedTabs[] = UserComments +;backgroundLoadedTabs[] = Details + +[VuFind\RecordDriver\SolrMarc] +tabs[Holdings] = HoldingsILS +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[HierarchyTree] = HierarchyTree +tabs[Map] = Map +tabs[Similar] = SimilarItemsCarousel +tabs[Details] = StaffViewMARC +defaultTab = null + +[VuFind\RecordDriver\Summon] +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Preview] = preview +tabs[Details] = StaffViewArray +defaultTab = null + +[VuFind\RecordDriver\WorldCat] +tabs[Holdings] = HoldingsWorldCat +tabs[Description] = Description +tabs[TOC] = TOC +tabs[UserComments] = UserComments +tabs[Reviews] = Reviews +tabs[Excerpt] = Excerpt +tabs[Details] = StaffViewMARC +defaultTab = null diff --git a/config/vufind/config.ini b/config/vufind/config.ini index 0aaed3028081061a593ac70f159cf28ed6e69a02..19f3bac01e45329a674de1aa16e84f8cbf2c62ca 100644 --- a/config/vufind/config.ini +++ b/config/vufind/config.ini @@ -97,7 +97,7 @@ defaultLoggedInModule = MyResearch ; The route VuFind will send users to following a log out operation. Set to false ; or omit to attempt to retain the user's current context after log out. ;logOutRoute = home -; This tab will show by default when a record is viewed: +; Default tab to display when a record is viewed (see also RecordTabs.ini): defaultRecordTab = Holdings ; Hide the holdings tab if no holdings are available from the ILS; note that this ; feature requires your ILS driver to support the hasHoldings() method. @@ -1594,7 +1594,8 @@ HMACkey = mySuperSecretValue ; link to the respective collections page rather than the record page ; (default = false). ;collections = true -; Control default tab of Collection view (default = CollectionList) +; Control default tab of Collection view (default = CollectionList); see also +; CollectionTabs.ini. ;defaultTab = CollectionList ; This controls where data is retrieved from to build the Collections/Home page. ; It can be set to Index (use the Solr index) or Alphabetic (use the AlphaBrowse diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index e00b93c159d4441e12eb3fb4b724bbf0425ce908..da276254db64b0fff45b65cca51c430b0ec18fab 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -371,6 +371,7 @@ $config = [ 'VuFind\Record\Router' => 'VuFind\Record\RouterFactory', 'VuFind\RecordDriver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\RecordTab\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', + 'VuFind\RecordTab\TabManager' => 'VuFind\RecordTab\TabManagerFactory', 'VuFind\Related\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Resolver\Driver\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Role\PermissionManager' => 'VuFind\Role\PermissionManagerFactory', @@ -538,110 +539,6 @@ $config = [ 'search_results' => [ /* See VuFind\Search\Results\PluginManager for defaults */ ], 'session' => [ /* see VuFind\Session\PluginManager for defaults */ ], ], - // This section behaves just like recorddriver_tabs below, but is used for - // the collection module instead of the standard record view. - 'recorddriver_collection_tabs' => [ - 'VuFind\RecordDriver\AbstractBase' => [ - 'tabs' => [ - 'CollectionList' => 'CollectionList', - 'HierarchyTree' => 'CollectionHierarchyTree', - ], - 'defaultTab' => null, - ], - ], - // This section controls which tabs are used for which record driver classes. - // Each sub-array is a map from a tab name (as used in a record URL) to a tab - // service (found in recordtab plugin manager settings above). If a - // particular record driver is not defined here, it will inherit - // configuration from a configured parent class. The defaultTab setting may - // be used to specify the default active tab; if null, the value from the - // relevant .ini file will be used. You can also specify which tabs are - // loaded in the background when arriving at a record tabs view with - // backgroundLoadedTabs as a list of tab indexes. - 'recorddriver_tabs' => [ - 'VuFind\RecordDriver\EDS' => [ - 'tabs' => [ - 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\Pazpar2' => [ - 'tabs' => [ - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\Primo' => [ - 'tabs' => [ - 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\SolrAuthDefault' => [ - 'tabs' => [ - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\SolrAuthMarc' => [ - 'tabs' => [ - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\DefaultRecord' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsILS', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', - 'Similar' => 'SimilarItemsCarousel', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - // 'backgroundLoadedTabs' => ['UserComments', 'Details'] - ], - 'VuFind\RecordDriver\SolrMarc' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsILS', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'HierarchyTree' => 'HierarchyTree', 'Map' => 'Map', - 'Similar' => 'SimilarItemsCarousel', - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\Summon' => [ - 'tabs' => [ - 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Preview' => 'preview', - 'Details' => 'StaffViewArray', - ], - 'defaultTab' => null, - ], - 'VuFind\RecordDriver\WorldCat' => [ - 'tabs' => [ - 'Holdings' => 'HoldingsWorldCat', 'Description' => 'Description', - 'TOC' => 'TOC', 'UserComments' => 'UserComments', - 'Reviews' => 'Reviews', 'Excerpt' => 'Excerpt', - 'Details' => 'StaffViewMARC', - ], - 'defaultTab' => null, - ], - ], ], // Authorization configuration: 'zfc_rbac' => [ diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php index e8c4a1a866d2b3938fa27ae90d9c570c638b9850..f30d23363b61baf455a42bee72e055d27aa45cd7 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetails.php @@ -28,7 +28,7 @@ namespace VuFind\AjaxHandler; use VuFind\Record\Loader; -use VuFind\RecordTab\PluginManager as TabManager; +use VuFind\RecordTab\TabManager; use Zend\Http\PhpEnvironment\Request; use Zend\Mvc\Controller\Plugin\Params; use Zend\View\Renderer\RendererInterface; @@ -72,7 +72,7 @@ class GetRecordDetails extends AbstractBase * * @var TabManager */ - protected $pluginManager; + protected $tabManager; /** * View renderer @@ -87,16 +87,16 @@ class GetRecordDetails extends AbstractBase * @param array $config ZF configuration * @param Request $request HTTP request * @param Loader $loader Record loader - * @param TabManager $pm RecordTab plugin manager + * @param TabManager $tm Record Tab manager * @param RendererInterface $renderer Renderer */ public function __construct(array $config, Request $request, Loader $loader, - TabManager $pm, RendererInterface $renderer + TabManager $tm, RendererInterface $renderer ) { $this->config = $config; $this->request = $request; $this->recordLoader = $loader; - $this->pluginManager = $pm; + $this->tabManager = $tm; $this->renderer = $renderer; } @@ -115,11 +115,8 @@ class GetRecordDetails extends AbstractBase '/\W/', '', trim(strtolower($params->fromQuery('type'))) ); - $details = $this->pluginManager->getTabDetailsForRecord( - $driver, - $this->config['vufind']['recorddriver_tabs'], - $this->request, - 'Information' + $details = $this->tabManager->getTabDetailsForRecord( + $driver, $this->request, 'Information' ); $html = $this->renderer->render( @@ -128,9 +125,8 @@ class GetRecordDetails extends AbstractBase 'defaultTab' => $details['default'], 'driver' => $driver, 'tabs' => $details['tabs'], - 'backgroundTabs' => $this->pluginManager->getBackgroundTabNames( - $driver, $this->config['vufind']['recorddriver_tabs'] - ) + 'backgroundTabs' => $this->tabManager + ->getBackgroundTabNames($driver), ] ); return $this->formatResponse(compact('html')); diff --git a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php index 361884e127fbdad5ea72ce0fb4f2fb4bdeeaf817..8730340ed54abb6095cd599f28e85d0464a5523d 100644 --- a/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php +++ b/module/VuFind/src/VuFind/AjaxHandler/GetRecordDetailsFactory.php @@ -67,7 +67,7 @@ class GetRecordDetailsFactory $container->get('Config'), $container->get('Request'), $container->get(\VuFind\Record\Loader::class), - $container->get(\VuFind\RecordTab\PluginManager::class), + $container->get(\VuFind\RecordTab\TabManager::class), $container->get('ViewRenderer') ); } diff --git a/module/VuFind/src/VuFind/Controller/AbstractBase.php b/module/VuFind/src/VuFind/Controller/AbstractBase.php index fdc729f9a73ece7dfccbfbca928498d84034b331..c7024ba7d54f726da972353cd7bf7262dbb9a4f6 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractBase.php +++ b/module/VuFind/src/VuFind/Controller/AbstractBase.php @@ -673,11 +673,10 @@ class AbstractBase extends AbstractActionController /** * Get the tab configuration for this controller. * - * @return array + * @return \VuFind\RecordTab\TabManager */ - protected function getRecordTabConfig() + protected function getRecordTabManager() { - $cfg = $this->serviceLocator->get('Config'); - return $cfg['vufind']['recorddriver_tabs']; + return $this->serviceLocator->get(\VuFind\RecordTab\TabManager::class); } } diff --git a/module/VuFind/src/VuFind/Controller/AbstractRecord.php b/module/VuFind/src/VuFind/Controller/AbstractRecord.php index 3eb73720f2b7540ca57fe0d04ccc317ee4159671..5a5bd02cd5cc532b4f877fa3708afb5fdc19a11f 100644 --- a/module/VuFind/src/VuFind/Controller/AbstractRecord.php +++ b/module/VuFind/src/VuFind/Controller/AbstractRecord.php @@ -647,16 +647,12 @@ class AbstractRecord extends AbstractBase { $driver = $this->loadRecord(); $request = $this->getRequest(); - $rtpm = $this->serviceLocator->get(\VuFind\RecordTab\PluginManager::class); - $details = $rtpm->getTabDetailsForRecord( - $driver, $this->getRecordTabConfig(), $request, - $this->fallbackDefaultTab - ); + $manager = $this->getRecordTabManager(); + $details = $manager + ->getTabDetailsForRecord($driver, $request, $this->fallbackDefaultTab); $this->allTabs = $details['tabs']; $this->defaultTab = $details['default'] ? $details['default'] : false; - $this->backgroundTabs = $rtpm->getBackgroundTabNames( - $driver, $this->getRecordTabConfig() - ); + $this->backgroundTabs = $manager->getBackgroundTabNames($driver); } /** diff --git a/module/VuFind/src/VuFind/Controller/AuthorityController.php b/module/VuFind/src/VuFind/Controller/AuthorityController.php index 4491051d9fb42b998df4630b1805ca70a7e26e8e..d803f88fbfa4d558e5cf85faaabc87f4a2a1ab56 100644 --- a/module/VuFind/src/VuFind/Controller/AuthorityController.php +++ b/module/VuFind/src/VuFind/Controller/AuthorityController.php @@ -79,9 +79,7 @@ class AuthorityController extends AbstractSearch $driver = $this->serviceLocator->get(\VuFind\Record\Loader::class) ->load($id, 'SolrAuth'); $request = $this->getRequest(); - $tabs = $this->serviceLocator - ->get(\VuFind\RecordTab\PluginManager::class) - ->getTabsForRecord($driver, $this->getRecordTabConfig(), $request); + $tabs = $this->getRecordTabManager()->getTabsForRecord($driver, $request); return $this->createViewModel(['driver' => $driver, 'tabs' => $tabs]); } diff --git a/module/VuFind/src/VuFind/Controller/CollectionController.php b/module/VuFind/src/VuFind/Controller/CollectionController.php index 959f1ef839f9c121686fc10cd5549504afb95579..39b32670b6982968ebbdbb5ac2197e84b9ccc2ed 100644 --- a/module/VuFind/src/VuFind/Controller/CollectionController.php +++ b/module/VuFind/src/VuFind/Controller/CollectionController.php @@ -61,12 +61,13 @@ class CollectionController extends AbstractRecord /** * Get the tab configuration for this controller. * - * @return array + * @return \VuFind\RecordTab\TabManager */ - protected function getRecordTabConfig() + protected function getRecordTabManager() { - $cfg = $this->serviceLocator->get('Config'); - return $cfg['vufind']['recorddriver_collection_tabs']; + $manager = parent::getRecordTabManager(); + $manager->setContext('collection'); + return $manager; } /** diff --git a/module/VuFind/src/VuFind/RecordTab/PluginManager.php b/module/VuFind/src/VuFind/RecordTab/PluginManager.php index 215f77130a389cfcfbc51af118c6b303d6c980dc..920541c56bab7fccabc5765c8baabfcdfa97aae5 100644 --- a/module/VuFind/src/VuFind/RecordTab/PluginManager.php +++ b/module/VuFind/src/VuFind/RecordTab/PluginManager.php @@ -27,7 +27,6 @@ */ namespace VuFind\RecordTab; -use VuFind\RecordDriver\AbstractBase as AbstractRecordDriver; use Zend\ServiceManager\Factory\InvokableFactory; /** @@ -104,35 +103,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager parent::__construct($configOrContainerInstance, $v3config); } - /** - * Load the specified key from the configuration array using the best - * available match to the class of the provided driver. Return the default - * value if no match is found. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (map of - * driver class => tab configuration) - * @param string $setting Key to load from configuration - * @param string $default Default to use if no setting found - * - * @return mixed - */ - protected function getConfigByClass(AbstractRecordDriver $driver, - array $config, $setting, $default - ) { - // Get the current record driver's class name, then start a loop - // in case we need to use a parent class' name to find the appropriate - // setting. - $className = get_class($driver); - do { - if (isset($config[$className][$setting])) { - return $config[$className][$setting]; - } - } while ($className = get_parent_class($className)); - // No setting found... - return $default; - } - /** * Return the name of the base class or interface that plug-ins must conform * to. @@ -143,125 +113,4 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager { return TabInterface::class; } - - /** - * Get an array of service names by looking up the provided record driver in - * the provided tab configuration array. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (associative array - * including 'tabs' array mapping driver class => tab service name) - * - * @return array - */ - protected function getTabServiceNames(AbstractRecordDriver $driver, - array $config - ) { - return $this->getConfigByClass($driver, $config, 'tabs', []); - } - - /** - * Get an array of tabs names configured to load via AJAX in the background - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (associative array - * including 'tabs' array mapping driver class => tab service name) - * - * @return array - */ - public function getBackgroundTabNames(AbstractRecordDriver $driver, - array $config - ) { - return $this->getConfigByClass($driver, $config, 'backgroundLoadedTabs', []); - } - - /** - * Get a default tab by looking up the provided record driver in the tab - * configuration array. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (map of - * driver class => tab configuration) - * @param array $tabs Details on available tabs (returned - * from getTabsForRecord()). - * @param string $fallback Fallback to use if no tab specified - * or matched. - * - * @return string - */ - public function getDefaultTabForRecord(AbstractRecordDriver $driver, - array $config, array $tabs, $fallback = null - ) { - // Load default from module configuration: - $default = $this->getConfigByClass($driver, $config, 'defaultTab', null); - - // Missing/invalid record driver configuration? Fall back to provided - // default: - if ((!$default || !isset($tabs[$default])) && isset($tabs[$fallback])) { - $default = $fallback; - } - - // Is configured tab still invalid? If so, pick first existing tab: - if ((!$default || !isset($tabs[$default])) && !empty($tabs)) { - $keys = array_keys($tabs); - $default = $keys[0]; - } - - return $default; - } - - /** - * Convenience method to load tab information, including default, in a - * single pass. Returns an associative array with 'tabs' and 'default' keys. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (map of - * driver class => tab configuration) - * @param \Zend\Http\Request $request User request (optional) - * @param string $fallback Fallback default tab to use if no - * tab specified or matched. - * - * @return array - */ - public function getTabDetailsForRecord(AbstractRecordDriver $driver, - array $config, $request = null, $fallback = null - ) { - $tabs = $this->getTabsForRecord($driver, $config, $request); - $default = $this->getDefaultTabForRecord($driver, $config, $tabs, $fallback); - return compact('tabs', 'default'); - } - - /** - * Get an array of valid tabs for the provided record driver. - * - * @param AbstractRecordDriver $driver Record driver - * @param array $config Tab configuration (map of - * driver class => tab configuration) - * @param \Zend\Http\Request $request User request (optional) - * - * @return array service name => tab object - */ - public function getTabsForRecord(AbstractRecordDriver $driver, - array $config, $request = null - ) { - $tabs = []; - foreach ($this->getTabServiceNames($driver, $config) as $tabKey => $svc) { - if (!$this->has($svc)) { - continue; - } - $newTab = $this->get($svc); - if (method_exists($newTab, 'setRecordDriver')) { - $newTab->setRecordDriver($driver); - } - if ($request instanceof \Zend\Http\Request - && method_exists($newTab, 'setRequest') - ) { - $newTab->setRequest($request); - } - if ($newTab->isActive()) { - $tabs[$tabKey] = $newTab; - } - } - return $tabs; - } } diff --git a/module/VuFind/src/VuFind/RecordTab/TabManager.php b/module/VuFind/src/VuFind/RecordTab/TabManager.php new file mode 100644 index 0000000000000000000000000000000000000000..760af483bd17f9ccfa384c950012901edcdedbd8 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/TabManager.php @@ -0,0 +1,283 @@ +<?php +/** + * Record tab manager + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +namespace VuFind\RecordTab; + +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\RecordDriver\AbstractBase as AbstractRecordDriver; + +/** + * Record tab manager + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:record_tabs Wiki + */ +class TabManager +{ + /** + * Settings for different tab contexts. + * + * @var array + */ + protected $contextSettings = [ + 'record' => [ + 'configFile' => 'RecordTabs', + 'legacyConfigSection' => 'recorddriver_tabs', + ], + 'collection' => [ + 'configFile' => 'CollectionTabs', + 'legacyConfigSection' => 'recorddriver_collection_tabs', + ], + ]; + + /** + * Tab configurations + * + * @var array + */ + protected $config = []; + + /** + * Configuration plugin manager + * + * @var ConfigManager + */ + protected $configManager; + + /** + * RecordTab plugin manager + * + * @var PluginManager + */ + protected $pluginManager; + + /** + * Overall framework configuration + * + * @var array + */ + protected $zendConfig; + + /** + * Current active context (defaults to 'record') + * + * @var string + */ + protected $context = 'record'; + + /** + * Constructor + * + * @param PluginManager $pm RecordTab plugin manager + * @param ConfigManager $cm Configuration plugin manager + * @param array $zendConfig Zend Framework configuration + */ + public function __construct(PluginManager $pm, ConfigManager $cm, + $zendConfig = [] + ) { + $this->pluginManager = $pm; + $this->configManager = $cm; + $this->zendConfig = $zendConfig; + + // Initialize default context. + $this->initializeCurrentContext(); + } + + /** + * Set and (if necessary) initialize the context. + * + * @param string $context Context to initialize + * + * @return void + * @throws \Exception + */ + public function setContext($context) + { + if (!in_array($context, array_keys($this->contextSettings))) { + throw new \Exception("Unsupported context: $context"); + } + $this->context = $context; + $this->initializeCurrentContext(); + } + + /** + * Initialize the current context (if not already initialized). + * + * @return void + */ + protected function initializeCurrentContext() + { + if (!isset($this->config[$this->context])) { + $key = $this->contextSettings[$this->context]['legacyConfigSection'] + ?? 'recorddriver_tabs'; + $legacyConfig = $this->zendConfig['vufind'][$key] ?? []; + $iniConfig = $this->configManager->get( + $this->contextSettings[$this->context]['configFile'] + )->toArray(); + $this->config[$this->context] = array_merge($legacyConfig, $iniConfig); + } + } + + /** + * Load the specified key from the configuration array using the best + * available match to the class of the provided driver. Return the default + * value if no match is found. + * + * @param AbstractRecordDriver $driver Record driver + * @param string $setting Key to load from configuration + * @param string $default Default to use if no setting found + * + * @return mixed + */ + protected function getConfigByClass(AbstractRecordDriver $driver, + $setting, $default + ) { + // Get the current record driver's class name, then start a loop + // in case we need to use a parent class' name to find the appropriate + // setting. + $className = get_class($driver); + do { + if (isset($this->config[$this->context][$className][$setting])) { + return $this->config[$this->context][$className][$setting]; + } + } while ($className = get_parent_class($className)); + // No setting found... + return $default; + } + + /** + * Get an array of service names by looking up the provided record driver in + * the provided tab configuration array. + * + * @param AbstractRecordDriver $driver Record driver + * + * @return array + */ + protected function getTabServiceNames(AbstractRecordDriver $driver) + { + return $this->getConfigByClass($driver, 'tabs', []); + } + + /** + * Get an array of tabs names configured to load via AJAX in the background + * + * @param AbstractRecordDriver $driver Record driver + * + * @return array + */ + public function getBackgroundTabNames(AbstractRecordDriver $driver) + { + return $this->getConfigByClass($driver, 'backgroundLoadedTabs', []); + } + + /** + * Get a default tab by looking up the provided record driver in the tab + * configuration array. + * + * @param AbstractRecordDriver $driver Record driver + * @param array $tabs Details on available tabs (returned + * from getTabsForRecord()). + * @param string $fallback Fallback to use if no tab specified + * or matched. + * + * @return string + */ + public function getDefaultTabForRecord(AbstractRecordDriver $driver, + array $tabs, $fallback = null + ) { + // Load default from module configuration: + $default = $this->getConfigByClass($driver, 'defaultTab', null); + + // Missing/invalid record driver configuration? Fall back to provided + // default: + if ((!$default || !isset($tabs[$default])) && isset($tabs[$fallback])) { + $default = $fallback; + } + + // Is configured tab still invalid? If so, pick first existing tab: + if ((!$default || !isset($tabs[$default])) && !empty($tabs)) { + $keys = array_keys($tabs); + $default = $keys[0]; + } + + return $default; + } + + /** + * Convenience method to load tab information, including default, in a + * single pass. Returns an associative array with 'tabs' and 'default' keys. + * + * @param AbstractRecordDriver $driver Record driver + * @param \Zend\Http\Request $request User request (optional) + * @param string $fallback Fallback default tab to use if no + * tab specified or matched. + * + * @return array + */ + public function getTabDetailsForRecord(AbstractRecordDriver $driver, + $request = null, $fallback = null + ) { + $tabs = $this->getTabsForRecord($driver, $request); + $default = $this->getDefaultTabForRecord($driver, $tabs, $fallback); + return compact('tabs', 'default'); + } + + /** + * Get an array of valid tabs for the provided record driver. + * + * @param AbstractRecordDriver $driver Record driver + * @param \Zend\Http\Request $request User request (optional) + * + * @return array service name => tab object + */ + public function getTabsForRecord(AbstractRecordDriver $driver, + $request = null + ) { + $tabs = []; + foreach ($this->getTabServiceNames($driver) as $tabKey => $svc) { + if (!$this->pluginManager->has($svc)) { + continue; + } + $newTab = $this->pluginManager->get($svc); + if (method_exists($newTab, 'setRecordDriver')) { + $newTab->setRecordDriver($driver); + } + if ($request instanceof \Zend\Http\Request + && method_exists($newTab, 'setRequest') + ) { + $newTab->setRequest($request); + } + if ($newTab->isActive()) { + $tabs[$tabKey] = $newTab; + } + } + return $tabs; + } +} diff --git a/module/VuFind/src/VuFind/RecordTab/TabManagerFactory.php b/module/VuFind/src/VuFind/RecordTab/TabManagerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..4f352736889f914b5aebab69761615b2ece5f897 --- /dev/null +++ b/module/VuFind/src/VuFind/RecordTab/TabManagerFactory.php @@ -0,0 +1,71 @@ +<?php +/** + * Factory for building the TabManager. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +namespace VuFind\RecordTab; + +use Interop\Container\ContainerInterface; + +/** + * Factory for building the TabManager. + * + * @category VuFind + * @package RecordTabs + * @author Demian Katz <demian.katz@villanova.edu> + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development Wiki + */ +class TabManagerFactory implements \Zend\ServiceManager\Factory\FactoryInterface +{ + /** + * Create an object + * + * @param ContainerInterface $container Service manager + * @param string $requestedName Service being created + * @param null|array $options Extra options (optional) + * + * @return object + * + * @throws ServiceNotFoundException if unable to resolve the service. + * @throws ServiceNotCreatedException if an exception is raised when + * creating a service. + * @throws ContainerException if any other error occurs + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options passed to factory.'); + } + return new $requestedName( + $container->get(PluginManager::class), + $container->get(\VuFind\Config\PluginManager::class), + $container->get('config') + ); + } +} diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/TabManagerTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/TabManagerTest.php new file mode 100644 index 0000000000000000000000000000000000000000..ff3c31f0e40db8c34fb2bcaaf9e9a00e175fffba --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/RecordTab/TabManagerTest.php @@ -0,0 +1,172 @@ +<?php +/** + * RecordTab Manager Test Class + * + * PHP version 7 + * + * Copyright (C) Villanova University 2019. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\RecordTab; + +use VuFind\Config\PluginManager as ConfigManager; +use VuFind\RecordTab\PluginManager; +use VuFind\RecordTab\TabManager; + +/** + * RecordTab Manager 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/wiki/development:testing:unit_tests Wiki + */ +class TabManagerTest extends \VuFindTest\Unit\TestCase +{ + /** + * Set up a tab manager for testing. + * + * @param PluginManager $pluginManager Plugin manager to use (null for default) + * @param ConfigManager $configManager Config manager to use (null for default) + * @return TabManager + */ + protected function getTabManager(PluginManager $pluginManager = null, + ConfigManager $configManager = null + ) { + $legacyConfig = [ + 'vufind' => [ + 'recorddriver_collection_tabs' => [ + 'VuFind\RecordDriver\AbstractBase' => [ + 'tabs' => [ + 'coll' => 'ection', + ], + 'defaultTab' => null, + ], + ], + 'recorddriver_tabs' => [ + 'VuFind\RecordDriver\AbstractBase' => [ + 'tabs' => [ + 'foo' => 'bar', + ], + 'defaultTab' => null, + ], + ], + ], + ]; + return new TabManager( + $pluginManager ?? $this->getMockPluginManager(), + $configManager ?? $this->getMockConfigManager(), + $legacyConfig + ); + } + + /** + * Build a mock plugin manager. + * + * @return PluginManager + */ + protected function getMockPluginManager() + { + $mockTab = $this->getMockBuilder(\VuFind\RecordTab\StaffViewArray::class) + ->disableOriginalConstructor()->setMethods(['isActive'])->getMock(); + $mockTab->expects($this->any())->method('isActive') + ->will($this->returnValue(true)); + $pm = $this->getMockBuilder(\VuFind\RecordTab\PluginManager::class) + ->disableOriginalConstructor()->getMock(); + $pm->expects($this->any())->method('has') + ->will($this->returnValue(true)); + $pm->expects($this->any())->method('get') + ->will($this->returnValue($mockTab)); + return $pm; + } + + /** + * Build a mock config manager. + * + * @return ConfigManager + */ + protected function getMockConfigManager() + { + $iniConfig = new \Zend\Config\Config( + [ + 'VuFind\RecordDriver\EDS' => [ + 'tabs' => [ + 'xyzzy' => 'yzzyx', + 'zip' => 'line', + ], + 'defaultTab' => 'zip', + 'backgroundLoadedTabs' => ['xyzzy'], + ], + ] + ); + $configManager = $this->getMockBuilder(\VuFind\Config\PluginManager::class) + ->disableOriginalConstructor() + ->setMethods(['has', 'get']) + ->getMock(); + $configManager->expects($this->any())->method('has') + ->will($this->returnValue(true)); + $configManager->expects($this->any())->method('get') + ->will($this->returnValue($iniConfig)); + return $configManager; + } + + /** + * Test that we get the expected tab service names. + * + * @return void + */ + public function testGetTabDetailsForRecord() + { + $tabManager = $this->getTabManager(); + $driver1 = $this->getMockBuilder(\VuFind\RecordDriver\EDS::class) + ->disableOriginalConstructor()->getMock(); + $details1 = $tabManager->getTabDetailsForRecord($driver1); + $this->assertEquals('zip', $details1['default']); + $this->assertEquals(['xyzzy', 'zip'], array_keys($details1['tabs'])); + $driver2 = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) + ->disableOriginalConstructor()->getMock(); + $details2 = $tabManager->getTabDetailsForRecord($driver2); + $this->assertEquals('foo', $details2['default']); + $this->assertEquals(['foo'], array_keys($details2['tabs'])); + // Switch to collection mode to load a different configuration: + $tabManager->setContext('collection'); + $details2b = $tabManager->getTabDetailsForRecord($driver2); + $this->assertEquals('coll', $details2b['default']); + $this->assertEquals(['coll'], array_keys($details2b['tabs'])); + } + + /** + * Test getBackgroundTabNames. + * + * @return void + */ + public function testGetBackgroundTabNames() + { + $tabManager = $this->getTabManager(); + $driver1 = $this->getMockBuilder(\VuFind\RecordDriver\EDS::class) + ->disableOriginalConstructor()->getMock(); + $this->assertEquals(['xyzzy'], $tabManager->getBackgroundTabNames($driver1)); + $driver2 = $this->getMockBuilder(\VuFind\RecordDriver\SolrDefault::class) + ->disableOriginalConstructor()->getMock(); + $this->assertEquals([], $tabManager->getBackgroundTabNames($driver2)); + } +}