Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
85.37% covered (warning)
85.37%
35 / 41
33.33% covered (danger)
33.33%
2 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
ConsortialVuFind
85.37% covered (warning)
85.37%
35 / 41
33.33% covered (danger)
33.33%
2 / 6
17.91
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 setConfig
83.33% covered (warning)
83.33%
15 / 18
0.00% covered (danger)
0.00%
0 / 1
7.23
 init
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 process
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 getResults
91.67% covered (success)
91.67%
11 / 12
0.00% covered (danger)
0.00%
0 / 1
4.01
 getMoreResultsUrl
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
2.03
1<?php
2
3/**
4 * ConsortialVuFind Recommendations Module
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2023.
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  Recommendations
25 * @author   Maccabee Levine <msl321@lehigh.edu>
26 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
27 * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
28 */
29
30namespace VuFind\Recommend;
31
32use Laminas\Config\Config;
33use VuFind\Connection\ExternalVuFind as Connection;
34
35use function intval;
36use function is_callable;
37
38/**
39 * ConsortialVuFind Recommendations Module
40 *
41 * This class searches a separate instance of VuFind via its public API and links to
42 * the record and results pages hosted within that instance. This is intended to
43 * search and link to a consortial catalog, such as ReShare, which uses its own VuFind
44 * instance to display consortium holdings and facilitate borrowing between institutions.
45 *
46 * @category VuFind
47 * @package  Recommendations
48 * @author   Maccabee Levine <msl321@lehigh.edu>
49 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
50 * @link     https://vufind.org/wiki/development:plugins:recommendation_modules Wiki
51 */
52class ConsortialVuFind implements RecommendInterface, \Laminas\Log\LoggerAwareInterface
53{
54    use \VuFind\Log\LoggerAwareTrait;
55
56    /**
57     * Request parameter for the search query
58     *
59     * @var string
60     */
61    protected $requestParam = 'lookfor';
62
63    /**
64     * Number of results to show
65     *
66     * @var int
67     */
68    protected $limit = 5;
69
70    /**
71     * Connection to consortial VuFind API
72     *
73     * @var Connection
74     */
75    protected $connection;
76
77    /**
78     * ConsortialVuFind.ini configuration
79     *
80     * @var Config
81     */
82    protected $config;
83
84    /**
85     * Base URL of a search results page
86     *
87     * @var string
88     */
89    protected $resultsBaseUrl = null;
90
91    /**
92     * Base URL of a record page
93     *
94     * @var string
95     */
96    protected $recordBaseUrl = null;
97
98    /**
99     * Any filters used in the search
100     *
101     * @var array
102     */
103    protected $searchFilters = [];
104
105    /**
106     * Boolean indicating if at least the minimal required configuration is present
107     *
108     * @var bool
109     */
110    protected $hasMinimumConfig = false;
111
112    /**
113     * Query string from the original search results
114     *
115     * @var ?string
116     */
117    protected $queryString = null;
118
119    /**
120     * Constructor
121     *
122     * @param Config     $config     ConsortialVuFind.ini configuration
123     * @param Connection $connection Connection to consortial VuFind API
124     */
125    public function __construct(
126        Config $config,
127        Connection $connection
128    ) {
129        $this->config = $config;
130        $this->connection = $connection;
131    }
132
133    /**
134     * Store the configuration of the recommendation module.
135     *
136     * @param string $settings Settings from searches.ini.
137     *
138     * @return void
139     */
140    public function setConfig($settings)
141    {
142        $settings = explode(':', $settings);
143        $this->requestParam = !empty($settings[0]) ? $settings[0] : $this->requestParam;
144        $limitSetting = intval($settings[1] ?? 0);
145        $this->limit = $limitSetting > 0 ? $limitSetting : $this->limit;
146        $configSectionName = $settings[2] ?? 'ReShare';
147
148        // Read config file
149        $configSection = $this->config->get($configSectionName);
150        if ($configSection) {
151            $this->resultsBaseUrl = $configSection->results_base_url;
152            $this->recordBaseUrl = $configSection->record_base_url;
153            $this->searchFilters = $configSection->filters?->toArray() ?? [];
154
155            // Configure connection
156            $this->connection->setBaseUrl($configSection->api_base_url);
157
158            // Confirm that required configuration is present
159            $this->hasMinimumConfig = $this->resultsBaseUrl
160                && $this->recordBaseUrl
161                && $configSection->api_base_url;
162            if (!$this->hasMinimumConfig) {
163                $this->logError("Required configuration missing in '$configSectionName'
164                    section of ConsortialVuFind.ini.");
165            }
166        } else {
167            $this->logError("'$configSectionName' section not found in ConsortialVuFind.ini.");
168        }
169    }
170
171    /**
172     * Called before the Search Results object performs its main search
173     * (specifically, in response to \VuFind\Search\SearchRunner::EVENT_CONFIGURED).
174     * This method is responsible for setting search parameters needed by the
175     * recommendation module and for reading any existing search parameters that may
176     * be needed.
177     *
178     * @param \VuFind\Search\Base\Params $params  Search parameter object
179     * @param \Laminas\Stdlib\Parameters $request Parameter object representing user
180     * request.
181     *
182     * @return void
183     *
184     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
185     */
186    public function init($params, $request)
187    {
188        // No action needed.
189    }
190
191    /**
192     * Called after the Search Results object has performed its main search. This
193     * may be used to extract necessary information from the Search Results object
194     * or to perform completely unrelated processing.
195     *
196     * @param \VuFind\Search\Base\Results $results Search results object
197     *
198     * @return void
199     */
200    public function process($results)
201    {
202        $query = $results->getParams()->getQuery();
203        if (is_callable([$query, 'getString'])) {
204            $this->queryString = $query->getString();
205        }
206    }
207
208    /**
209     * Get the consortial VuFind instance's search results.
210     *
211     * @return array
212     */
213    public function getResults()
214    {
215        if (!$this->hasMinimumConfig || !$this->queryString) {
216            return [];
217        }
218
219        $results = $this->connection->search(
220            $this->queryString,
221            $this->requestParam,
222            $this->limit,
223            $this->searchFilters
224        );
225        foreach (($results['records'] ?? []) as $i => $record) {
226            $results['records'][$i]['url'] =
227                $this->recordBaseUrl . '/' . urlencode($record['id']);
228        }
229        return $results;
230    }
231
232    /**
233     * Get a URL to the full search results page in the consortial VuFind instance.
234     *
235     * @return string The url
236     */
237    public function getMoreResultsUrl()
238    {
239        $url = $this->resultsBaseUrl
240            . '?' . $this->requestParam . '=' . urlencode($this->queryString);
241        foreach ($this->searchFilters as $filter) {
242            $url .= '&filter[]=' . urlencode($filter);
243        }
244        return $url;
245    }
246}