diff --git a/module/VuFind/config/module.config.php b/module/VuFind/config/module.config.php index fe0150d4993..4e22a120087 100644 --- a/module/VuFind/config/module.config.php +++ b/module/VuFind/config/module.config.php @@ -563,6 +563,7 @@ 'result_cache' => 'filesystem', 'metadata_cache' => 'filesystem', 'hydration_cache' => 'filesystem', + 'proxy_dir' => LOCAL_CACHE_DIR . '/doctrine-proxies', ], ], 'driver' => [ diff --git a/module/VuFind/src/VuFind/Db/Entity/Feedback.php b/module/VuFind/src/VuFind/Db/Entity/Feedback.php index ede7fb06b42..1900bed464e 100644 --- a/module/VuFind/src/VuFind/Db/Entity/Feedback.php +++ b/module/VuFind/src/VuFind/Db/Entity/Feedback.php @@ -79,7 +79,7 @@ class Feedback implements EntityInterface /** * Form data * - * @var string + * @var mixed * * @ORM\Column(name="form_data", * type="json", @@ -181,6 +181,16 @@ class Feedback implements EntityInterface */ protected $updatedBy; + /** + * Id getter + * + * @return int + */ + public function getId() + { + return $this->id; + } + /** * Message setter * @@ -194,19 +204,39 @@ public function setMessage(string $message): Feedback return $this; } + /** + * Message getter + * + * @return string + */ + public function getMessage() + { + return $this->message; + } + /** * Form data setter. * - * @param string $data Form data + * @param mixed $data Form data * * @return Feedback */ - public function setFormData(string $data): Feedback + public function setFormData($data): Feedback { $this->formData = $data; return $this; } + /** + * Form data getter + * + * @return mixed + */ + public function getFormData() + { + return $this->formData; + } + /** * Form name setter. * @@ -220,6 +250,16 @@ public function setFormName(string $name): Feedback return $this; } + /** + * Form name getter + * + * @return string + */ + public function getFormName() + { + return $this->formName; + } + /** * Created setter. * @@ -233,6 +273,16 @@ public function setCreated(DateTime $dateTime): Feedback return $this; } + /** + * Created getter + * + * @return Datetime + */ + public function getCreated() + { + return $this->created; + } + /** * Updated setter. * @@ -246,6 +296,16 @@ public function setUpdated(DateTime $dateTime): Feedback return $this; } + /** + * Updated getter + * + * @return Datetime + */ + public function getUpdated() + { + return $this->updated; + } + /** * Status setter. * @@ -259,6 +319,16 @@ public function setStatus(string $status): Feedback return $this; } + /** + * Status getter + * + * @return string + */ + public function getStatus() + { + return $this->status; + } + /** * Site URL setter. * @@ -272,6 +342,16 @@ public function setSiteUrl(string $url): Feedback return $this; } + /** + * Site URL getter + * + * @return string + */ + public function getSiteUrl() + { + return $this->siteUrl; + } + /** * User setter. * @@ -285,6 +365,16 @@ public function setUser(?User $user): Feedback return $this; } + /** + * User getter + * + * @return User + */ + public function getUser() + { + return $this->user; + } + /** * Updatedby setter. * @@ -297,4 +387,14 @@ public function setUpdatedBy(?User $user): Feedback $this->updatedBy = $user; return $this; } + + /** + * Updatedby getter + * + * @return User + */ + public function getUpdatedBy() + { + return $this->updatedBy; + } } diff --git a/module/VuFind/src/VuFind/Db/Entity/User.php b/module/VuFind/src/VuFind/Db/Entity/User.php index 5ce2d9e81b0..380af93aede 100644 --- a/module/VuFind/src/VuFind/Db/Entity/User.php +++ b/module/VuFind/src/VuFind/Db/Entity/User.php @@ -256,4 +256,14 @@ class User implements EntityInterface * @ORM\Column(name="last_language", type="string", length=30, nullable=false) */ protected $lastLanguage = ''; + + /** + * Id getter + * + * @return int + */ + public function getId() + { + return $this->id; + } } diff --git a/module/VuFind/src/VuFind/Db/Row/Feedback.php b/module/VuFind/src/VuFind/Db/Row/Feedback.php deleted file mode 100644 index 1d0c85dd76e..00000000000 --- a/module/VuFind/src/VuFind/Db/Row/Feedback.php +++ /dev/null @@ -1,52 +0,0 @@ - - * @license https://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Site - */ -namespace VuFind\Db\Row; - -/** - * Class Feedback - * - * @category VuFind - * @package Db_Row - * @author Josef Moravec - * @license https://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Site - */ -class Feedback extends RowGateway -{ - /** - * Constructor - * - * @param \Laminas\Db\Adapter\Adapter $adapter Database adapter - */ - public function __construct($adapter) - { - parent::__construct('id', 'feedback', $adapter); - } -} diff --git a/module/VuFind/src/VuFind/Db/Row/PluginManager.php b/module/VuFind/src/VuFind/Db/Row/PluginManager.php index 25573dfbb90..5d8ac67bac0 100644 --- a/module/VuFind/src/VuFind/Db/Row/PluginManager.php +++ b/module/VuFind/src/VuFind/Db/Row/PluginManager.php @@ -74,7 +74,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager ChangeTracker::class => RowGatewayFactory::class, Comments::class => RowGatewayFactory::class, ExternalSession::class => RowGatewayFactory::class, - Feedback::class => RowGatewayFactory::class, OaiResumption::class => RowGatewayFactory::class, Ratings::class => RowGatewayFactory::class, Record::class => RowGatewayFactory::class, diff --git a/module/VuFind/src/VuFind/Db/Service/FeedbackService.php b/module/VuFind/src/VuFind/Db/Service/FeedbackService.php index e6ae74a1d96..f16c779d2d9 100644 --- a/module/VuFind/src/VuFind/Db/Service/FeedbackService.php +++ b/module/VuFind/src/VuFind/Db/Service/FeedbackService.php @@ -4,7 +4,7 @@ * * PHP version 7 * - * Copyright (C) Villanova University 2022. + * Copyright (C) Villanova University 2023. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -21,12 +21,13 @@ * * @category VuFind * @package Database - * @author Demian Katz + * @author Sudharma Kellampalli * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License * @link https://vufind.org/wiki/development:plugins:database_gateways Wiki */ namespace VuFind\Db\Service; +use Doctrine\ORM\Tools\Pagination\Paginator; use VuFind\Db\Entity\Feedback; /** @@ -40,6 +41,19 @@ */ class FeedbackService extends AbstractService { + /** + * Db column name to Doctrine entity field mapper + * + * @var array + */ + protected $fieldMap = [ + 'form_data' => 'formData', + 'form_name' => 'formName', + 'site_url' => 'siteUrl', + 'user_id' => 'user', + 'updated_by' => 'updatedBy', + ]; + /** * Create a feedback entity object. * @@ -50,4 +64,127 @@ public function createEntity(): Feedback $class = $this->getEntityClass(Feedback::class); return new $class; } + + /** + * Get feedback by filter + * + * @param string|null $formName Form name + * @param string|null $siteUrl Site URL + * @param string|null $status Current status + * @param int|null $page Current page + * @param int $limit Limit per page + * + * @return Paginator + */ + public function getFeedbackByFilter( + $formName = null, + $siteUrl = null, + $status = null, + $page = null, + $limit = 20 + ): Paginator { + $dql = "SELECT f, CONCAT(u.firstname, ' ', u.lastname) AS user_name, " + . "CONCAT(m.firstname, ' ', m.lastname) AS manager_name " + . "FROM " . $this->getEntityClass(Feedback::class) . " f " + . "LEFT JOIN f.user u " + . "LEFT JOIN f.updatedBy m"; + $parameters = $dqlWhere = []; + + if (null !== $formName) { + $dqlWhere[] = "f.formName = :formName"; + $parameters['formName'] = $formName; + } + if (null !== $siteUrl) { + $dqlWhere[] = "f.siteUrl = :siteUrl"; + $parameters['siteUrl'] = $siteUrl; + } + if (null !== $status) { + $dqlWhere[] = "f.status = :status"; + $parameters['status'] = $status; + } + if (!empty($dqlWhere)) { + $dql .= ' WHERE ' . implode(' AND ', $dqlWhere); + } + $dql .= " ORDER BY f.created DESC"; + $query = $this->entityManager->createQuery($dql); + $query->setParameters($parameters); + + if (null !== $page) { + $query->setMaxResults($limit); + $query->setFirstResult($limit * ($page - 1)); + } + $paginator = new Paginator($query); + return $paginator; + } + + /** + * Delete feedback by ids + * + * @param array $ids IDs + * + * @return int Count of deleted rows + */ + public function deleteByIdArray(array $ids): int + { + // Do nothing if we have no IDs to delete! + if (empty($ids)) { + return 0; + } + $dql = 'DELETE FROM ' . $this->getEntityClass(Feedback::class) . ' fb ' + . 'WHERE fb.id IN (:ids)'; + $query = $this->entityManager->createQuery($dql); + $query->setParameters(compact('ids')); + $query->execute(); + return count($ids); + } + + /** + * Get values for a column + * + * @param string $column Column name + * + * @return array + */ + public function getColumn(string $column): array + { + $dql = "SELECT f.id, f." . $this->mapField($column) + . " FROM " . $this->getEntityClass(Feedback::class) . " f " + . "ORDER BY f." . $this->mapField($column); + $query = $this->entityManager->createQuery($dql); + return $query->getResult(); + } + + /** + * Update a column + * + * @param string $column Column name + * @param mixed $value Column value + * @param int $id id value + * + * @return bool + */ + public function updateColumn($column, $value, $id) + { + $parameters = []; + $dql = "UPDATE " . $this->getEntityClass(Feedback::class) . " f " + . "SET f." . $this->mapField($column) . " = :value " + . "WHERE f.id = :id"; + $parameters['value'] = $value; + $parameters['id'] = $id; + $query = $this->entityManager->createQuery($dql); + $query->setParameters($parameters); + return $query->execute(); + } + + /** + * Column mapper + * + * @param string $column Column name + * + * @return string + */ + public function mapField($column) + { + return $this->fieldMap[$column] ?? $column; + } } diff --git a/module/VuFind/src/VuFind/Db/Service/PluginManager.php b/module/VuFind/src/VuFind/Db/Service/PluginManager.php index 38bd516ccc2..4cb4e87e1fc 100644 --- a/module/VuFind/src/VuFind/Db/Service/PluginManager.php +++ b/module/VuFind/src/VuFind/Db/Service/PluginManager.php @@ -4,7 +4,7 @@ * * PHP version 7 * - * Copyright (C) Villanova University 2021. + * Copyright (C) Villanova University 2023. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -46,6 +46,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager protected $aliases = [ 'feedback' => FeedbackService::class, 'tag' => TagService::class, + 'user' => UserService::class, ]; /** @@ -56,6 +57,7 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager protected $factories = [ FeedbackService::class => AbstractServiceFactory::class, TagService::class => TagServiceFactory::class, + UserService::class => AbstractServiceFactory::class, ]; /** diff --git a/module/VuFind/src/VuFind/Db/Service/UserService.php b/module/VuFind/src/VuFind/Db/Service/UserService.php new file mode 100644 index 00000000000..5a1bd9c2a71 --- /dev/null +++ b/module/VuFind/src/VuFind/Db/Service/UserService.php @@ -0,0 +1,73 @@ + + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:database_gateways Wiki + */ +namespace VuFind\Db\Service; + +use Doctrine\ORM\EntityManager; +use VuFind\Db\Entity\PluginManager as EntityPluginManager; +use VuFind\Db\Entity\User; + +/** + * Database service for user. + * + * @category VuFind + * @package Database + * @author Demian Katz + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:plugins:database_gateways Wiki + */ +class UserService extends AbstractService +{ + /** + * Constructor + * + * @param EntityManager $entityManager Doctrine ORM entity manager + * @param EntityPluginManager $entityPluginManager VuFind entity plugin manager + */ + public function __construct( + EntityManager $entityManager, + EntityPluginManager $entityPluginManager + ) { + parent::__construct($entityManager, $entityPluginManager); + } + + /** + * Lookup and return a user. + * + * @param int $id id value + * + * @return User + */ + public function getUserById($id) + { + $user = $this->entityManager->find( + $this->getEntityClass(\VuFind\Db\Entity\User::class), + $id + ); + return $user; + } +} diff --git a/module/VuFind/src/VuFind/Db/Table/Feedback.php b/module/VuFind/src/VuFind/Db/Table/Feedback.php deleted file mode 100644 index 452e47b47de..00000000000 --- a/module/VuFind/src/VuFind/Db/Table/Feedback.php +++ /dev/null @@ -1,148 +0,0 @@ - - * @license https://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Site - */ -namespace VuFind\Db\Table; - -use Laminas\Db\Adapter\Adapter; -use Laminas\Paginator\Paginator; -use VuFind\Db\Row\RowGateway; - -/** - * Class Feedback - * - * @category VuFind - * @package Db_Table - * @author Josef Moravec - * @license https://opensource.org/licenses/gpl-2.0.php GNU General Public License - * @link https://vufind.org Main Site - */ -class Feedback extends Gateway -{ - /** - * Constructor - * - * @param Adapter $adapter Database adapter - * @param PluginManager $tm Table manager - * @param array $cfg Laminas configuration - * @param RowGateway|null $rowObj Row prototype object (null for default) - * @param string $table Name of database table to interface with - */ - public function __construct( - Adapter $adapter, - PluginManager $tm, - $cfg, - ?RowGateway $rowObj = null, - $table = 'feedback' - ) { - parent::__construct($adapter, $tm, $cfg, $rowObj, $table); - } - - /** - * Get feedback by filter - * - * @param string|null $formName Form name - * @param string|null $siteUrl Site URL - * @param string|null $status Current status - * @param string|null $page Current page - * @param int $limit Limit per page - * - * @return Paginator - */ - public function getFeedbackByFilter( - ?string $formName = null, - ?string $siteUrl = null, - ?string $status = null, - ?string $page = null, - int $limit = 20 - ): Paginator { - $sql = $this->getSql(); - $select = $sql->select()->columns( - [ - '*', - 'user_name' => new \Laminas\Db\Sql\Expression( - "CONCAT_WS(' ', u.firstname, u.lastname)" - ), - 'manager_name' => new \Laminas\Db\Sql\Expression( - "CONCAT_WS(' ', m.firstname, m.lastname)" - ), - ] - ); - if (null !== $formName) { - $select->where->equalTo('form_name', $formName); - } - if (null !== $siteUrl) { - $select->where->equalTo('site_url', $siteUrl); - } - if (null !== $status) { - $select->where->equalTo('status', $status); - } - $select->join( - ['u' => 'user'], - 'u.id = feedback.user_id', - [], - $select::JOIN_LEFT - )->join( - ['m' => 'user'], - 'm.id = feedback.updated_by', - [], - $select::JOIN_LEFT - )->order('created DESC'); - - if (null !== $page) { - $select->limit($limit); - $select->offset($limit * ($page - 1)); - } - $adapter = new \Laminas\Paginator\Adapter\LaminasDb\DbSelect($select, $sql); - $paginator = new \Laminas\Paginator\Paginator($adapter); - $paginator->setItemCountPerPage($limit); - if (null !== $page) { - $paginator->setCurrentPageNumber($page); - } - return $paginator; - } - - /** - * Delete feedback by ids - * - * @param array $ids IDs - * - * @return int Count of deleted rows - */ - public function deleteByIdArray(array $ids): int - { - // Do nothing if we have no IDs to delete! - if (empty($ids)) { - return 0; - } - $callback = function ($select) use ($ids) { - $select->where->in('id', $ids); - }; - return $this->delete($callback); - } -} diff --git a/module/VuFind/src/VuFind/Db/Table/PluginManager.php b/module/VuFind/src/VuFind/Db/Table/PluginManager.php index a88768052a2..25c7c003e1a 100644 --- a/module/VuFind/src/VuFind/Db/Table/PluginManager.php +++ b/module/VuFind/src/VuFind/Db/Table/PluginManager.php @@ -74,7 +74,6 @@ class PluginManager extends \VuFind\ServiceManager\AbstractPluginManager ChangeTracker::class => GatewayFactory::class, Comments::class => GatewayFactory::class, ExternalSession::class => GatewayFactory::class, - Feedback::class => GatewayFactory::class, OaiResumption::class => GatewayFactory::class, Ratings::class => GatewayFactory::class, Record::class => GatewayFactory::class, diff --git a/module/VuFind/src/VuFind/Form/Handler/Database.php b/module/VuFind/src/VuFind/Form/Handler/Database.php index 3c2904800b0..b06cef507fa 100644 --- a/module/VuFind/src/VuFind/Form/Handler/Database.php +++ b/module/VuFind/src/VuFind/Form/Handler/Database.php @@ -7,6 +7,7 @@ * PHP version 8 * * Copyright (C) Moravian Library 2022. + * Copyright (C) Villanova University 2023. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -50,7 +51,14 @@ class Database implements HandlerInterface, LoggerAwareInterface * * @var \VuFind\Db\Service\FeedbackService */ - protected $db; + protected $feedbackService; + + /** + * User database service + * + * @var \VuFind\Db\Service\UserService + */ + protected $userService; /** * Site base url @@ -62,14 +70,17 @@ class Database implements HandlerInterface, LoggerAwareInterface /** * Constructor * - * @param \VuFind\Db\Service\FeedbackService $db Feedback database service + * @param \VuFind\Db\Service\FeedbackService $fs Feedback database service + * @param \VuFind\Db\Service\UserService $us User database service * @param string $baseUrl Site base url */ public function __construct( - \VuFind\Db\Service\FeedbackService $db, + \VuFind\Db\Service\FeedbackService $fs, + \VuFind\Db\Service\UserService $us, string $baseUrl ) { - $this->db = $db; + $this->feedbackService = $fs; + $this->userService = $us; $this->baseUrl = $baseUrl; } @@ -89,20 +100,25 @@ public function handle( ): bool { $fields = $form->mapRequestParamsToFieldValues($params->fromPost()); $fields = array_column($fields, 'value', 'name'); - + // Backward compatibility: convert Laminas\Db to Doctrine; + // we can simplify after completing migration. + $userVal = null; + if ($user) { + $userVal = $this->userService->getUserById($user->id); + } $formData = $fields; unset($formData['message']); $now = new \DateTime(); - $data = $this->db->createEntity() - ->setUser($user) + $data = $this->feedbackService->createEntity() + ->setUser($userVal) ->setMessage($fields['message'] ?? '') - ->setFormData(json_encode($formData)) + ->setFormData($formData) ->setFormName($form->getFormId()) ->setSiteUrl($this->baseUrl) ->setCreated($now) ->setUpdated($now); try { - $this->db->persistEntity($data); + $this->feedbackService->persistEntity($data); } catch (\Exception $e) { throw $e; $this->logError('Could not save feedback data: ' . $e->getMessage()); diff --git a/module/VuFind/src/VuFind/Form/Handler/DatabaseFactory.php b/module/VuFind/src/VuFind/Form/Handler/DatabaseFactory.php index a827b877d53..a52edbfc19d 100644 --- a/module/VuFind/src/VuFind/Form/Handler/DatabaseFactory.php +++ b/module/VuFind/src/VuFind/Form/Handler/DatabaseFactory.php @@ -7,6 +7,7 @@ * PHP version 7 * * Copyright (C) Moravian Library 2022. + * Copyright (C) Villanova University 2023. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -75,6 +76,7 @@ public function __invoke( $baseUrl = $serverUrl($router->assemble([], ['name' => 'home'])); return new $requestedName( $dbServiceManager->get(\VuFind\Db\Service\FeedbackService::class), + $dbServiceManager->get(\VuFind\Db\Service\UserService::class), $baseUrl ); } diff --git a/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Service/FeedbackServiceTest.php b/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Service/FeedbackServiceTest.php new file mode 100644 index 00000000000..a7f470bdd43 --- /dev/null +++ b/module/VuFind/tests/unit-tests/src/VuFindTest/Db/Service/FeedbackServiceTest.php @@ -0,0 +1,210 @@ + + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +namespace VuFindTest\Db\Service; + +use VuFind\Db\Entity\Feedback; +use VuFind\Db\Service\FeedbackService; + +/** + * FeedbackService Test Class + * + * @category VuFind + * @package Tests + * @author Sudharma Kellampalli + * @license http://opensource.org/licenses/gpl-2.0.php GNU General Public License + * @link https://vufind.org/wiki/development:testing:unit_tests Wiki + */ +class FeedbackServiceTest extends \PHPUnit\Framework\TestCase +{ + /** + * Test creating a feedback entity. + * + * @return void + */ + public function testCreateEntity(): void + { + $feedbackService = $this->getConfiguredFeedbackService()['feedbackService']; + + $this->assertInstanceOf(Feedback::class, $feedbackService->createEntity()); + } + + /** + * Test getting column values. + * + * @return void + */ + public function testGetColumn(): void + { + $mocks = $this->getConfiguredFeedbackService(); + $entityManager = $mocks['entityManager']; + $feedbackService = $mocks['feedbackService']; + $queryStmt = "SELECT f.id, f.status FROM VuFind\Db\Entity\Feedback f " + . "ORDER BY f.status"; + $query = $this->getMockBuilder(\Doctrine\ORM\AbstractQuery::class) + ->disableOriginalConstructor() + ->onlyMethods(['getResult']) + ->getMockForAbstractClass(); + $entityManager->expects($this->once())->method('createQuery') + ->with($this->equalTo($queryStmt)) + ->willReturn($query); + $query->expects($this->once())->method('getResult') + ->willReturn([]); + $feedbackService->getColumn('status'); + } + + /** + * Test delete based on id. + * + * @return void + */ + public function testDeleteByIdArray(): void + { + $mocks = $this->getConfiguredFeedbackService(); + $entityManager = $mocks['entityManager']; + $feedbackService = $mocks['feedbackService']; + $queryStmt = "DELETE FROM VuFind\Db\Entity\Feedback fb WHERE fb.id IN (:ids)"; + + $query = $this->getMockBuilder(\Doctrine\ORM\AbstractQuery::class) + ->disableOriginalConstructor() + ->onlyMethods(['execute', 'setParameters']) + ->getMockForAbstractClass(); + $entityManager->expects($this->once())->method('createQuery') + ->with($this->equalTo($queryStmt)) + ->willReturn($query); + $query->expects($this->once())->method('execute'); + $query->expects($this->once())->method('setParameters') + ->with(['ids' => [1,2]]) + ->willReturn($query); + $feedbackService->deleteByIdArray([1, 2]); + } + + /** + * Test Update a column. + * + * @return void + */ + public function testUpdateColumn(): void + { + $mocks = $this->getConfiguredFeedbackService(); + $entityManager = $mocks['entityManager']; + $feedbackService = $mocks['feedbackService']; + $queryStmt = "UPDATE VuFind\Db\Entity\Feedback f SET f.status " + . "= :value WHERE f.id = :id"; + + $query = $this->getMockBuilder(\Doctrine\ORM\AbstractQuery::class) + ->disableOriginalConstructor() + ->onlyMethods(['execute', 'setParameters']) + ->getMockForAbstractClass(); + $entityManager->expects($this->once())->method('createQuery') + ->with($this->equalTo($queryStmt)) + ->willReturn($query); + $query->expects($this->once())->method('execute'); + $query->expects($this->once())->method('setParameters') + ->with(['value' => 'closed', 'id' => 1]) + ->willReturn($query); + $feedbackService->updateColumn('status', 'closed', 1); + } + + /** + * Data provider for testGetFeedbackByFilter. + * + * @return array + */ + public function pageProvider(): array + { + return ['Test1' => [1], + 'Test2' => [null], + ]; + } + + /** + * Test getting feedback based on filters. + * + * @param int|null $page Page number + * + * @return void + * + * @dataProvider pageProvider + */ + public function testGetFeedbackByFilter($page): void + { + $mocks = $this->getConfiguredFeedbackService(); + $entityManager = $mocks['entityManager']; + $feedbackService = $mocks['feedbackService']; + $queryStmt = "SELECT f, CONCAT(u.firstname, ' ', u.lastname) AS user_name, " + . "CONCAT(m.firstname, ' ', m.lastname) AS manager_name FROM " + . "VuFind\Db\Entity\Feedback f LEFT JOIN f.user u LEFT JOIN f.updatedBy m " + . "WHERE f.formName = :formName AND f.siteUrl = :siteUrl AND " + . "f.status = :status ORDER BY f.created DESC"; + + $query = $this->getMockBuilder(\Doctrine\ORM\AbstractQuery::class) + ->disableOriginalConstructor() + ->onlyMethods(['setParameters']) + ->addMethods(['setMaxResults', 'setFirstResult']) + ->getMockForAbstractClass(); + $entityManager->expects($this->once())->method('createQuery') + ->with($this->equalTo($queryStmt)) + ->willReturn($query); + + $query->expects($this->once())->method('setParameters') + ->with( + ['formName' => 'foo', + 'siteUrl' => 'bar', + 'status' => 'closed'] + ) + ->willReturn($query); + if ($page) { + $query->expects($this->once())->method('setFirstResult') + ->with($this->equalTo(0)); + $query->expects($this->once())->method('setMaxResults') + ->with($this->equalTo(20)); + } + + $feedbackService->getFeedbackByFilter('foo', 'bar', 'closed', $page); + } + + /** + * Get a configured FeedbackService object. + * + * @return array + */ + protected function getConfiguredFeedbackService() + { + $entityManager = $this->getMockBuilder(\Doctrine\ORM\EntityManager::class) + ->disableOriginalConstructor() + ->getMock(); + $entityPluginManager = $this->getMockBuilder(\VuFind\Db\Entity\PluginManager::class) + ->disableOriginalConstructor() + ->getMock(); + $entityPluginManager->expects($this->once())->method('get') + ->with($this->equalTo(Feedback::class)) + ->willReturn(new Feedback()); + $feedbackService = new FeedbackService($entityManager, $entityPluginManager); + return compact('entityManager', 'entityPluginManager', 'feedbackService'); + } +} diff --git a/module/VuFindAdmin/src/VuFindAdmin/Controller/FeedbackController.php b/module/VuFindAdmin/src/VuFindAdmin/Controller/FeedbackController.php index 0afc1347665..922fff2dd80 100644 --- a/module/VuFindAdmin/src/VuFindAdmin/Controller/FeedbackController.php +++ b/module/VuFindAdmin/src/VuFindAdmin/Controller/FeedbackController.php @@ -6,7 +6,7 @@ * * PHP version 7 * - * Copyright (C) Moravian Library 2022. + * Copyright (C) Moravian Library 2023. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -29,7 +29,8 @@ */ namespace VuFindAdmin\Controller; -use Laminas\Db\Sql\Select; +use Laminas\ServiceManager\ServiceLocatorInterface; +use VuFind\Db\Service\FeedbackService; use VuFind\Db\Table\Feedback; /** @@ -43,20 +44,40 @@ */ class FeedbackController extends AbstractAdmin { + /** + * Feedback service + * + * @var FeedbackService + */ + protected $feedbackService; + + /** + * Constructor + * + * @param ServiceLocatorInterface $sm Service locator + */ + public function __construct(ServiceLocatorInterface $sm) + { + parent::__construct($sm); + $this->feedbackService = $sm->get(\VuFind\Db\Service\PluginManager::class) + ->get(FeedbackService::class); + } + /** * Get the url parameters * * @param string $param A key to check the url params for * @param bool $prioritizePost If true, check the POST params first + * @param mixed $default Default value if no value found * * @return string */ - protected function getParam($param, $prioritizePost = false) + protected function getParam($param, $prioritizePost = false, $default = null) { $primary = $prioritizePost ? 'fromPost' : 'fromQuery'; $secondary = $prioritizePost ? 'fromQuery' : 'fromPost'; return $this->params()->$primary($param) - ?? $this->params()->$secondary($param); + ?? $this->params()->$secondary($param, $default); } /** @@ -66,15 +87,18 @@ protected function getParam($param, $prioritizePost = false) */ public function homeAction() { - $feedbackTable = $this->getFeedbackTable(); - $feedback = $feedbackTable->getFeedbackByFilter( + $feedback = $this->feedbackService->getFeedbackByFilter( $this->convertFilter($this->getParam('form_name')), $this->convertFilter($this->getParam('site_url')), $this->convertFilter($this->getParam('status')) ); $view = $this->createViewModel( [ - 'feedback' => $feedback, + 'feedback' => new \Laminas\Paginator\Paginator( + new \DoctrineORMModule\Paginator\Adapter\DoctrinePaginator( + $feedback + ) + ), 'statuses' => $this->getStatuses(), 'uniqueForms' => $this->getUniqueColumn('form_name'), 'uniqueSites' => $this->getUniqueColumn('site_url'), @@ -82,6 +106,9 @@ public function homeAction() => $this->params()->fromQuery() + $this->params()->fromPost(), ] ); + $page = $this->getParam('page', false, '1'); + $view->feedback->setCurrentPageNumber($page); + $view->feedback->setItemCountPerPage(20); $view->setTemplate('admin/feedback/home'); return $view; } @@ -94,7 +121,6 @@ public function homeAction() public function deleteAction() { $confirm = $this->getParam('confirm', true); - $feedbackTable = $this->getFeedbackTable(); $originUrl = $this->url()->fromRoute('admin/feedback'); $formName = $this->getParam('form_name', true); $siteUrl = $this->getParam('site_url', true); @@ -119,7 +145,7 @@ public function deleteAction() if (!$confirm) { return $this->confirmDelete($ids, $originUrl, $newUrl); } - $delete = $feedbackTable->deleteByIdArray($ids); + $delete = $this->feedbackService->deleteByIdArray($ids); if (0 == $delete) { $this->flashMessenger()->addMessage('feedback_delete_failure', 'error'); return $this->redirect()->toUrl($originUrl); @@ -211,12 +237,9 @@ protected function getConfirmDeleteMessages(int $count): array */ public function updateStatusAction() { - $feedbackTable = $this->getFeedbackTable(); $newStatus = $this->getParam('new_status', true); $id = $this->getParam('id', true); - $feedback = $feedbackTable->select(['id' => $id])->current(); - $feedback->status = $newStatus; - $success = $feedback->save(); + $success = $this->feedbackService->updateColumn("status", $newStatus, $id); if ($success) { $this->flashMessenger()->addMessage( 'feedback_status_update_success', @@ -232,25 +255,17 @@ public function updateStatusAction() 'admin/feedback', [], [ - 'query' => [ - 'form_name' => $this->getParam('form_name'), - 'site_url' => $this->getParam('site_url'), - 'status' => $this->getParam('status'), - ], + 'query' => array_filter( + [ + 'form_name' => $this->getParam('form_name'), + 'site_url' => $this->getParam('site_url'), + 'status' => $this->getParam('status'), + ] + ), ] ); } - /** - * Get Feedback table - * - * @return Feedback - */ - protected function getFeedbackTable(): Feedback - { - return $this->getTable(Feedback::class); - } - /** * Get unique values for a column * @@ -260,14 +275,8 @@ protected function getFeedbackTable(): Feedback */ protected function getUniqueColumn(string $column): array { - $feedbackTable = $this->getFeedbackTable(); - $feedback = $feedbackTable->select( - function (Select $select) use ($column) { - $select->columns(['id', $column]); - $select->order($column); - } - ); - $feedbackArray = $feedback->toArray(); + $feedbackArray = $this->feedbackService->getColumn($column); + $column = $this->feedbackService->mapField($column); return array_unique(array_column($feedbackArray, $column)); } diff --git a/themes/bootstrap3/templates/admin/feedback/home.phtml b/themes/bootstrap3/templates/admin/feedback/home.phtml index 4f4c3c20ded..fd8161faea3 100644 --- a/themes/bootstrap3/templates/admin/feedback/home.phtml +++ b/themes/bootstrap3/templates/admin/feedback/home.phtml @@ -72,30 +72,30 @@ $this->headTitle($this->translate('VuFind Administration - Feedback Management') feedback as $feedbackItem): ?> form_data, true); + $data = $feedbackItem[0]->getFormData(); ?> - - - escapeHtml($feedbackItem->form_name)?> + + + escapeHtml($feedbackItem[0]->getFormName())?> - escapeHtml($feedbackItem->site_url)?> + escapeHtml($feedbackItem[0]->getSiteUrl())?> - truncate($feedbackItem->message, 100, '')?> - message)) > 100): ?> - message?> - - + transEsc('Show')?> -