From 17b768dfab3ed04263b7c1402c9e6948833209d2 Mon Sep 17 00:00:00 2001 From: Thorsten Rinne Date: Sun, 24 Nov 2024 16:06:02 +0100 Subject: [PATCH] refactor: migrated ratings page to controller (#3257) --- nginx.conf | 2 +- phpmyfaq/.htaccess | 2 +- phpmyfaq/admin/assets/src/api/statistics.js | 21 +++++ phpmyfaq/admin/assets/src/index.js | 9 +- phpmyfaq/admin/assets/src/statistics/index.js | 2 + .../admin/assets/src/statistics/ratings.js | 35 +++++++ phpmyfaq/admin/header.php | 3 +- phpmyfaq/admin/index.php | 4 - phpmyfaq/admin/statistics.ratings.php | 91 ------------------- .../templates/admin/statistics/ratings.twig | 21 ++--- phpmyfaq/src/admin-api-routes.php | 5 + phpmyfaq/src/admin-routes.php | 6 ++ .../AbstractAdministrationController.php | 7 +- .../Api/StatisticsController.php | 33 +++++-- .../Administration/RatingController.php | 77 ++++++++++++++++ phpmyfaq/src/services.php | 24 +++++ 16 files changed, 219 insertions(+), 123 deletions(-) create mode 100644 phpmyfaq/admin/assets/src/statistics/ratings.js delete mode 100644 phpmyfaq/admin/statistics.ratings.php create mode 100644 phpmyfaq/src/phpMyFAQ/Controller/Administration/RatingController.php diff --git a/nginx.conf b/nginx.conf index ceebaf8751..800c4f34c1 100644 --- a/nginx.conf +++ b/nginx.conf @@ -129,7 +129,7 @@ server { rewrite admin/api/(.*) /admin/api/index.php last; # Administration pages - rewrite admin/(attachments|backup|configuration|elasticsearch|export|group|import|instance|instances|password|session-keep-alive|stopwords|system|update|user) /admin/front.php last; + rewrite admin/(attachments|backup|configuration|elasticsearch|export|group|import|instance|instances|password|session-keep-alive|statistics|stopwords|system|update|user) /admin/front.php last; # REST API v3.0 and v3.1 rewrite ^api/v3\.[01]/(.*) /api/index.php last; diff --git a/phpmyfaq/.htaccess b/phpmyfaq/.htaccess index 102c313151..07ce5fd43b 100644 --- a/phpmyfaq/.htaccess +++ b/phpmyfaq/.htaccess @@ -143,7 +143,7 @@ Header set Access-Control-Allow-Headers "Content-Type, Authorization" # Administration API RewriteRule ^admin/api/(.*) admin/api/index.php [L,QSA] # Administration pages - RewriteRule ^admin/(attachments|backup|configuration|elasticsearch|export|group|import|instance|instances|password|session-keep-alive|stopwords|system|update|user) admin/front.php [L,QSA] + RewriteRule ^admin/(attachments|backup|configuration|elasticsearch|export|group|import|instance|instances|password|session-keep-alive|statistics|stopwords|system|update|user) admin/front.php [L,QSA] # Private APIs RewriteRule ^api/(autocomplete|bookmark/delete|bookmark/create|user/data/update|user/password/update|user/request-removal|user/remove-twofactor|contact|voting|register|captcha|share|comment/create|faq/create|question/create|webauthn/prepare|webauthn/register|webauthn/prepare-login|webauthn/login) api/index.php [L,QSA] # Setup APIs diff --git a/phpmyfaq/admin/assets/src/api/statistics.js b/phpmyfaq/admin/assets/src/api/statistics.js index b14657f932..f5e3805d43 100644 --- a/phpmyfaq/admin/assets/src/api/statistics.js +++ b/phpmyfaq/admin/assets/src/api/statistics.js @@ -54,3 +54,24 @@ export const truncateSearchTerms = async (csrfToken) => { console.error(error); } }; + +export const clearRatings = async (csrfToken) => { + try { + const response = await fetch(`./api/statistics/ratings/clear`, { + method: 'DELETE', + cache: 'no-cache', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + csrfToken: csrfToken, + }), + redirect: 'follow', + referrerPolicy: 'no-referrer', + }); + + return await response.json(); + } catch (error) { + console.error(error); + } +}; diff --git a/phpmyfaq/admin/assets/src/index.js b/phpmyfaq/admin/assets/src/index.js index 9d82fa760a..611da0fd4a 100644 --- a/phpmyfaq/admin/assets/src/index.js +++ b/phpmyfaq/admin/assets/src/index.js @@ -14,7 +14,13 @@ */ import { getLatestVersion, renderVisitorCharts, renderTopTenCharts, handleVerificationModal } from './dashboard'; -import { handleCreateReport, handleDeleteAdminLog, handleSessions, handleStatistics } from './statistics'; +import { + handleClearRatings, + handleCreateReport, + handleDeleteAdminLog, + handleSessions, + handleStatistics, +} from './statistics'; import { handleConfiguration, handleInstances, @@ -124,6 +130,7 @@ document.addEventListener('DOMContentLoaded', async () => { handleStatistics(); handleCreateReport(); handleTruncateSearchTerms(); + handleClearRatings(); // Configuration → FAQ configuration await handleConfiguration(); diff --git a/phpmyfaq/admin/assets/src/statistics/index.js b/phpmyfaq/admin/assets/src/statistics/index.js index e1eb07b8df..44d1756d1e 100644 --- a/phpmyfaq/admin/assets/src/statistics/index.js +++ b/phpmyfaq/admin/assets/src/statistics/index.js @@ -1,4 +1,6 @@ export * from './admin-log'; +export * from './ratings'; export * from './report'; +export * from './search'; export * from './sessions'; export * from './statistics'; diff --git a/phpmyfaq/admin/assets/src/statistics/ratings.js b/phpmyfaq/admin/assets/src/statistics/ratings.js new file mode 100644 index 0000000000..d177617ba7 --- /dev/null +++ b/phpmyfaq/admin/assets/src/statistics/ratings.js @@ -0,0 +1,35 @@ +/** + * Clear Ratings Handling + * + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + * + * @package phpMyFAQ + * @author Thorsten Rinne + * @copyright 2024 phpMyFAQ Team + * @license http://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @link https://www.phpmyfaq.de + * @since 2024-11-14 + */ + +import { pushErrorNotification, pushNotification } from '../utils/index.js'; +import { clearRatings } from '../api/index.js'; + +export const handleClearRatings = () => { + const buttonClearRatings = document.getElementById('pmf-admin-clear-ratings'); + + if (buttonClearRatings) { + buttonClearRatings.addEventListener('click', async (event) => { + event.preventDefault(); + const csrf = event.target.getAttribute('data-pmf-csrf'); + const response = await clearRatings(csrf); + + if (response.success) { + pushNotification(response.success); + } else { + pushErrorNotification(response.error); + } + }); + } +}; diff --git a/phpmyfaq/admin/header.php b/phpmyfaq/admin/header.php index 92cefe0dfa..dc6a829b53 100644 --- a/phpmyfaq/admin/header.php +++ b/phpmyfaq/admin/header.php @@ -106,7 +106,8 @@ $secLevelEntries['statistics'] = $adminHelper->addMenuEntry( PermissionType::STATISTICS_VIEWLOGS->value, 'statistics', - 'ad_menu_stat' + 'ad_menu_stat', + 'statistics/ratings' ); $secLevelEntries['statistics'] .= $adminHelper->addMenuEntry( PermissionType::STATISTICS_VIEWLOGS->value, diff --git a/phpmyfaq/admin/index.php b/phpmyfaq/admin/index.php index c3c34ef258..9c9a5b4e2b 100755 --- a/phpmyfaq/admin/index.php +++ b/phpmyfaq/admin/index.php @@ -328,10 +328,6 @@ case 'viewsession': require 'statistics.show.php'; break; - case 'clear-statistics': - case 'statistics': - require 'statistics.ratings.php'; - break; case 'truncatesearchterms': case 'searchstats': require 'statistics.search.php'; diff --git a/phpmyfaq/admin/statistics.ratings.php b/phpmyfaq/admin/statistics.ratings.php deleted file mode 100644 index f3f5c912e7..0000000000 --- a/phpmyfaq/admin/statistics.ratings.php +++ /dev/null @@ -1,91 +0,0 @@ - - * @copyright 2003-2024 phpMyFAQ Team - * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 - * @link https://www.phpmyfaq.de - * @since 2003-02-24 - */ - -use phpMyFAQ\Administration\RatingData; -use phpMyFAQ\Category; -use phpMyFAQ\Configuration; -use phpMyFAQ\Enums\PermissionType; -use phpMyFAQ\Filter; -use phpMyFAQ\Rating; -use phpMyFAQ\Session\Token; -use phpMyFAQ\Template\TwigWrapper; -use phpMyFAQ\Translation; -use phpMyFAQ\User\CurrentUser; - -if (!defined('IS_VALID_PHPMYFAQ')) { - http_response_code(400); - exit(); -} - -$faqConfig = Configuration::getConfigurationInstance(); -$user = CurrentUser::getCurrentUser($faqConfig); - -[ $currentAdminUser, $currentAdminGroups ] = CurrentUser::getCurrentUserGroupId($user); - -if ($user->perm->hasPermission($user->getUserId(), PermissionType::STATISTICS_VIEWLOGS->value)) { - $csrfToken = Filter::filterInput(INPUT_GET, 'csrf', FILTER_SANITIZE_SPECIAL_CHARS); - - $twig = new TwigWrapper(PMF_ROOT_DIR . '/assets/templates'); - $template = $twig->loadTemplate('@admin/statistics/ratings.twig'); - - $category = new Category($faqConfig, [], false); - $category->setUser($currentAdminUser); - $category->setGroups($currentAdminGroups); - $ratingData = new RatingData($faqConfig); - $ratings = new Rating($faqConfig); - - if ($csrfToken && !Token::getInstance($container->get('session'))->verifyToken('clear-statistics', $csrfToken)) { - $clearStatistics = false; - } else { - $clearStatistics = true; - } - - if ('clear-statistics' === $action && $clearStatistics) { - if ($ratings->deleteAll()) { - $deletedStatistics = true; - } else { - $deletedStatistics = false; - } - } - - $data = $ratingData->getAll(); - $numberOfRatings = is_countable($data) ? count($data) : 0; - $currentCategory = 0; - - $templateVars = [ - 'adminHeaderRatings' => Translation::get('ad_rs'), - 'csrfToken' => Token::getInstance($container->get('session'))->getTokenString('clear-statistics'), - 'buttonDeleteAllVotings' => Translation::get('ad_delete_all_votings'), - 'isDeleteAllVotings' => 'clear-statistics' === $action && $clearStatistics, - 'isDeletedStatistics' => $deletedStatistics ?? false, - 'msgDeleteAllVotings' => Translation::get('msgDeleteAllVotings'), - 'msgDeleteAllVotingsError' => Translation::get('msgDeleteAllVotingsError'), - 'currentCategory' => $currentCategory, - 'ratingData' => $data, - 'numberOfRatings' => $numberOfRatings, - 'categoryNames' => $category->categoryName, - 'green' => Translation::get('ad_rs_green'), - 'greenNote' => Translation::get('ad_rs_ahtf'), - 'red' => Translation::get('ad_rs_red'), - 'redNote' => Translation::get('ad_rs_altt'), - 'msgNoRatings' => Translation::get('ad_rs_no') - ]; - - echo $template->render($templateVars); -} else { - require __DIR__ . '/no-permission.php'; -} diff --git a/phpmyfaq/assets/templates/admin/statistics/ratings.twig b/phpmyfaq/assets/templates/admin/statistics/ratings.twig index c3e52cc015..ea4ea12f32 100644 --- a/phpmyfaq/assets/templates/admin/statistics/ratings.twig +++ b/phpmyfaq/assets/templates/admin/statistics/ratings.twig @@ -1,29 +1,19 @@ +{% extends '@admin/index.twig' %} + +{% block content %}

{{ adminHeaderRatings }}

-{% if isDeleteAllVotings %} - {% if isDeletedStatistics %} - - {% else %} - - {% endif %} -{% endif %} -
@@ -81,3 +71,4 @@ +{% endblock %} diff --git a/phpmyfaq/src/admin-api-routes.php b/phpmyfaq/src/admin-api-routes.php index 9462b57ccc..7bde6dc84e 100644 --- a/phpmyfaq/src/admin-api-routes.php +++ b/phpmyfaq/src/admin-api-routes.php @@ -476,6 +476,11 @@ 'controller' => [StatisticsController::class, 'truncateSearchTerms'], 'methods' => 'DELETE' ], + 'admin.api.statistics.ratings.clear' => [ + 'path' => '/statistics/ratings/clear', + 'controller' => [StatisticsController::class, 'clearRatings'], + 'methods' => 'DELETE' + ], // Forms API 'admin.api.forms.activate' => [ 'path' => '/forms/activate', diff --git a/phpmyfaq/src/admin-routes.php b/phpmyfaq/src/admin-routes.php index fdd30c560c..90f7b49f02 100644 --- a/phpmyfaq/src/admin-routes.php +++ b/phpmyfaq/src/admin-routes.php @@ -24,6 +24,7 @@ use phpMyFAQ\Controller\Administration\ImportController; use phpMyFAQ\Controller\Administration\InstanceController; use phpMyFAQ\Controller\Administration\PasswordChangeController; +use phpMyFAQ\Controller\Administration\RatingController; use phpMyFAQ\Controller\Administration\SessionKeepAliveController; use phpMyFAQ\Controller\Administration\StopWordsController; use phpMyFAQ\Controller\Administration\SystemInformationController; @@ -145,6 +146,11 @@ 'controller' => [SessionKeepAliveController::class, 'index'], 'methods' => 'GET' ], + 'admin.statistics.ratings' => [ + 'path' => '/statistics/ratings', + 'controller' => [RatingController::class, 'index'], + 'methods' => 'GET' + ], 'admin.stopwords' => [ 'path' => '/stopwords', 'controller' => [StopwordsController::class, 'index'], diff --git a/phpmyfaq/src/phpMyFAQ/Controller/Administration/AbstractAdministrationController.php b/phpmyfaq/src/phpMyFAQ/Controller/Administration/AbstractAdministrationController.php index 28f262707d..e065ecc208 100644 --- a/phpmyfaq/src/phpMyFAQ/Controller/Administration/AbstractAdministrationController.php +++ b/phpmyfaq/src/phpMyFAQ/Controller/Administration/AbstractAdministrationController.php @@ -122,7 +122,8 @@ protected function getHeader(Request $request): array $secLevelEntries['statistics'] = $adminHelper->addMenuEntry( PermissionType::STATISTICS_VIEWLOGS->value, 'statistics', - 'ad_menu_stat' + 'ad_menu_stat', + 'statistics/ratings' ); $secLevelEntries['statistics'] .= $adminHelper->addMenuEntry( PermissionType::STATISTICS_VIEWLOGS->value, @@ -232,7 +233,6 @@ protected function getHeader(Request $request): array case 'stickyfaqs': $contentPage = true; break; - case 'statistics': case 'viewsessions': case 'sessionbrowse': case 'sessionsuche': @@ -268,6 +268,9 @@ protected function getHeader(Request $request): array case 'admin.attachments': $contentPage = true; break; + case 'admin.statistics.ratings': + $statisticsPage = true; + break; case 'admin.export': case 'admin.import': $exportsPage = true; diff --git a/phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/StatisticsController.php b/phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/StatisticsController.php index 5f680a31c5..0982fcdf4f 100644 --- a/phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/StatisticsController.php +++ b/phpmyfaq/src/phpMyFAQ/Controller/Administration/Api/StatisticsController.php @@ -18,22 +18,21 @@ namespace phpMyFAQ\Controller\Administration\Api; use JsonException; -use phpMyFAQ\Administration\AdminLog; use phpMyFAQ\Controller\AbstractController; use phpMyFAQ\Core\Exception; use phpMyFAQ\Enums\PermissionType; -use phpMyFAQ\Search; use phpMyFAQ\Session\Token; use phpMyFAQ\Translation; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Routing\Annotation\Route; +use Symfony\Component\Routing\Attribute\Route; class StatisticsController extends AbstractController { /** * @throws Exception|JsonException + * @throws \Exception */ #[Route('./admin/api/statistics/admin-log', methods: ['DELETE'])] public function deleteAdminLog(Request $request): JsonResponse @@ -46,8 +45,7 @@ public function deleteAdminLog(Request $request): JsonResponse return $this->json(['error' => Translation::get('msgNoPermission')], Response::HTTP_UNAUTHORIZED); } - $logging = new AdminLog($this->configuration); - if ($logging->delete()) { + if ($this->container->get('phpmyfaq.admin.admin-log')->delete()) { return $this->json(['success' => Translation::get('ad_adminlog_delete_success')], Response::HTTP_OK); } @@ -56,6 +54,7 @@ public function deleteAdminLog(Request $request): JsonResponse /** * @throws Exception|JsonException + * @throws \Exception */ #[Route('./admin/api/statistics/search-terms', methods: ['DELETE'])] public function truncateSearchTerms(Request $request): JsonResponse @@ -71,11 +70,31 @@ public function truncateSearchTerms(Request $request): JsonResponse return $this->json(['error' => Translation::get('msgNoPermission')], Response::HTTP_UNAUTHORIZED); } - $search = new Search($this->configuration); - if ($search->deleteAllSearchTerms()) { + if ($this->container->get('phpmyfaq.search')->deleteAllSearchTerms()) { return $this->json(['success' => Translation::get('ad_searchterm_del_suc')], Response::HTTP_OK); } return $this->json(['error' => Translation::get('ad_searchterm_del_err')], Response::HTTP_BAD_REQUEST); } + + /** + * @throws \Exception + */ + #[Route('./admin/api/statistics/ratings/clear', name: 'admin.api.statistics.ratings.clear', methods: ['DELETE'])] + public function clearRatings(Request $request): JsonResponse + { + $this->userHasPermission(PermissionType::STATISTICS_VIEWLOGS); + + $data = json_decode($request->getContent(), false, 512, JSON_THROW_ON_ERROR); + + if (!Token::getInstance($this->container->get('session'))->verifyToken('clear-statistics', $data->csrfToken)) { + return $this->json(['error' => Translation::get('msgNoPermission')], Response::HTTP_UNAUTHORIZED); + } + + if ($this->container->get('phpmyfaq.rating')->deleteAll()) { + return $this->json(['success' => Translation::get('msgDeleteAllVotings')], Response::HTTP_OK); + } + + return $this->json(['error' => Translation::get('msgDeleteAllVotings')], Response::HTTP_BAD_REQUEST); + } } diff --git a/phpmyfaq/src/phpMyFAQ/Controller/Administration/RatingController.php b/phpmyfaq/src/phpMyFAQ/Controller/Administration/RatingController.php new file mode 100644 index 0000000000..af539a9b1a --- /dev/null +++ b/phpmyfaq/src/phpMyFAQ/Controller/Administration/RatingController.php @@ -0,0 +1,77 @@ + + * @copyright 2024 phpMyFAQ Team + * @license https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0 + * @link https://www.phpmyfaq.de + * @since 2024-11-24 + */ + +declare(strict_types=1); + +namespace phpMyFAQ\Controller\Administration; + +use phpMyFAQ\Category; +use phpMyFAQ\Core\Exception; +use phpMyFAQ\Enums\PermissionType; +use phpMyFAQ\Session\Token; +use phpMyFAQ\Translation; +use phpMyFAQ\User\CurrentUser; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\Attribute\Route; +use Twig\Error\LoaderError; + +class RatingController extends AbstractAdministrationController +{ + /** + * @throws Exception + * @throws LoaderError + * @throws \Exception + */ + #[Route('/statistics/rating', name: 'admin.statistics.rating', methods: ['GET'])] + public function index(Request $request): Response + { + $this->userHasPermission(PermissionType::STATISTICS_VIEWLOGS); + + [ $currentUser, $currentGroups ] = CurrentUser::getCurrentUserGroupId($this->currentUser); + + $category = new Category($this->configuration, [], false); + $category->setUser($currentUser); + $category->setGroups($currentGroups); + + $ratingData = $this->container->get('phpmyfaq.admin.rating-data'); + + $data = $ratingData->getAll(); + $numberOfRatings = is_countable($data) ? count($data) : 0; + $currentCategory = 0; + + return $this->render( + '@admin/statistics/ratings.twig', + [ + ... $this->getHeader($request), + ... $this->getFooter(), + 'csrfToken' => Token::getInstance($this->container->get('session'))->getTokenString('clear-statistics'), + 'currentCategory' => $currentCategory, + 'ratingData' => $data, + 'numberOfRatings' => $numberOfRatings, + 'categoryNames' => $category->categoryName, + 'adminHeaderRatings' => Translation::get('ad_rs'), + 'buttonDeleteAllVotings' => Translation::get('ad_delete_all_votings'), + 'green' => Translation::get('ad_rs_green'), + 'greenNote' => Translation::get('ad_rs_ahtf'), + 'red' => Translation::get('ad_rs_red'), + 'redNote' => Translation::get('ad_rs_altt'), + 'msgNoRatings' => Translation::get('ad_rs_no') + ] + ); + } +} diff --git a/phpmyfaq/src/services.php b/phpmyfaq/src/services.php index 68698bfccf..0205ddb5da 100644 --- a/phpmyfaq/src/services.php +++ b/phpmyfaq/src/services.php @@ -15,8 +15,10 @@ * @since 2024-10-07 */ +use phpMyFAQ\Administration\AdminLog; use phpMyFAQ\Administration\Backup; use phpMyFAQ\Administration\Category; +use phpMyFAQ\Administration\RatingData; use phpMyFAQ\Administration\Session as AdminSession; use phpMyFAQ\Attachment\AttachmentCollection; use phpMyFAQ\Auth; @@ -33,6 +35,8 @@ use phpMyFAQ\Helper\CategoryHelper; use phpMyFAQ\Instance; use phpMyFAQ\Language; +use phpMyFAQ\Rating; +use phpMyFAQ\Search; use phpMyFAQ\Services\Gravatar; use phpMyFAQ\Session\Token; use phpMyFAQ\Setup\EnvironmentConfigurator; @@ -56,11 +60,21 @@ $services->set('session', Session::class); + $services->set('phpmyfaq.admin.admin-log', AdminLog::class) + ->args([ + new Reference('phpmyfaq.configuration'), + ]); + $services->set('phpmyfaq.admin.category', Category::class) ->args([ new Reference('phpmyfaq.configuration'), ]); + $services->set('phpmyfaq.admin.rating-data', RatingData::class) + ->args([ + new Reference('phpmyfaq.configuration'), + ]); + $services->set('phpmyfaq.admin.session', AdminSession::class) ->args([ new Reference('phpmyfaq.configuration'), @@ -123,6 +137,11 @@ new Reference('phpmyfaq.configuration') ]); + $services->set('phpmyfaq.rating', Rating::class) + ->args([ + new Reference('phpmyfaq.configuration'), + ]); + $services->set('phpmyfaq.faq.statistics', Statistics::class) ->args([ new Reference('phpmyfaq.configuration') @@ -151,6 +170,11 @@ new Reference('session') ]); + $services->set('phpmyfaq.search', Search::class) + ->args([ + new Reference('phpmyfaq.configuration') + ]); + $services->set('phpmyfaq.session.token', Token::class) ->factory([Token::class, 'getInstance']) ->args([