PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 5.12.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v5.12.1
5.12.1 5.12.0 5.11.1 5.11.0 5.10.2 5.10.1 trunk 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.1.0 1.1.1 1.1.2 1.1.3 1.2.0 1.3.0 1.3.1 1.3.2 4.0.0 4.0.1 4.0.2 4.0.3 4.0.4 4.1.0 4.1.1 4.1.2 4.1.3 4.10.0 4.11.0 4.12.0 4.13.0 4.13.2 4.13.3 4.13.4 4.13.5 4.14.0 4.14.1 4.14.2 4.15.0 4.15.1 4.15.2 4.15.3 4.2.0 4.3.0 4.3.1 4.4.1 4.4.2 4.5.0 4.6.0 5.0.1 5.0.2 5.0.3 5.0.4 5.0.5 5.0.6 5.0.7 5.0.8 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.10.0 5.2.0 5.2.1 5.2.2 5.3.0 5.3.1 5.3.2 5.3.3 5.6.0 5.6.1 5.7.0 5.7.1 5.8.0 5.8.1 5.8.2
matomo / app / core / Plugin / ControllerAdmin.php
matomo / app / core / Plugin Last commit date
ConsoleCommand 2 months ago Dimension 2 months ago API.php 8 months ago AggregatedMetric.php 2 years ago ArchivedMetric.php 2 weeks ago Archiver.php 2 months ago Categories.php 2 years ago ComponentFactory.php 2 weeks ago ComputedMetric.php 1 year ago ConsoleCommand.php 2 months ago Controller.php 2 months ago ControllerAdmin.php 2 weeks ago Dependency.php 2 months ago LogTablesProvider.php 2 weeks ago Manager.php 2 weeks ago Menu.php 2 weeks ago MetadataLoader.php 2 weeks ago Metric.php 2 months ago PluginException.php 1 year ago ProcessedMetric.php 4 months ago ReleaseChannels.php 4 months ago Report.php 2 months ago ReportsProvider.php 2 years ago RequestProcessors.php 5 months ago Segment.php 4 months ago SettingsProvider.php 2 weeks ago Tasks.php 2 months ago ThemeStyles.php 2 weeks ago ViewDataTable.php 2 weeks ago Visualization.php 2 weeks ago WidgetsProvider.php 4 months ago
ControllerAdmin.php
445 lines
1 <?php
2
3 /**
4 * Matomo - free/libre analytics platform
5 *
6 * @link https://matomo.org
7 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
8 */
9 namespace Piwik\Plugin;
10
11 use Piwik\Config as PiwikConfig;
12 use Piwik\Config;
13 use Piwik\Container\StaticContainer;
14 use Piwik\Date;
15 use Piwik\Db\Schema;
16 use Piwik\Development;
17 use Piwik\Exception\MissingFilePermissionException;
18 use Piwik\Menu\MenuAdmin;
19 use Piwik\Menu\MenuTop;
20 use Piwik\Notification;
21 use Piwik\Notification\Manager as NotificationManager;
22 use Piwik\Piwik;
23 use Piwik\Plugins\CorePluginsAdmin\CorePluginsAdmin;
24 use Piwik\Plugins\Marketplace\Marketplace;
25 use Piwik\Tracker\TrackerConfig;
26 use Piwik\Url;
27 use Piwik\Version;
28 use Piwik\View;
29 use Piwik\ProxyHttp;
30 use Piwik\SettingsPiwik;
31 /**
32 * Base class of plugin controllers that provide administrative functionality.
33 *
34 * See {@link Controller} to learn more about Piwik controllers.
35 *
36 */
37 abstract class ControllerAdmin extends \Piwik\Plugin\Controller
38 {
39 private const NEXT_REQUIRED_MINIMUM_PHP = '8.1';
40 private const NEXT_REQUIRED_MINIMUM_MYSQL = '8.0';
41 private const NEXT_REQUIRED_MINIMUM_MARIADB = '10.6';
42 private static function notifyWhenTrackingStatisticsDisabled()
43 {
44 $statsEnabled = PiwikConfig::getInstance()->Tracker['record_statistics'];
45 if ($statsEnabled == "0") {
46 $notification = new Notification(Piwik::translate('General_StatisticsAreNotRecorded'));
47 $notification->context = Notification::CONTEXT_INFO;
48 Notification\Manager::notify('ControllerAdmin_StatsAreNotRecorded', $notification);
49 }
50 }
51 private static function notifyAnyInvalidLicense()
52 {
53 if (!Marketplace::isMarketplaceEnabled()) {
54 return;
55 }
56 if (Piwik::isUserIsAnonymous()) {
57 return;
58 }
59 if (!Piwik::isUserHasSomeAdminAccess()) {
60 return;
61 }
62 if (Development::isEnabled()) {
63 return;
64 }
65 $expired = StaticContainer::get('Piwik\\Plugins\\Marketplace\\Plugins\\InvalidLicenses');
66 $messageLicenseMissing = $expired->getMessageNoLicense();
67 if (!empty($messageLicenseMissing)) {
68 $notification = new Notification($messageLicenseMissing);
69 $notification->raw = \true;
70 $notification->context = Notification::CONTEXT_ERROR;
71 $notification->title = Piwik::translate('Marketplace_LicenseMissing');
72 Notification\Manager::notify('ControllerAdmin_LicenseMissingWarning', $notification);
73 }
74 $messageExceeded = $expired->getMessageExceededLicenses();
75 if (!empty($messageExceeded)) {
76 $notification = new Notification($messageExceeded);
77 $notification->raw = \true;
78 $notification->context = Notification::CONTEXT_WARNING;
79 $notification->title = Piwik::translate('Marketplace_LicenseExceeded');
80 Notification\Manager::notify('ControllerAdmin_LicenseExceededWarning', $notification);
81 }
82 $messageExpired = $expired->getMessageExpiredLicenses();
83 if (!empty($messageExpired)) {
84 $notification = new Notification($messageExpired);
85 $notification->raw = \true;
86 $notification->context = Notification::CONTEXT_WARNING;
87 $notification->title = Piwik::translate('Marketplace_LicenseExpired');
88 Notification\Manager::notify('ControllerAdmin_LicenseExpiredWarning', $notification);
89 }
90 }
91 private static function notifyAnyInvalidPlugin()
92 {
93 if (!Piwik::hasUserSuperUserAccess()) {
94 return;
95 }
96 $missingPlugins = \Piwik\Plugin\Manager::getInstance()->getMissingPlugins();
97 if (empty($missingPlugins)) {
98 return;
99 }
100 $invalidPluginsWarning = Piwik::translate('CoreAdminHome_InvalidPluginsWarning', [self::getPiwikVersion(), '<strong>' . implode('</strong>,&nbsp;<wbr><strong>', $missingPlugins) . '</strong>']);
101 if (CorePluginsAdmin::isPluginsAdminEnabled()) {
102 $pluginsLink = Url::getCurrentQueryStringWithParametersModified(['module' => 'CorePluginsAdmin', 'action' => 'plugins']);
103 $invalidPluginsWarning .= '<br/>' . Piwik::translate('CoreAdminHome_InvalidPluginsYouCanUninstall', ['<a href="' . $pluginsLink . '">', '</a>']);
104 } else {
105 $invalidPluginsWarning .= '<br/>' . Piwik::translate('CoreAdminHome_InvalidPluginsAdminDisabled');
106 }
107 $notification = new Notification($invalidPluginsWarning);
108 $notification->raw = \true;
109 $notification->context = Notification::CONTEXT_WARNING;
110 $notification->title = Piwik::translate('General_Warning');
111 Notification\Manager::notify('ControllerAdmin_InvalidPluginsWarning', $notification);
112 }
113 /**
114 * Calls {@link setBasicVariablesNoneAdminView()} and {@link setBasicVariablesAdminView()}
115 * using the supplied view.
116 *
117 * @param View $view
118 * @param string $viewType If 'admin', the admin variables are set as well as basic ones.
119 */
120 protected function setBasicVariablesViewAs($view, $viewType = 'admin')
121 {
122 $this->setBasicVariablesNoneAdminView($view);
123 if ($viewType === 'admin') {
124 self::setBasicVariablesAdminView($view);
125 }
126 }
127 private static function notifyIfURLIsNotSecure()
128 {
129 $isURLSecure = ProxyHttp::isHttps();
130 if ($isURLSecure) {
131 return;
132 }
133 if (!Piwik::hasUserSuperUserAccess()) {
134 return;
135 }
136 if (Url::isLocalHost(Url::getCurrentHost())) {
137 return;
138 }
139 if (Development::isEnabled()) {
140 return;
141 }
142 $message = Piwik::translate('General_CurrentlyUsingUnsecureHttp');
143 $message .= " ";
144 $message .= Piwik::translate('General_ReadThisToLearnMore', [Url::getExternalLinkTag('https://matomo.org/faq/how-to/faq_91/'), '</a>']);
145 $notification = new Notification($message);
146 $notification->context = Notification::CONTEXT_WARNING;
147 $notification->raw = \true;
148 Notification\Manager::notify('ControllerAdmin_HttpIsUsed', $notification);
149 }
150 private static function notifyIfDevelopmentModeOnButNotInstalledThroughGit()
151 {
152 if (!Piwik::hasUserSuperUserAccess()) {
153 return;
154 }
155 if (!Development::isEnabled()) {
156 return;
157 }
158 if (SettingsPiwik::isGitDeployment()) {
159 return;
160 }
161 $message = Piwik::translate('General_WarningDevelopmentModeOnButNotGitInstalled');
162 $notification = new Notification($message);
163 $notification->context = Notification::CONTEXT_WARNING;
164 $notification->raw = \true;
165 $notification->flags = Notification::FLAG_CLEAR;
166 Notification\Manager::notify('ControllerAdmin_DevelopmentModeOn', $notification);
167 }
168 /**
169 * @ignore
170 */
171 public static function displayWarningIfConfigFileNotWritable()
172 {
173 try {
174 PiwikConfig::getInstance()->checkConfigIsWritable();
175 } catch (MissingFilePermissionException $exception) {
176 $notification = new Notification($exception->getMessage());
177 $notification->raw = \true;
178 $notification->context = Notification::CONTEXT_WARNING;
179 Notification\Manager::notify('ControllerAdmin_ConfigNotWriteable', $notification);
180 }
181 }
182 private static function notifyIfEAcceleratorIsUsed()
183 {
184 $isEacceleratorUsed = ini_get('eaccelerator.enable');
185 if (empty($isEacceleratorUsed)) {
186 return;
187 }
188 $message = sprintf("You are using the PHP accelerator & optimizer eAccelerator which is known to be not compatible with Matomo.\n We have disabled eAccelerator, which might affect the performance of Matomo.\n Read the %srelated ticket%s for more information and how to fix this problem.", '<a rel="noreferrer noopener" target="_blank" href="https://github.com/matomo-org/matomo/issues/4439">', '</a>');
189 $notification = new Notification($message);
190 $notification->context = Notification::CONTEXT_WARNING;
191 $notification->raw = \true;
192 Notification\Manager::notify('ControllerAdmin_EacceleratorIsUsed', $notification);
193 }
194 /**
195 * PHP Version required by the next major Matomo version
196 * @return string
197 */
198 private static function getNextRequiredMinimumPHP()
199 {
200 return self::NEXT_REQUIRED_MINIMUM_PHP;
201 }
202 private static function isUsingPhpVersionCompatibleWithNextPiwik($phpVersion = \PHP_VERSION)
203 {
204 return version_compare($phpVersion, self::getNextRequiredMinimumPHP(), '>=');
205 }
206 private static function notifyWhenPhpVersionIsNotCompatibleWithNextMajorPiwik()
207 {
208 if (defined('PIWIK_TEST_MODE')) {
209 // to avoid changing every admin UI test
210 return;
211 }
212 if (!Piwik::hasUserSuperUserAccess()) {
213 return;
214 }
215 if (self::isUsingPhpVersionCompatibleWithNextPiwik()) {
216 return;
217 }
218 $youMustUpgradePHP = Piwik::translate('General_YouMustUpgradePhpVersionToReceiveNextMajorMatomo');
219 $message = Piwik::translate('General_MatomoCannotBeUpgradedToNextMajorBecausePhpIsTooOld') . ' ' . sprintf(Piwik::translate('General_PleaseUpgradeYourPhpVersionForNextMajorMatomo'), self::getNextRequiredMinimumPHP());
220 $notification = new Notification($message);
221 $notification->title = $youMustUpgradePHP;
222 $notification->priority = Notification::PRIORITY_LOW;
223 $notification->context = Notification::CONTEXT_WARNING;
224 $notification->type = Notification::TYPE_TRANSIENT;
225 $notification->flags = Notification::FLAG_NO_CLEAR;
226 NotificationManager::notify('PHPVersionTooOldForNewestPiwikCheck', $notification);
227 }
228 private static function getNextRequiredMinimumDatabaseVersion(string $databaseType, string $databaseVersion) : ?string
229 {
230 $databaseType = self::getEffectiveDatabaseType($databaseType, $databaseVersion);
231 if ($databaseType === 'mysql') {
232 return self::NEXT_REQUIRED_MINIMUM_MYSQL;
233 }
234 if ($databaseType === 'mariadb') {
235 return self::NEXT_REQUIRED_MINIMUM_MARIADB;
236 }
237 return null;
238 }
239 private static function isUsingDatabaseVersionCompatibleWithNextPiwik(string $databaseType, string $databaseVersion) : bool
240 {
241 $requiredVersion = self::getNextRequiredMinimumDatabaseVersion($databaseType, $databaseVersion);
242 if ($requiredVersion === null) {
243 return \true;
244 }
245 return version_compare(self::getComparableDatabaseVersion($databaseVersion), $requiredVersion, '>=');
246 }
247 private static function getEffectiveDatabaseType(string $databaseType, string $databaseVersion) : string
248 {
249 if (stripos($databaseVersion, 'mariadb') !== \false) {
250 return 'mariadb';
251 }
252 return strtolower($databaseType);
253 }
254 private static function getComparableDatabaseVersion(string $databaseVersion) : string
255 {
256 if (stripos($databaseVersion, 'mariadb') !== \false && preg_match('/5\\.5\\.5-(\\d+(?:\\.\\d+){0,2})-MariaDB/i', $databaseVersion, $matches)) {
257 return $matches[1];
258 }
259 if (preg_match('/\\d+(?:\\.\\d+){0,2}/', $databaseVersion, $matches)) {
260 return $matches[0];
261 }
262 return $databaseVersion;
263 }
264 private static function notifyWhenDatabaseVersionIsNotCompatibleWithNextMajorPiwik() : void
265 {
266 if (defined('PIWIK_TEST_MODE')) {
267 // to avoid changing every admin UI test
268 return;
269 }
270 if (!Piwik::hasUserSuperUserAccess()) {
271 return;
272 }
273 $databaseType = Schema::getInstance()->getDatabaseType();
274 $databaseVersion = Schema::getInstance()->getVersion();
275 if (self::isUsingDatabaseVersionCompatibleWithNextPiwik($databaseType, $databaseVersion)) {
276 return;
277 }
278 $requiredVersion = self::getNextRequiredMinimumDatabaseVersion($databaseType, $databaseVersion);
279 $databaseType = self::getEffectiveDatabaseType($databaseType, $databaseVersion) === 'mariadb' ? 'MariaDB' : 'MySQL';
280 $message = Piwik::translate('General_MatomoCannotBeUpgradedToNextMajorBecauseDatabaseIsTooOld') . ' ' . Piwik::translate('General_PleaseUpgradeYourDatabaseVersionForNextMajorMatomo', [$databaseType, $requiredVersion]);
281 $notification = new Notification($message);
282 $notification->title = Piwik::translate('General_YouMustUpgradeDatabaseVersionToReceiveNextMajorMatomo');
283 $notification->priority = Notification::PRIORITY_LOW;
284 $notification->context = Notification::CONTEXT_WARNING;
285 $notification->type = Notification::TYPE_TRANSIENT;
286 $notification->flags = Notification::FLAG_NO_CLEAR;
287 NotificationManager::notify('DatabaseVersionTooOldForNewestPiwikCheck', $notification);
288 }
289 private static function notifyWhenPhpVersionIsEOL()
290 {
291 if (defined('PIWIK_TEST_MODE')) {
292 // to avoid changing every admin UI test
293 return;
294 }
295 $notifyPhpIsEOL = Piwik::hasUserSuperUserAccess() && self::isPhpVersionEOL();
296 if (!$notifyPhpIsEOL) {
297 return;
298 }
299 $deprecatedMajorPhpVersion = \PHP_MAJOR_VERSION . '.' . \PHP_MINOR_VERSION;
300 $message = '';
301 if (version_compare(\PHP_VERSION, self::getNextRequiredMinimumPHP(), '<')) {
302 $message = Piwik::translate('General_WarningPiwikWillStopSupportingPHPVersion', [$deprecatedMajorPhpVersion, self::getNextRequiredMinimumPHP()]) . '<br/>';
303 }
304 $message .= Piwik::translate('General_WarningPhpVersionXIsTooOld', $deprecatedMajorPhpVersion);
305 $notification = new Notification($message);
306 $notification->raw = \true;
307 $notification->title = Piwik::translate('General_Warning');
308 $notification->priority = Notification::PRIORITY_LOW;
309 $notification->context = Notification::CONTEXT_WARNING;
310 $notification->type = Notification::TYPE_TRANSIENT;
311 $notification->flags = Notification::FLAG_NO_CLEAR;
312 NotificationManager::notify('PHPVersionCheck', $notification);
313 }
314 private static function notifyWhenDatabaseVersionIsEOL() : void
315 {
316 if (defined('PIWIK_TEST_MODE')) {
317 // to avoid changing every admin UI test
318 return;
319 }
320 $isEOL = Piwik::hasUserSuperUserAccess() && Schema::getInstance()->hasReachedEOL();
321 if (!$isEOL) {
322 return;
323 }
324 $databaseVersion = Schema::getInstance()->getVersion();
325 $message = Piwik::translate('General_WarningDatabaseVersionXIsTooOld', [Schema::getInstance()->getDatabaseType(), $databaseVersion]);
326 $notification = new Notification($message);
327 $notification->raw = \true;
328 $notification->title = Piwik::translate('General_Warning');
329 $notification->priority = Notification::PRIORITY_LOW;
330 $notification->context = Notification::CONTEXT_WARNING;
331 $notification->type = Notification::TYPE_TRANSIENT;
332 $notification->flags = Notification::FLAG_NO_CLEAR;
333 NotificationManager::notify('DatabaseVersionCheck', $notification);
334 }
335 private static function notifyWhenDebugOnDemandIsEnabled($trackerSetting)
336 {
337 if (!Development::isEnabled() && Piwik::hasUserSuperUserAccess() && TrackerConfig::getConfigValue($trackerSetting)) {
338 $message = Piwik::translate('General_WarningDebugOnDemandEnabled');
339 $message = sprintf($message, '"' . $trackerSetting . '"', '"[Tracker] ' . $trackerSetting . '"', '"0"', '"config/config.ini.php"');
340 $notification = new Notification($message);
341 $notification->title = Piwik::translate('General_Warning');
342 $notification->priority = Notification::PRIORITY_LOW;
343 $notification->context = Notification::CONTEXT_WARNING;
344 $notification->type = Notification::TYPE_TRANSIENT;
345 $notification->flags = Notification::FLAG_NO_CLEAR;
346 NotificationManager::notify('Tracker' . $trackerSetting, $notification);
347 }
348 }
349 /**
350 * Assigns view properties that would be useful to views that render admin pages.
351 *
352 * Assigns the following variables:
353 *
354 * - **statisticsNotRecorded** - Set to true if the `[Tracker] record_statistics` INI
355 * config is `0`. If not `0`, this variable will not be defined.
356 * - **topMenu** - The result of `MenuTop::getInstance()->getMenu()`.
357 * - **enableFrames** - The value of the `[General] enable_framed_pages` INI config option. If
358 * true, {@link Piwik\View::setXFrameOptions()} is called on the view.
359 * - **isSuperUser** - Whether the current user is a superuser or not.
360 * - **usingOldGeoIPPlugin** - Whether this Piwik install is currently using the old GeoIP
361 * plugin or not.
362 * - **invalidPluginsWarning** - Set if some of the plugins to load (determined by INI configuration)
363 * are invalid or missing.
364 * - **phpVersion** - The current PHP version.
365 * - **phpIsNewEnough** - Whether the current PHP version is new enough to run Piwik.
366 * - **adminMenu** - The result of `MenuAdmin::getInstance()->getMenu()`.
367 *
368 * @api
369 */
370 public static function setBasicVariablesAdminView(View $view)
371 {
372 self::notifyWhenTrackingStatisticsDisabled();
373 self::notifyIfEAcceleratorIsUsed();
374 self::notifyIfURLIsNotSecure();
375 self::notifyIfDevelopmentModeOnButNotInstalledThroughGit();
376 $view->topMenu = MenuTop::getInstance()->getMenu();
377 $view->isDataPurgeSettingsEnabled = self::isDataPurgeSettingsEnabled();
378 $enableFrames = PiwikConfig::getInstance()->General['enable_framed_settings'];
379 $view->enableFrames = $enableFrames;
380 if (!$enableFrames) {
381 $view->setXFrameOptions('sameorigin');
382 }
383 $view->isSuperUser = Piwik::hasUserSuperUserAccess();
384 self::notifyAnyInvalidLicense();
385 self::notifyAnyInvalidPlugin();
386 self::notifyWhenPhpVersionIsEOL();
387 self::notifyWhenPhpVersionIsNotCompatibleWithNextMajorPiwik();
388 self::notifyWhenDatabaseVersionIsNotCompatibleWithNextMajorPiwik();
389 self::notifyWhenDatabaseVersionIsEOL();
390 self::notifyWhenDebugOnDemandIsEnabled('debug');
391 self::notifyWhenDebugOnDemandIsEnabled('debug_on_demand');
392 /**
393 * Posted when rendering an admin page and notifications about any warnings or errors should be triggered.
394 * You can use it for example when you have a plugin that needs to be configured in order to work and the
395 * plugin has not been configured yet. It can be also used to cancel / remove other notifications by calling
396 * eg `Notification\Manager::cancel($notificationId)`.
397 *
398 * **Example**
399 *
400 * public function onTriggerAdminNotifications(Piwik\Widget\WidgetsList $list)
401 * {
402 * if ($pluginFooIsNotConfigured) {
403 * $notification = new Notification('The plugin foo has not been configured yet');
404 * $notification->context = Notification::CONTEXT_WARNING;
405 * Notification\Manager::notify('fooNotConfigured', $notification);
406 * }
407 * }
408 *
409 */
410 Piwik::postEvent('Controller.triggerAdminNotifications');
411 $view->adminMenu = MenuAdmin::getInstance()->getMenu();
412 $notifications = $view->notifications;
413 if (empty($notifications)) {
414 $view->notifications = NotificationManager::getAllNotificationsToDisplay();
415 NotificationManager::cancelAllNonPersistent();
416 }
417 }
418 public static function isDataPurgeSettingsEnabled()
419 {
420 return (bool) Config::getInstance()->General['enable_delete_old_data_settings_admin'];
421 }
422 protected static function getPiwikVersion()
423 {
424 return "Matomo " . Version::VERSION;
425 }
426 private static function isPhpVersionEOL()
427 {
428 $phpEOL = '8.0';
429 // End of security update for certain PHP versions as of https://www.php.net/supported-versions
430 if (Date::today()->isLater(Date::factory('2025-12-31'))) {
431 $phpEOL = '8.1';
432 }
433 if (Date::today()->isLater(Date::factory('2026-12-31'))) {
434 $phpEOL = '8.2';
435 }
436 if (Date::today()->isLater(Date::factory('2027-12-31'))) {
437 $phpEOL = '8.3';
438 }
439 if (Date::today()->isLater(Date::factory('2028-12-31'))) {
440 $phpEOL = '8.4';
441 }
442 return version_compare(\PHP_MAJOR_VERSION . '.' . \PHP_MINOR_VERSION, $phpEOL, '<=');
443 }
444 }
445