Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
68.83% covered (warning)
68.83%
53 / 77
50.00% covered (danger)
50.00%
1 / 2
CRAP
0.00% covered (danger)
0.00%
0 / 1
DashboardController
68.83% covered (warning)
68.83%
53 / 77
50.00% covered (danger)
50.00%
1 / 2
11.45
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 index
68.42% covered (warning)
68.42%
52 / 76
0.00% covered (danger)
0.00%
0 / 1
10.02
1<?php
2
3/**
4 * The Administration Dashboard Controller
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2024-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2024-12-29
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Controller\Administration;
21
22use phpMyFAQ\Administration\Backup;
23use phpMyFAQ\Administration\Faq as AdminFaq;
24use phpMyFAQ\Administration\RecentUsers;
25use phpMyFAQ\Administration\RemoteApiClient;
26use phpMyFAQ\Administration\Session as AdminSession;
27use phpMyFAQ\Core\Exception;
28use phpMyFAQ\Database;
29use phpMyFAQ\Enums\PermissionType;
30use phpMyFAQ\Environment;
31use phpMyFAQ\Filter;
32use phpMyFAQ\Session\Token;
33use phpMyFAQ\System;
34use phpMyFAQ\Translation;
35use Symfony\Component\HttpFoundation\Request;
36use Symfony\Component\HttpFoundation\Response;
37use Symfony\Component\Routing\Attribute\Route;
38use Symfony\Contracts\HttpClient\Exception\DecodingExceptionInterface;
39use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
40use Twig\Error\LoaderError;
41
42final class DashboardController extends AbstractAdministrationController
43{
44    public function __construct(
45        private readonly AdminSession $adminSession,
46        private readonly AdminFaq $adminFaq,
47        private readonly Backup $backup,
48        private readonly RecentUsers $recentUsers,
49        private readonly RemoteApiClient $adminApi,
50    ) {
51        parent::__construct();
52    }
53
54    /**
55     * @throws LoaderError
56     * @throws Exception
57     * @throws \Exception
58     */
59    #[Route(path: '/', name: 'admin.dashboard', methods: ['GET', 'POST'])]
60    public function index(Request $request): Response
61    {
62        $this->userIsAuthenticated();
63
64        $faqTableInfo = $this->configuration->getDb()->getTableStatus(Database::getTablePrefix());
65        $userId = $this->currentUser->getUserId();
66
67        $backupInfo = $this->backup->getLastBackupInfo();
68
69        $templateVars = [
70            'isDebugMode' => Environment::isDebugMode(),
71            'isMaintenanceMode' => $this->configuration->get(item: 'main.maintenanceMode'),
72            'isDevelopmentVersion' => System::isDevelopmentVersion(),
73            'currentVersionApp' => System::getVersion(),
74            'msgAdminWarningDevelopmentVersion' => sprintf(
75                Translation::getString(key: 'msgAdminWarningDevelopmentVersion'),
76                System::getVersion(),
77                System::getGitHubIssuesUrl(),
78            ),
79            'adminDashboardInfoNumVisits' => $this->adminSession->getNumberOfSessions(),
80            'adminDashboardInfoNumFaqs' => $faqTableInfo[Database::getTablePrefix() . 'faqdata'],
81            'adminDashboardInfoNumComments' => $faqTableInfo[Database::getTablePrefix() . 'faqcomments'],
82            'adminDashboardInfoNumQuestions' => $faqTableInfo[Database::getTablePrefix() . 'faqquestions'],
83            'adminDashboardInfoUser' => Translation::get(key: 'msgNews'),
84            'adminDashboardInfoNumUser' => (int) $faqTableInfo[Database::getTablePrefix() . 'faquser'] - 1,
85            'adminDashboardHeaderUsersOnline' => Translation::get(key: 'msgUserOnline'),
86            'adminDashboardInfoNumUsersOnline' => $this->adminSession->getNumberOfOnlineUsers(windowSeconds: 600),
87            'adminDashboardHeaderVisits' => Translation::get(key: 'ad_stat_report_visits'),
88            'hasUserTracking' => $this->configuration->get(item: 'main.enableUserTracking'),
89            'adminDashboardHeaderInactiveFaqs' => Translation::get(key: 'ad_record_inactive'),
90            'adminDashboardInactiveFaqs' => $this->adminFaq->getInactiveFaqsData(),
91            'hasPermissionEditConfig' => $this->currentUser?->perm->hasPermission(
92                $userId,
93                PermissionType::CONFIGURATION_EDIT->value,
94            ),
95            'showVersion' => $this->configuration->get(item: 'main.enableAutoUpdateHint'),
96            'documentationUrl' => System::getDocumentationUrl(),
97            'lastBackupDate' => $backupInfo['lastBackupDate'],
98            'isBackupOlderThan30Days' => $backupInfo['isBackupOlderThan30Days'],
99            'adminDashboardRecentUsers' => $this->recentUsers->getList(limit: 5),
100            'hasRecentNews' => $this->configuration->get(item: 'main.enableRecentNews'),
101            'dashboardCsrfToken' => Token::getInstance($this->session)->getTokenString(page: 'dashboard'),
102        ];
103
104        if (version_compare($this->configuration->getVersion(), System::getVersion(), operator: '<')) {
105            $templateVars = [
106                ...$templateVars,
107                'hasVersionConflict' => true,
108                'currentVersionDatabase' => $this->configuration->getVersion(),
109            ];
110        }
111
112        if ($this->currentUser->perm->hasPermission($userId, PermissionType::CONFIGURATION_EDIT->value)) {
113            $version = Filter::filterVar($request->query->get(key: 'param'), FILTER_SANITIZE_SPECIAL_CHARS);
114            if (!$this->configuration->get(item: 'main.enableAutoUpdateHint') && $version === 'version') {
115                try {
116                    $versions = $this->adminApi->getVersions();
117                    $templateVars = [
118                        ...$templateVars,
119                        'adminDashboardShouldUpdateMessage' => false,
120                        'adminDashboardLatestVersionMessage' => Translation::get(key: 'ad_xmlrpc_latest'),
121                        'adminDashboardVersions' => $versions,
122                    ];
123
124                    if (-1 === version_compare($versions['installed'], $versions['stable'])) {
125                        $templateVars = [
126                            ...$templateVars,
127                            'adminDashboardShouldUpdateMessage' => true,
128                            'adminDashboardLatestVersionMessage' => Translation::get(key: 'ad_you_should_update'),
129                            'adminDashboardVersions' => $versions,
130                        ];
131                    }
132                } catch (DecodingExceptionInterface|TransportExceptionInterface|Exception $e) {
133                    $templateVars = [
134                        ...$templateVars,
135                        'adminDashboardErrorMessage' => $e->getMessage(),
136                    ];
137                }
138            }
139
140            $templateVars = [
141                ...$templateVars,
142                'showVersion' =>
143                    (bool) $this->configuration->get(item: 'main.enableAutoUpdateHint') || $version === 'version',
144            ];
145        }
146
147        return $this->render(file: '@admin/dashboard.twig', context: [
148            ...$this->getHeader($request),
149            ...$this->getFooter(),
150            ...$templateVars,
151        ]);
152    }
153}