Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 8
CRAP
0.00% covered (danger)
0.00%
0 / 1
ChannelLoader
0.00% covered (danger)
0.00%
0 / 68
0.00% covered (danger)
0.00%
0 / 8
462
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 performChannelSearch
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
6
 getChannelsFromResults
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
6
 getChannelProviders
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 getChannelProvider
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
12
 getHomeContext
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
42
 getRecordContext
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
6
 getSearchContext
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Channel loader
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2016.
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2,
12 * as published by the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *
23 * @category VuFind
24 * @package  Channels
25 * @author   Demian Katz <demian.katz@villanova.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/development Wiki
28 */
29
30namespace VuFind\ChannelProvider;
31
32use Laminas\Config\Config;
33use VuFind\Cache\Manager as CacheManager;
34use VuFind\ChannelProvider\PluginManager as ChannelManager;
35use VuFind\Record\Loader as RecordLoader;
36use VuFind\Search\Base\Results;
37use VuFind\Search\SearchRunner;
38
39use function in_array;
40
41/**
42 * Channel loader
43 *
44 * @category VuFind
45 * @package  Channels
46 * @author   Demian Katz <demian.katz@villanova.edu>
47 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
48 * @link     https://vufind.org/wiki/development Wiki
49 */
50class ChannelLoader
51{
52    /**
53     * Cache manager
54     *
55     * @var CacheManager
56     */
57    protected $cacheManager;
58
59    /**
60     * Channel manager
61     *
62     * @var ChannelManager
63     */
64    protected $channelManager;
65
66    /**
67     * Channel configuration
68     *
69     * @var Config
70     */
71    protected $config;
72
73    /**
74     * Record loader
75     *
76     * @var RecordLoader
77     */
78    protected $recordLoader;
79
80    /**
81     * Search runner
82     *
83     * @var SearchRunner
84     */
85    protected $searchRunner;
86
87    /**
88     * Current locale (used for caching)
89     *
90     * @var string
91     */
92    protected $locale;
93
94    /**
95     * Constructor
96     *
97     * @param Config         $config Channels configuration
98     * @param CacheManager   $cache  Cache manager
99     * @param ChannelManager $cm     Channel manager
100     * @param SearchRunner   $runner Search runner
101     * @param RecordLoader   $loader Record loader
102     * @param string         $locale Current locale (used for caching)
103     */
104    public function __construct(
105        Config $config,
106        CacheManager $cache,
107        ChannelManager $cm,
108        SearchRunner $runner,
109        RecordLoader $loader,
110        string $locale = ''
111    ) {
112        $this->config = $config;
113        $this->cacheManager = $cache;
114        $this->channelManager = $cm;
115        $this->searchRunner = $runner;
116        $this->recordLoader = $loader;
117        $this->locale = $locale;
118    }
119
120    /**
121     * Get a search results object configured by channel providers.
122     *
123     * @param array  $searchRequest Search request parameters
124     * @param array  $providers     Array of channel providers
125     * @param string $source        Backend to use
126     *
127     * @return Results
128     */
129    protected function performChannelSearch($searchRequest, $providers, $source)
130    {
131        // Perform search and configure providers:
132        $callback = function ($runner, $params) use ($providers) {
133            foreach ($providers as $provider) {
134                $provider->configureSearchParams($params);
135            }
136        };
137        return $this->searchRunner->run($searchRequest, $source, $callback);
138    }
139
140    /**
141     * Get channel details using an array of providers and a populated search
142     * results object.
143     *
144     * @param array   $providers Array of channel providers
145     * @param Results $results   Search results object from performChannelSearch
146     * @param string  $token     Optional channel token
147     *
148     * @return array
149     */
150    protected function getChannelsFromResults($providers, Results $results, $token)
151    {
152        // Collect details:
153        $channels = [];
154        foreach ($providers as $provider) {
155            $channels = array_merge(
156                $channels,
157                $provider->getFromSearch($results, $token)
158            );
159        }
160        return $channels;
161    }
162
163    /**
164     * Get an array of channel providers matching the provided IDs (or just one,
165     * if the channelProvider GET parameter is set).
166     *
167     * @param string $source        Search backend ID
168     * @param array  $configSection Configuration section to load ID list from
169     * @param string $activeId      Currently selected channel ID (if any; used
170     * when making an AJAX request for a single additional channel)
171     *
172     * @return array
173     */
174    protected function getChannelProviders($source, $configSection, $activeId = null)
175    {
176        $providerIds = isset($this->config->{"source.$source"}->$configSection)
177            ? $this->config->{"source.$source"}->$configSection->toArray() : [];
178        $finalIds = (!empty($activeId) && in_array($activeId, $providerIds))
179            ? [$activeId] : $providerIds;
180        return array_map([$this, 'getChannelProvider'], $finalIds);
181    }
182
183    /**
184     * Convenience method to retrieve a channel provider.
185     *
186     * @param string $providerId Channel provider name and optional config
187     * (colon-delimited)
188     *
189     * @return ChannelProviderInterface
190     */
191    protected function getChannelProvider($providerId)
192    {
193        // The provider ID consists of a service name and an optional config
194        // section -- break out the relevant parts:
195        [$serviceName, $configSection] = explode(':', $providerId . ':');
196
197        // Load configuration, using default value if necessary:
198        if (empty($configSection)) {
199            $configSection = "provider.$serviceName";
200        }
201        $options = isset($this->config->{$configSection})
202            ? $this->config->{$configSection}->toArray() : [];
203
204        // Load the service, and configure appropriately:
205        $provider = $this->channelManager->get($serviceName);
206        $provider->setProviderId($providerId);
207        $provider->setOptions($options);
208        return $provider;
209    }
210
211    /**
212     * Generates static front page of channels.
213     *
214     * @param string $token         Channel token (optional, used for AJAX fetching)
215     * @param string $activeChannel Channel being requested (optional, used w/ token)
216     * @param string $activeSource  Search backend to use (null to use configured
217     * default).
218     *
219     * @return array
220     */
221    public function getHomeContext(
222        $token = null,
223        $activeChannel = null,
224        $activeSource = null
225    ) {
226        // Load appropriate channel objects:
227        $defaultSource = $this->config->General->default_home_source
228            ?? DEFAULT_SEARCH_BACKEND;
229        $source = $activeSource ?? $defaultSource;
230        $providers = $this->getChannelProviders($source, 'home', $activeChannel);
231
232        // Set up the cache, if appropriate:
233        if ($this->config->General->cache_home_channels ?? false) {
234            $providerIds = array_map('get_class', $providers);
235            $parts = [implode(',', $providerIds), $source, $token, $this->locale];
236            $cacheKey = md5(implode('-', $parts));
237            $cache = $this->cacheManager->getCache('object', 'homeChannels');
238        } else {
239            $cacheKey = false;
240            $cache = null;
241        }
242
243        // Fetch channel data from cache, or populate cache if necessary:
244        if (!($channels = $cacheKey ? $cache->getItem($cacheKey) : false)) {
245            $searchParams = [];
246            if (isset($this->config->General->default_home_search)) {
247                $searchParams['lookfor']
248                    = $this->config->General->default_home_search;
249            }
250            $results = $this
251                ->performChannelSearch($searchParams, $providers, $source);
252            $channels = $this->getChannelsFromResults($providers, $results, $token);
253            if ($cacheKey) {
254                $cache->setItem($cacheKey, $channels);
255            }
256        }
257
258        // Return context array:
259        return compact('token', 'channels');
260    }
261
262    /**
263     * Generates channels for a record.
264     *
265     * @param string $recordId      Record ID to load
266     * @param string $token         Channel token (optional, used for AJAX fetching)
267     * @param string $activeChannel Channel being requested (optional, used w/ token)
268     * @param string $source        Search backend to use
269     *
270     * @return array
271     */
272    public function getRecordContext(
273        $recordId,
274        $token = null,
275        $activeChannel = null,
276        $source = DEFAULT_SEARCH_BACKEND
277    ) {
278        // Load record:
279        $driver = $this->recordLoader->load($recordId, $source);
280
281        // Load appropriate channel objects:
282        $providers = $this->getChannelProviders($source, 'record', $activeChannel);
283
284        // Collect details:
285        $channels = [];
286        foreach ($providers as $provider) {
287            $channels = array_merge(
288                $channels,
289                $provider->getFromRecord($driver, $token)
290            );
291        }
292
293        // Return context array:
294        return compact('driver', 'channels', 'token');
295    }
296
297    /**
298     * Generates channels for a search.
299     *
300     * @param array  $searchRequest Request parameters
301     * @param string $token         Channel token (optional, used for AJAX fetching)
302     * @param string $activeChannel Channel being requested (optional, used w/ token)
303     * @param string $source        Search backend to use
304     *
305     * @return array
306     */
307    public function getSearchContext(
308        $searchRequest = [],
309        $token = null,
310        $activeChannel = null,
311        $source = DEFAULT_SEARCH_BACKEND
312    ) {
313        // Load appropriate channel objects:
314        $providers = $this->getChannelProviders($source, 'search', $activeChannel);
315
316        // Perform search:
317        $results = $this->performChannelSearch($searchRequest, $providers, $source);
318
319        // Collect details:
320        $lookfor = $searchRequest['lookfor'] ?? null;
321        $channels = $this->getChannelsFromResults($providers, $results, $token);
322
323        // Return context array:
324        return compact('results', 'lookfor', 'channels', 'token');
325    }
326}