Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 73
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
Results
0.00% covered (danger)
0.00%
0 / 73
0.00% covered (danger)
0.00%
0 / 6
600
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 getFacetList
0.00% covered (danger)
0.00%
0 / 28
0.00% covered (danger)
0.00%
0 / 1
90
 performSearch
0.00% covered (danger)
0.00%
0 / 36
0.00% covered (danger)
0.00%
0 / 1
90
 getTagFilters
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
2
 getListObject
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 getAllIds
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Favorites aspect of the Search Multi-class (Results)
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2010-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  Search_Favorites
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 Main Site
28 */
29
30namespace VuFind\Search\Favorites;
31
32use LmcRbacMvc\Service\AuthorizationServiceAwareInterface;
33use LmcRbacMvc\Service\AuthorizationServiceAwareTrait;
34use VuFind\Db\Entity\UserEntityInterface;
35use VuFind\Db\Entity\UserListEntityInterface;
36use VuFind\Db\Service\ResourceServiceInterface;
37use VuFind\Db\Service\UserListServiceInterface;
38use VuFind\Exception\ListPermission as ListPermissionException;
39use VuFind\Record\Cache;
40use VuFind\Record\Loader;
41use VuFind\Search\Base\Results as BaseResults;
42use VuFind\Tags\TagsService;
43use VuFindSearch\Service as SearchService;
44
45use function array_slice;
46use function count;
47
48/**
49 * Search Favorites Results
50 *
51 * @category VuFind
52 * @package  Search_Favorites
53 * @author   Demian Katz <demian.katz@villanova.edu>
54 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
55 * @link     https://vufind.org Main Site
56 */
57class Results extends BaseResults implements AuthorizationServiceAwareInterface
58{
59    use AuthorizationServiceAwareTrait;
60
61    /**
62     * Object if user is logged in, null otherwise.
63     *
64     * @var ?UserEntityInterface
65     */
66    protected $user = null;
67
68    /**
69     * Active user list (false if we haven't tried to load yet; null if inapplicable).
70     *
71     * @var UserListEntityInterface|null|false
72     */
73    protected $list = false;
74
75    /**
76     * Facet list
77     *
78     * @var array
79     */
80    protected $facets;
81
82    /**
83     * All ids
84     *
85     * @var array
86     */
87    protected $allIds;
88
89    /**
90     * Constructor
91     *
92     * @param \VuFind\Search\Base\Params $params          Object representing user search parameters
93     * @param SearchService              $searchService   Search service
94     * @param Loader                     $recordLoader    Record loader
95     * @param ResourceServiceInterface   $resourceService Resource database service
96     * @param UserListServiceInterface   $userListService UserList database service
97     * @param TagsService                $tagsService     Tags service
98     */
99    public function __construct(
100        \VuFind\Search\Base\Params $params,
101        SearchService $searchService,
102        Loader $recordLoader,
103        protected ResourceServiceInterface $resourceService,
104        protected UserListServiceInterface $userListService,
105        protected TagsService $tagsService
106    ) {
107        parent::__construct($params, $searchService, $recordLoader);
108    }
109
110    /**
111     * Returns the stored list of facets for the last search
112     *
113     * @param array $filter Array of field => on-screen description listing
114     * all of the desired facet fields; set to null to get all configured values.
115     *
116     * @return array        Facets data arrays
117     */
118    public function getFacetList($filter = null)
119    {
120        // Make sure we have processed the search before proceeding:
121        if (null === $this->results) {
122            $this->performAndProcessSearch();
123        }
124
125        // If there is no filter, we'll use all facets as the filter:
126        if (null === $filter) {
127            $filter = $this->getParams()->getFacetConfig();
128        }
129
130        // Start building the facet list:
131        $retVal = [];
132
133        // Loop through every requested field:
134        $validFields = array_keys($filter);
135        foreach ($validFields as $field) {
136            if (!isset($this->facets[$field])) {
137                $this->facets[$field] = [
138                    'label' => $this->getParams()->getFacetLabel($field),
139                    'list' => [],
140                ];
141                switch ($field) {
142                    case 'tags':
143                        if ($list = $this->getListObject()) {
144                            $tags = $this->tagsService->getUserTagsFromFavorites($list->getUser(), $list);
145                        } else {
146                            $tags = $this->tagsService->getUserTagsFromFavorites($this->user);
147                        }
148                        foreach ($tags as $tag) {
149                            $this->facets[$field]['list'][] = [
150                                'value' => $tag['tag'],
151                                'displayText' => $tag['tag'],
152                                'count' => $tag['cnt'],
153                                'isApplied' => $this->getParams()
154                                    ->hasFilter("$field:" . $tag['tag']),
155                            ];
156                        }
157                        break;
158                }
159            }
160            if (isset($this->facets[$field])) {
161                $retVal[$field] = $this->facets[$field];
162            }
163        }
164        return $retVal;
165    }
166
167    /**
168     * Support method for performAndProcessSearch -- perform a search based on the
169     * parameters passed to the object.
170     *
171     * @return void
172     */
173    protected function performSearch()
174    {
175        $list = $this->getListObject();
176        $this->user = $this->getAuthorizationService()?->getIdentity();
177
178        // Make sure the user and/or list objects make it possible to view
179        // the current result set -- we need to check logged in status and
180        // list permissions.
181        if (!$list && !$this->user) {
182            throw new ListPermissionException(
183                'Cannot retrieve favorites without logged in user.'
184            );
185        }
186        if ($list && !$list->isPublic() && $list->getUser()->getId() !== $this->user?->getId()) {
187            throw new ListPermissionException(
188                $this->translate('list_access_denied')
189            );
190        }
191
192        // How many results were there?
193        $userId = $list ? $list->getUser()->getId() : $this->user->getId();
194        $listId = $list?->getId();
195        // Get results as an array so that we can rewind it:
196        $rawResults = $this->resourceService->getFavorites(
197            $userId,
198            $listId,
199            $this->getTagFilters(),
200            $this->getParams()->getSort(),
201            caseSensitiveTags: $this->tagsService->hasCaseSensitiveTags()
202        );
203        $this->resultTotal = count($rawResults);
204        $this->allIds = array_map(function ($result) {
205            return $result->getSource() . '|' . $result->getRecordId();
206        }, $rawResults);
207
208        // Apply offset and limit if necessary!
209        $limit = $this->getParams()->getLimit();
210        if ($this->resultTotal > $limit) {
211            $rawResults = array_slice($rawResults, $this->getStartRecord() - 1, $limit);
212        }
213
214        // Retrieve record drivers for the selected items.
215        $recordsToRequest = [];
216        foreach ($rawResults as $row) {
217            $recordsToRequest[] = [
218                'id' => $row->getRecordId(), 'source' => $row->getSource(),
219                'extra_fields' => [
220                    'title' => $row->getTitle(),
221                ],
222            ];
223        }
224
225        $this->recordLoader->setCacheContext(Cache::CONTEXT_FAVORITE);
226        $this->results = $this->recordLoader->loadBatch($recordsToRequest, true);
227    }
228
229    /**
230     * Get an array of tags being applied as filters.
231     *
232     * @return array
233     */
234    protected function getTagFilters()
235    {
236        $filters = $this->getParams()->getRawFilters();
237        return $filters['tags'] ?? [];
238    }
239
240    /**
241     * Get the list object associated with the current search (null if no list
242     * selected).
243     *
244     * @return ?UserListEntityInterface
245     */
246    public function getListObject(): ?UserListEntityInterface
247    {
248        // If we haven't previously tried to load a list, do it now:
249        if ($this->list === false) {
250            // Check the filters for a list ID, and load the corresponding object
251            // if one is found:
252            $filters = $this->getParams()->getRawFilters();
253            $listId = $filters['lists'][0] ?? null;
254            $this->list = (null === $listId) ? null : $this->userListService->getUserListById($listId);
255        }
256        return $this->list;
257    }
258
259    /**
260     * Get all ids.
261     *
262     * @return array
263     */
264    public function getAllIds()
265    {
266        return $this->allIds;
267    }
268}