diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index 84f123e8d96cd46123c20afe106790c34106dc8d..5d2e347e77b16cb35c22187b86c46af35936840e 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -112,7 +112,7 @@ $config = [ 'VuFind\Controller\BrowseController' => 'VuFind\Controller\AbstractBaseWithConfigFactory', 'VuFind\Controller\BrowZineController' => 'VuFind\Controller\AbstractBaseFactory', 'VuFind\Controller\CartController' => 'VuFind\Controller\CartControllerFactory', - 'VuFind\Controller\ChannelsController' => 'VuFind\Controller\AbstractBaseFactory', + 'VuFind\Controller\ChannelsController' => 'VuFind\Controller\ChannelsControllerFactory', 'VuFind\Controller\CollectionController' => 'VuFind\Controller\AbstractBaseWithConfigFactory', 'VuFind\Controller\CollectionsController' => 'VuFind\Controller\AbstractBaseWithConfigFactory', 'VuFind\Controller\CombinedController' => 'VuFind\Controller\AbstractBaseFactory', @@ -288,6 +288,7 @@ $config = [ 'VuFind\Autocomplete\Suggester' => 'VuFind\Autocomplete\SuggesterFactory', 'VuFind\Cache\Manager' => 'VuFind\Cache\ManagerFactory', 'VuFind\Cart' => 'VuFind\CartFactory', + 'VuFind\ChannelProvider\ChannelLoader' => 'VuFind\ChannelProvider\ChannelLoaderFactory', 'VuFind\ChannelProvider\PluginManager' => 'VuFind\ServiceManager\AbstractPluginManagerFactory', 'VuFind\Config\AccountCapabilities' => 'VuFind\Config\AccountCapabilitiesFactory', 'VuFind\Config\PluginManager' => 'VuFind\Config\PluginManagerFactory', diff --git a/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php new file mode 100644 index 0000000000000000000000000000000000000000..df9f225d300ac995b8552edef0a96e2f0dc46d0d --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoader.php @@ -0,0 +1,291 @@ +<?php +/** + * Channel loader + * + * PHP version 7 + * + * Copyright (C) Villanova University 2016. + * + * 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 Channels + * @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\ChannelProvider; + +use VuFind\Cache\Manager as CacheManager; +use VuFind\ChannelProvider\PluginManager as ChannelManager; +use VuFind\Record\Loader as RecordLoader; +use VuFind\Search\Base\Results; +use VuFind\Search\SearchRunner; +use Zend\Config\Config; + +/** + * Channel loader + * + * @category VuFind + * @package Channels + * @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 ChannelLoader +{ + /** + * Cache manager + * + * @var CacheManager + */ + protected $cacheManager; + + /** + * Channel manager + * + * @var ChannelManager + */ + protected $channelManager; + + /** + * Channel configuration + * + * @var Config + */ + protected $config; + + /** + * Record loader + * + * @var RecordLoader + */ + protected $recordLoader; + + /** + * Search runner + * + * @var SearchRunner + */ + protected $searchRunner; + + /** + * Constructor + * + * @param Config $config Channels configuration + * @param CacheManager $cache Cache manager + * @param ChannelManager $cm Channel manager + * @param SearchRunner $runner Search runner + * @param RecordLoader $loader Record loader + */ + public function __construct(Config $config, CacheManager $cache, + ChannelManager $cm, SearchRunner $runner, RecordLoader $loader + ) { + $this->config = $config; + $this->cacheManager = $cache; + $this->channelManager = $cm; + $this->searchRunner = $runner; + $this->recordLoader = $loader; + } + + /** + * Get a search results object configured by channel providers. + * + * @param array $searchRequest Search request parameters + * @param array $providers Array of channel providers + * @param string $source Backend to use + * + * @return Results + */ + protected function performChannelSearch($searchRequest, $providers, $source) + { + // Perform search and configure providers: + $callback = function ($runner, $params) use ($providers) { + foreach ($providers as $provider) { + $provider->configureSearchParams($params); + } + }; + return $this->searchRunner->run($searchRequest, $source, $callback); + } + + /** + * Get channel details using an array of providers and a populated search + * results object. + * + * @param array $providers Array of channel providers + * @param Results $results Search results object from performChannelSearch + * @param string $token Optional channel token + * + * @return array + */ + protected function getChannelsFromResults($providers, Results $results, $token) + { + // Collect details: + $channels = []; + foreach ($providers as $provider) { + $channels = array_merge( + $channels, $provider->getFromSearch($results, $token) + ); + } + return $channels; + } + + /** + * Get an array of channel providers matching the provided IDs (or just one, + * if the channelProvider GET parameter is set). + * + * @param string $source Search backend ID + * @param array $configSection Configuration section to load ID list from + * @param string $activeId Currently selected channel ID (if any; used + * when making an AJAX request for a single additional channel) + * + * @return array + */ + protected function getChannelProviders($source, $configSection, $activeId = null) + { + $providerIds = isset($this->config->{"source.$source"}->$configSection) + ? $this->config->{"source.$source"}->$configSection->toArray() : []; + $finalIds = (!empty($activeId) && in_array($activeId, $providerIds)) + ? [$activeId] : $providerIds; + return array_map([$this, 'getChannelProvider'], $finalIds); + } + + /** + * Convenience method to retrieve a channel provider. + * + * @param string $providerId Channel provider name and optional config + * (colon-delimited) + * + * @return ChannelProviderInterface + */ + protected function getChannelProvider($providerId) + { + // The provider ID consists of a service name and an optional config + // section -- break out the relevant parts: + list($serviceName, $configSection) = explode(':', $providerId . ':'); + + // Load configuration, using default value if necessary: + if (empty($configSection)) { + $configSection = "provider.$serviceName"; + } + $options = isset($this->config->{$configSection}) + ? $this->config->{$configSection}->toArray() : []; + + // Load the service, and configure appropriately: + $provider = $this->channelManager->get($serviceName); + $provider->setProviderId($providerId); + $provider->setOptions($options); + return $provider; + } + + /** + * Generates static front page of channels. + * + * @param string $token Channel token (optional, used for AJAX fetching) + * @param string $activeChannel Channel being requested (optional, used w/ token) + * @param string $activeSource Search backend to use (null to use configured + * default). + * + * @return array + */ + public function getHomeContext($token = null, $activeChannel = null, + $activeSource = null + ) { + // Load appropriate channel objects: + $defaultSource = $this->config->General->default_home_source + ?? DEFAULT_SEARCH_BACKEND; + $source = $activeSource ?? $defaultSource; + $providers = $this->getChannelProviders($source, 'home', $activeChannel); + + // Set up the cache, if appropriate: + if ($this->config->General->cache_home_channels ?? false) { + $providerIds = array_map('get_class', $providers); + $parts = [implode(',', $providerIds), $source, $token]; + $cacheKey = md5(implode('-', $parts)); + $cache = $this->cacheManager->getCache('object', 'homeChannels'); + } else { + $cacheKey = false; + } + + // Fetch channel data from cache, or populate cache if necessary: + if (!($channels = $cacheKey ? $cache->getItem($cacheKey) : false)) { + $results = $this->performChannelSearch([], $providers, $source); + $channels = $this->getChannelsFromResults($providers, $results, $token); + if ($cacheKey) { + $cache->setItem($cacheKey, $channels); + } + } + + // Return context array: + return compact('token', 'channels'); + } + + /** + * Generates channels for a record. + * + * @param string $recordId Record ID to load + * @param string $token Channel token (optional, used for AJAX fetching) + * @param string $activeChannel Channel being requested (optional, used w/ token) + * @param string $source Search backend to use + * + * @return array + */ + public function getRecordContext($recordId, $token = null, + $activeChannel = null, $source = DEFAULT_SEARCH_BACKEND + ) { + // Load record: + $driver = $this->recordLoader->load($recordId, $source); + + // Load appropriate channel objects: + $providers = $this->getChannelProviders($source, 'record', $activeChannel); + + // Collect details: + $channels = []; + foreach ($providers as $provider) { + $channels = array_merge( + $channels, $provider->getFromRecord($driver, $token) + ); + } + + // Return context array: + return compact('driver', 'channels', 'token'); + } + + /** + * Generates channels for a search. + * + * @param array $searchRequest Request parameters + * @param string $token Channel token (optional, used for AJAX fetching) + * @param string $activeChannel Channel being requested (optional, used w/ token) + * @param string $source Search backend to use + * + * @return array + */ + public function getSearchContext($searchRequest = [], $token = null, + $activeChannel = null, $source = DEFAULT_SEARCH_BACKEND + ) { + // Load appropriate channel objects: + $providers = $this->getChannelProviders($source, 'search', $activeChannel); + + // Perform search: + $results = $this->performChannelSearch($searchRequest, $providers, $source); + + // Collect details: + $lookfor = $searchRequest['lookfor'] ?? null; + $channels = $this->getChannelsFromResults($providers, $results, $token); + + // Return context array: + return compact('results', 'lookfor', 'channels', 'token'); + } +} diff --git a/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..e5204d9d1d1de5e1206ef89d88296db953f5e739 --- /dev/null +++ b/module/VuFind/src/VuFind/ChannelProvider/ChannelLoaderFactory.php @@ -0,0 +1,72 @@ +<?php +/** + * Factory for channel loader. + * + * PHP version 5 + * + * Copyright (C) Villanova University 2018. + * + * 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 Channels + * @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\ChannelProvider; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Factory for channel loader. + * + * @category VuFind + * @package Channels + * @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 ChannelLoaderFactory implements 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 + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if ($options !== null) { + throw new \Exception('Unexpected options sent to factory!'); + } + return new $requestedName( + $container->get('VuFind\Config\PluginManager')->get('channels'), + $container->get('VuFind\Cache\Manager'), + $container->get('VuFind\ChannelProvider\PluginManager'), + $container->get('VuFind\Search\SearchRunner'), + $container->get('VuFind\Record\Loader') + ); + } +} diff --git a/module/VuFind/src/VuFind/Controller/ChannelsController.php b/module/VuFind/src/VuFind/Controller/ChannelsController.php index e29414c0c58939d90ac3ca057728fb85661c10e0..d60077c81cd7ed8bcaf58c52bc58be9bf6900a32 100644 --- a/module/VuFind/src/VuFind/Controller/ChannelsController.php +++ b/module/VuFind/src/VuFind/Controller/ChannelsController.php @@ -27,7 +27,7 @@ */ namespace VuFind\Controller; -use Zend\Config\Config; +use VuFind\ChannelProvider\ChannelLoader; /** * Channels Class @@ -43,31 +43,20 @@ use Zend\Config\Config; class ChannelsController extends AbstractBase { /** - * Retrieve channel information for the Channels/Home page. + * Channel loader * - * @param array $providers Array of channel providers - * @param string $searchClassId Search class ID - * @param string $token Channel token + * @var ChannelLoader + */ + protected $loader; + + /** + * Constructor * - * @return array + * @param ChannelLoader $loader Channel loader */ - protected function getHomeChannels($providers, $searchClassId, $token) + public function __construct(ChannelLoader $loader) { - $callback = function ($runner, $params, $searchClassId) use ($providers) { - foreach ($providers as $provider) { - $provider->configureSearchParams($params); - } - }; - $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); - $results = $runner->run([], $searchClassId, $callback); - - $channels = []; - foreach ($providers as $provider) { - $channels = array_merge( - $channels, $provider->getFromSearch($results, $token) - ); - } - return $channels; + $this->loader = $loader; } /** @@ -77,31 +66,11 @@ class ChannelsController extends AbstractBase */ public function homeAction() { - $config = $this->getConfig('channels'); - $defaultSearchClassId - = $config->General->default_home_source ?? DEFAULT_SEARCH_BACKEND; - $searchClassId = $this->params()->fromQuery('source', $defaultSearchClassId); - $providerIds = isset($config->{"source.$searchClassId"}->home) - ? $config->{"source.$searchClassId"}->home->toArray() : []; - $providers = $this->getChannelProviderArray($providerIds, $config); - + $source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND); + $activeChannel = $this->params()->fromQuery('channelProvider'); $token = $this->params()->fromQuery('channelToken'); - if ($config->General->cache_home_channels ?? false) { - $parts = [implode(',', $providerIds), $searchClassId, $token]; - $cacheKey = md5(implode('-', $parts)); - $cache = $this->serviceLocator->get('VuFind\Cache\Manager') - ->getCache('object', 'homeChannels'); - } else { - $cacheKey = false; - } - $channels = $cacheKey ? $cache->getItem($cacheKey) : false; - if (!$channels) { - $channels = $this->getHomeChannels($providers, $searchClassId, $token); - if ($cacheKey) { - $cache->setItem($cacheKey, $channels); - } - } - return $this->createViewModel(compact('token', 'channels')); + $context = $this->loader->getHomeContext($token, $activeChannel, $source); + return $this->createViewModel($context); } /** @@ -111,25 +80,13 @@ class ChannelsController extends AbstractBase */ public function recordAction() { - $view = $this->createViewModel(); - - $loader = $this->getRecordLoader(); + $recordId = $this->params()->fromQuery('id'); $source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND); - $view->driver = $loader->load($this->params()->fromQuery('id'), $source); - - $config = $this->getConfig('channels'); - $providerIds = isset($config->{"source.$source"}->record) - ? $config->{"source.$source"}->record->toArray() : []; - $view->channels = []; - $view->token = $this->params()->fromQuery('channelToken'); - $providers = $this->getChannelProviderArray($providerIds, $config); - foreach ($providers as $provider) { - $view->channels = array_merge( - $view->channels, - $provider->getFromRecord($view->driver, $view->token) - ); - } - return $view; + $activeChannel = $this->params()->fromQuery('channelProvider'); + $token = $this->params()->fromQuery('channelToken'); + $context = $this->loader + ->getRecordContext($recordId, $token, $activeChannel, $source); + return $this->createViewModel($context); } /** @@ -139,89 +96,14 @@ class ChannelsController extends AbstractBase */ public function searchAction() { - $view = $this->createViewModel(); - - $runner = $this->serviceLocator->get('VuFind\Search\SearchRunner'); - // Send both GET and POST variables to search class: $request = $this->getRequest()->getQuery()->toArray() + $this->getRequest()->getPost()->toArray(); - $searchClassId = $this->params() - ->fromQuery('source', DEFAULT_SEARCH_BACKEND); - - $config = $this->getConfig('channels'); - $providerIds = isset($config->{"source.$searchClassId"}->search) - ? $config->{"source.$searchClassId"}->search->toArray() : []; - $providers = $this->getChannelProviderArray($providerIds, $config); - - $callback = function ($runner, $params, $searchClassId) use ($providers) { - foreach ($providers as $provider) { - $provider->configureSearchParams($params); - } - }; - $view->results = $runner->run($request, $searchClassId, $callback); - - $view->channels = []; - $view->lookfor = $this->params()->fromQuery('lookfor'); - $view->token = $this->params()->fromQuery('channelToken'); - foreach ($providers as $provider) { - $view->channels = array_merge( - $view->channels, - $provider->getFromSearch($view->results, $view->token) - ); - } - return $view; - } - - /** - * Get an array of channel providers matching the provided IDs (or just one, - * if the channelProvider GET parameter is set). - * - * @param array $providerIds Array of IDs to load - * @param Config $config Channel configuration - * - * @return array - */ - protected function getChannelProviderArray($providerIds, $config) - { - $id = $this->params()->fromQuery('channelProvider'); - if (!empty($id) && in_array($id, $providerIds)) { - return [$this->getChannelProvider($id, $config)]; - } - $results = []; - foreach ($providerIds as $id) { - $results[] = $this->getChannelProvider($id, $config); - } - return $results; - } - - /** - * Convenience method to retrieve a channel provider. - * - * @param string $providerId Channel provider name and optional config - * (colon-delimited) - * @param Config $config Channel configuration - * - * @return \VuFind\ChannelProvider\ChannelProviderInterface - */ - protected function getChannelProvider($providerId, Config $config) - { - // The provider ID consists of a service name and an optional config - // section -- break out the relevant parts: - list($serviceName, $configSection) = explode(':', $providerId . ':'); - - // Load configuration, using default value if necessary: - if (empty($configSection)) { - $configSection = "provider.$serviceName"; - } - $options = isset($config->{$configSection}) - ? $config->{$configSection}->toArray() : []; - - // Load the service, and configure appropriately: - $provider = $this->serviceLocator - ->get('VuFind\ChannelProvider\PluginManager')->get($serviceName); - $provider->setProviderId($providerId); - $provider->setOptions($options); - return $provider; + $source = $this->params()->fromQuery('source', DEFAULT_SEARCH_BACKEND); + $activeChannel = $this->params()->fromQuery('channelProvider'); + $token = $this->params()->fromQuery('channelToken'); + $context = $this->loader + ->getSearchContext($request, $token, $activeChannel, $source); + return $this->createViewModel($context); } } diff --git a/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php b/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php new file mode 100644 index 0000000000000000000000000000000000000000..72fb8015b4701bf014c80c70c84d119377424c8b --- /dev/null +++ b/module/VuFind/src/VuFind/Controller/ChannelsControllerFactory.php @@ -0,0 +1,67 @@ +<?php +/** + * Channels controller factory. + * + * PHP version 7 + * + * Copyright (C) Villanova University 2018. + * + * 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 Controller + * @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\Controller; + +use Interop\Container\ContainerInterface; +use Zend\ServiceManager\Factory\FactoryInterface; + +/** + * Channels controller factory. + * + * @category VuFind + * @package Controller + * @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 ChannelsControllerFactory implements 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 + */ + public function __invoke(ContainerInterface $container, $requestedName, + array $options = null + ) { + if (!empty($options)) { + throw new \Exception('Unexpected options sent to factory.'); + } + $loader = $container->get('VuFind\ChannelProvider\ChannelLoader'); + return new $requestedName($loader); + } +}