Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 172
0.00% covered (danger)
0.00%
0 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
TagsController
0.00% covered (danger)
0.00%
0 / 172
0.00% covered (danger)
0.00%
0 / 13
1482
0.00% covered (danger)
0.00%
0 / 1
 getParam
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 homeAction
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 manageAction
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 listAction
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
 deleteAction
0.00% covered (danger)
0.00%
0 / 40
0.00% covered (danger)
0.00%
0 / 1
156
 getConfirmDeleteMessages
0.00% covered (danger)
0.00%
0 / 38
0.00% covered (danger)
0.00%
0 / 1
110
 confirmTagsDelete
0.00% covered (danger)
0.00%
0 / 18
0.00% covered (danger)
0.00%
0 / 1
2
 confirmTagsDeleteByFilter
0.00% covered (danger)
0.00%
0 / 22
0.00% covered (danger)
0.00%
0 / 1
2
 getUniqueResources
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getUniqueTags
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 getUniqueUsers
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 convertFilter
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
20
 deleteResourceTagsByFilter
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4 * Admin Tag Controller
5 *
6 * PHP version 8
7 *
8 * Copyright (C) Villanova University 2010.
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  Controller
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 VuFindAdmin\Controller;
31
32use VuFind\Db\Service\ResourceServiceInterface;
33use VuFind\Db\Service\ResourceTagsServiceInterface;
34use VuFind\Db\Service\TagServiceInterface;
35use VuFind\Db\Service\UserServiceInterface;
36use VuFind\Tags\TagsService;
37
38use function count;
39use function intval;
40use function is_array;
41
42/**
43 * Class controls distribution of tags and resource tags.
44 *
45 * @category VuFind
46 * @package  Controller
47 * @author   Demian Katz <demian.katz@villanova.edu>
48 * @license  http://opensource.org/licenses/gpl-2.0.php GNU General Public License
49 * @link     https://vufind.org Main Site
50 */
51class TagsController extends AbstractAdmin
52{
53    /**
54     * Params
55     *
56     * @var array
57     */
58    protected $params;
59
60    /**
61     * Get the url parameters
62     *
63     * @param string $param          A key to check the url params for
64     * @param bool   $prioritizePost If true, check the POST params first
65     * @param mixed  $default        Default value if no value found
66     *
67     * @return string
68     */
69    protected function getParam($param, $prioritizePost = true, $default = null)
70    {
71        $primary = $prioritizePost ? 'fromPost' : 'fromQuery';
72        $secondary = $prioritizePost ? 'fromQuery' : 'fromPost';
73        return $this->params()->$primary($param)
74            ?? $this->params()->$secondary($param, $default);
75    }
76
77    /**
78     * Tag Details
79     *
80     * @return \Laminas\View\Model\ViewModel
81     */
82    public function homeAction()
83    {
84        $view = $this->createViewModel();
85        $view->setTemplate('admin/tags/home');
86        $view->statistics = $this->serviceLocator->get(TagsService::class)->getStatistics(true);
87        return $view;
88    }
89
90    /**
91     * Manage Tags
92     *
93     * @return \Laminas\View\Model\ViewModel
94     */
95    public function manageAction()
96    {
97        $view = $this->createViewModel();
98        $view->setTemplate('admin/tags/manage');
99        $view->type = $this->params()->fromPost('type', null)
100            ?? $this->params()->fromQuery('type', null);
101        $view->uniqueTags = $this->getUniqueTags();
102        $view->uniqueUsers = $this->getUniqueUsers();
103        $view->uniqueResources = $this->getUniqueResources();
104        $view->params = $this->params()->fromQuery();
105        return $view;
106    }
107
108    /**
109     * List Tags
110     *
111     * @return \Laminas\View\Model\ViewModel
112     */
113    public function listAction()
114    {
115        $view = $this->createViewModel();
116        $view->setTemplate('admin/tags/list');
117        $view->uniqueTags = $this->getUniqueTags();
118        $view->uniqueUsers = $this->getUniqueUsers();
119        $view->uniqueResources = $this->getUniqueResources();
120        $page = intval($this->getParam('page', false, '1'));
121        $view->results = $this->serviceLocator->get(TagsService::class)->getResourceTagsPaginator(
122            $this->convertFilter($this->getParam('user_id', false)),
123            $this->convertFilter($this->getParam('resource_id', false)),
124            $this->convertFilter($this->getParam('tag_id', false)),
125            $this->getParam('order', false),
126            $page
127        );
128        $view->params = $this->params()->fromQuery();
129        return $view;
130    }
131
132    /**
133     * Delete Tags
134     *
135     * @return \Laminas\View\Model\ViewModel
136     */
137    public function deleteAction()
138    {
139        $origin = $this->getParam('origin');
140
141        $action = ('list' == $origin) ? 'List' : 'Manage';
142
143        $originUrl = $this->url()->fromRoute('admin/tags', ['action' => $action]);
144        if ($action == 'List') {
145            $originUrl .= '?' . http_build_query(
146                [
147                    'user_id' => $this->getParam('user_id'),
148                    'resource_id' => $this->getParam('resource_id'),
149                    'tag_id' => $this->getParam('tag_id'),
150                ]
151            );
152        }
153        $newUrl = $this->url()->fromRoute('admin/tags', ['action' => 'Delete']);
154
155        $confirm = $this->params()->fromPost('confirm', false);
156
157        // Delete All
158        if (
159            'manage' == $origin
160            || null !== $this->getRequest()->getPost('deleteFilter')
161            || null !== $this->getRequest()->getQuery('deleteFilter')
162        ) {
163            if (false === $confirm) {
164                return $this->confirmTagsDeleteByFilter($originUrl, $newUrl);
165            }
166            $delete = $this->deleteResourceTagsByFilter();
167        } else {
168            // Delete by ID
169            // Fail if we have nothing to delete:
170            $ids = null === $this->getRequest()->getPost('deletePage')
171                ? $this->params()->fromPost('ids')
172                : $this->params()->fromPost('idsAll');
173
174            if (!is_array($ids) || empty($ids)) {
175                $this->flashMessenger()->addMessage('bulk_noitems_advice', 'error');
176                return $this->redirect()->toUrl($originUrl);
177            }
178
179            if (false === $confirm) {
180                return $this->confirmTagsDelete($ids, $originUrl, $newUrl);
181            }
182            $delete = $this->getDbService(ResourceTagsServiceInterface::class)->deleteLinksByResourceTagsIdArray($ids);
183        }
184
185        if (0 == $delete) {
186            $this->flashMessenger()->addMessage('tags_delete_fail', 'error');
187            return $this->redirect()->toUrl($originUrl);
188        }
189
190        // If we got this far, we should clean up orphans:
191        $this->getDbService(TagServiceInterface::class)->deleteOrphanedTags();
192
193        $this->flashMessenger()->addMessage(
194            [
195                'msg' => 'tags_deleted',
196                'tokens' => ['%count%' => $delete],
197            ],
198            'success'
199        );
200        return $this->redirect()->toUrl($originUrl);
201    }
202
203    /**
204     * Get confirmation messages.
205     *
206     * @param int $count Count of tags that are about to be deleted
207     *
208     * @return array
209     */
210    protected function getConfirmDeleteMessages($count)
211    {
212        // Default all messages to "All"; we'll make them more specific as needed:
213        $userMsg = $tagMsg = $resourceMsg = $this->translate('All');
214
215        $userId = intval($this->getParam('user_id'));
216        if ($userId) {
217            $user = $this->getDbService(UserServiceInterface::class)->getUserById($userId);
218            if (!$user) {
219                throw new \Exception("Unexpected error retrieving user $userId");
220            }
221            $userMsg = "{$user->getUsername()} ({$user->getId()})";
222        }
223
224        $tagId = intval($this->getParam('tag_id'));
225        if ($tagId) {
226            $tag = $this->getDbService(TagServiceInterface::class)->getTagById($tagId);
227            if (!$tag) {
228                throw new \Exception("Unexpected error retrieving tag $tagId");
229            }
230            $tagMsg = "{$tag->getTag()} ({$tag->getId()})";
231        }
232
233        $resourceId = intval($this->getParam('resource_id'));
234        if ($resourceId) {
235            $resource = $this->getDbService(ResourceServiceInterface::class)->getResourceById($resourceId);
236            if (!$resource) {
237                throw new \Exception(
238                    "Unexpected error retrieving resource $resourceId"
239                );
240            }
241            $resourceMsg = "{$resource->getTitle()} ({$resource->getId()})";
242        }
243
244        $messages = [
245            [
246                'msg' => 'tag_delete_warning',
247                'tokens' => ['%count%' => $count],
248            ],
249        ];
250        if ($userId || $tagId || $resourceId) {
251            $messages[] = [
252                'msg' => 'tag_delete_filter',
253                'tokens' => [
254                    '%username%' => $userMsg,
255                    '%tag%' => $tagMsg,
256                    '%resource%' => $resourceMsg,
257                ],
258            ];
259        }
260        $messages[] = ['msg' => 'confirm_delete'];
261        return $messages;
262    }
263
264    /**
265     * Confirm Delete by Id
266     *
267     * @param array  $ids       A list of resource tag Ids
268     * @param string $originUrl An origin url
269     * @param string $newUrl    The url of the desired action
270     *
271     * @return mixed
272     */
273    protected function confirmTagsDelete($ids, $originUrl, $newUrl)
274    {
275        $count = count($ids);
276
277        $data = [
278            'data' => [
279                'confirm' => $newUrl,
280                'cancel' => $originUrl,
281                'title' => 'confirm_delete_tags_brief',
282                'messages' => $this->getConfirmDeleteMessages($count),
283                'ids' => $ids,
284                'extras' => [
285                    'origin' => 'list',
286                    'user_id' => $this->getParam('user_id'),
287                    'tag_id' => $this->getParam('tag_id'),
288                    'resource_id' => $this->getParam('resource_id'),
289                    'ids' => $ids,
290                ],
291            ],
292        ];
293
294        return $this->forwardTo('Confirm', 'Confirm', $data);
295    }
296
297    /**
298     * Confirm Tag Delete by Filter
299     *
300     * @param string $originUrl An origin url
301     * @param string $newUrl    The url of the desired action
302     *
303     * @return mixed
304     */
305    protected function confirmTagsDeleteByFilter($originUrl, $newUrl)
306    {
307        $count = $this->serviceLocator->get(TagsService::class)->getResourceTagsPaginator(
308            $this->convertFilter($this->getParam('user_id')),
309            $this->convertFilter($this->getParam('resource_id')),
310            $this->convertFilter($this->getParam('tag_id'))
311        )->getTotalItemCount();
312
313        $data = [
314            'data' => [
315                'confirm' => $newUrl,
316                'cancel' => $originUrl,
317                'title' => 'confirm_delete_tags_brief',
318                'messages' => $this->getConfirmDeleteMessages($count),
319                'extras' => [
320                    'origin' => 'manage',
321                    'type' => $this->getParam('type'),
322                    'user_id' => $this->getParam('user_id'),
323                    'tag_id' => $this->getParam('tag_id'),
324                    'resource_id' => $this->getParam('resource_id'),
325                    'deleteFilter' => $this->getParam('deleteFilter'),
326                ],
327            ],
328        ];
329
330        return $this->forwardTo('Confirm', 'Confirm', $data);
331    }
332
333    /**
334     * Gets a list of unique resources based on the url params
335     *
336     * @return array[]
337     */
338    protected function getUniqueResources(): array
339    {
340        return $this->getDbService(ResourceTagsServiceInterface::class)->getUniqueResources(
341            $this->convertFilter($this->getParam('user_id', false)),
342            $this->convertFilter($this->getParam('resource_id', false)),
343            $this->convertFilter($this->getParam('tag_id', false))
344        );
345    }
346
347    /**
348     * Gets a list of unique tags based on the url params
349     *
350     * @return array[]
351     */
352    protected function getUniqueTags(): array
353    {
354        return $this->serviceLocator->get(TagsService::class)->getUniqueTags(
355            $this->convertFilter($this->getParam('user_id', false)),
356            $this->convertFilter($this->getParam('resource_id', false)),
357            $this->convertFilter($this->getParam('tag_id', false))
358        );
359    }
360
361    /**
362     * Gets a list of unique users based on the url params
363     *
364     * @return array[]
365     */
366    protected function getUniqueUsers(): array
367    {
368        return $this->getDbService(ResourceTagsServiceInterface::class)->getUniqueUsers(
369            $this->convertFilter($this->getParam('user_id', false)),
370            $this->convertFilter($this->getParam('resource_id', false)),
371            $this->convertFilter($this->getParam('tag_id', false))
372        );
373    }
374
375    /**
376     * Converts empty params and "ALL" to null
377     *
378     * @param string $value A parameter to check
379     *
380     * @return string|null A modified parameter
381     */
382    protected function convertFilter($value)
383    {
384        return ('ALL' !== $value && '' !== $value && null !== $value)
385            ? $value : null;
386    }
387
388    /**
389     * Delete tags based on filter settings.
390     *
391     * @return int Number of IDs deleted
392     */
393    protected function deleteResourceTagsByFilter(): int
394    {
395        return $this->getDbService(ResourceTagsServiceInterface::class)->deleteResourceTags(
396            $this->convertFilter($this->getParam('user_id')),
397            $this->convertFilter($this->getParam('resource_id')),
398            $this->convertFilter($this->getParam('tag_id'))
399        );
400    }
401}