PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 1.3.1
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v1.3.1
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 / classes / WpMatomo / Admin / SystemReport.php
matomo / classes / WpMatomo / Admin Last commit date
TrackingSettings 5 years ago views 5 years ago AccessSettings.php 6 years ago Admin.php 6 years ago AdminSettings.php 6 years ago AdminSettingsInterface.php 6 years ago AdvancedSettings.php 6 years ago Dashboard.php 6 years ago ExclusionSettings.php 6 years ago GeolocationSettings.php 6 years ago GetStarted.php 6 years ago Info.php 6 years ago Marketplace.php 6 years ago Menu.php 5 years ago PrivacySettings.php 5 years ago SafeModeMenu.php 6 years ago Summary.php 5 years ago SystemReport.php 5 years ago TrackingSettings.php 5 years ago
SystemReport.php
1289 lines
1 <?php
2 /**
3 * Matomo - free/libre analytics platform
4 *
5 * @link https://matomo.org
6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
7 * @package matomo
8 */
9
10 namespace WpMatomo\Admin;
11
12 use DeviceDetector\DeviceDetector;
13 use Piwik\CliMulti;
14 use Piwik\Common;
15 use Piwik\Config;
16 use Piwik\Container\StaticContainer;
17 use Piwik\Date;
18 use Piwik\DeviceDetector\DeviceDetectorFactory;
19 use Piwik\Filesystem;
20 use Piwik\MetricsFormatter;
21 use Piwik\Plugins\CoreAdminHome\API;
22 use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult;
23 use Piwik\Plugins\Diagnostics\DiagnosticService;
24 use Piwik\Plugins\UserCountry\LocationProvider;
25 use Piwik\Tracker\Failures;
26 use WpMatomo\Bootstrap;
27 use WpMatomo\Capabilities;
28 use WpMatomo\Installer;
29 use WpMatomo\Logger;
30 use WpMatomo\Paths;
31 use WpMatomo\ScheduledTasks;
32 use WpMatomo\Settings;
33 use WpMatomo\Site;
34 use WpMatomo\Site\Sync as SiteSync;
35 use WpMatomo\User\Sync as UserSync;
36
37 if ( ! defined( 'ABSPATH' ) ) {
38 exit; // if accessed directly
39 }
40
41 class SystemReport {
42 const NONCE_NAME = 'matomo_troubleshooting';
43 const TROUBLESHOOT_SYNC_USERS = 'matomo_troubleshooting_action_site_users';
44 const TROUBLESHOOT_SYNC_ALL_USERS = 'matomo_troubleshooting_action_all_users';
45 const TROUBLESHOOT_SYNC_SITE = 'matomo_troubleshooting_action_site';
46 const TROUBLESHOOT_SYNC_ALL_SITES = 'matomo_troubleshooting_action_all_sites';
47 const TROUBLESHOOT_CLEAR_MATOMO_CACHE = 'matomo_troubleshooting_action_clear_matomo_cache';
48 const TROUBLESHOOT_ARCHIVE_NOW = 'matomo_troubleshooting_action_archive_now';
49 const TROUBLESHOOT_UPDATE_GEOIP_DB = 'matomo_troubleshooting_action_update_geoipdb';
50 const TROUBLESHOOT_CLEAR_LOGS = 'matomo_troubleshooting_action_clear_logs';
51
52 private $not_compatible_plugins = array(
53 'background-manager', // Uses an old version of Twig and plugin is no longer maintained.
54 'data-tables-generator-by-supsystic', // uses an old version of twig causing some styles to go funny in the reporting and admin
55 );
56
57 private $valid_tabs = array( 'troubleshooting' );
58
59 /**
60 * @var Settings
61 */
62 private $settings;
63
64 /**
65 * @var Logger
66 */
67 private $logger;
68
69 public function __construct( Settings $settings ) {
70 $this->settings = $settings;
71 $this->logger = new Logger();
72 }
73
74 public function get_not_compatible_plugins() {
75 return $this->not_compatible_plugins;
76 }
77
78 private function execute_troubleshoot_if_needed() {
79 if ( ! empty( $_POST )
80 && is_admin()
81 && check_admin_referer( self::NONCE_NAME )
82 && current_user_can( Capabilities::KEY_SUPERUSER ) ) {
83 if ( ! empty( $_POST[ self::TROUBLESHOOT_ARCHIVE_NOW ] ) ) {
84 Bootstrap::do_bootstrap();
85 $scheduled_tasks = new ScheduledTasks( $this->settings );
86
87 if (!defined('PIWIK_ARCHIVE_NO_TRUNCATE')) {
88 define('PIWIK_ARCHIVE_NO_TRUNCATE', 1); // when triggering it manually, we prefer the full error message
89 }
90
91 try {
92 // force invalidation of archive to ensure it actually will rearchive the data
93 $site = new Site();
94 $idsite = $site->get_current_matomo_site_id();
95 if ($idsite) {
96 $timezone = \Piwik\Site::getTimezoneFor($idsite);
97 $now_string = \Piwik\Date::factory('now', $timezone)->toString();
98 foreach (array('day', 'week', 'month') as $period) {
99 API::getInstance()->invalidateArchivedReports($idsite, $now_string, $period, false, false);
100 }
101 }
102 } catch (\Exception $e) {
103 $this->logger->log_exception('archive_invalidate', $e);
104 }
105
106 try {
107 $errors = $scheduled_tasks->archive( $force = true, $throw_exception = false );
108 } catch (\Exception $e) {
109 echo '<div class="error"><p>' . esc_html__('Matomo Archive Error', 'matomo') . ': '. esc_html(matomo_anonymize_value($e->getMessage() . ' =>' . $this->logger->get_readable_trace($e))) . '</p></div>';
110 throw $e;
111 }
112
113 if ( ! empty( $errors ) ) {
114 echo '<div class="notice notice-warning"><p>Matomo Archive Warnings: ';
115 foreach ($errors as $error) {
116 echo nl2br(esc_html(matomo_anonymize_value(var_export($error, 1))));
117 echo '<br/>';
118 }
119 echo '</p></div>';
120 }
121 }
122
123 if ( ! empty( $_POST[ self::TROUBLESHOOT_CLEAR_MATOMO_CACHE ] ) ) {
124 $paths = new Paths();
125 $paths->clear_cache_dir();
126 // we first delete the cache dir manually just in case there's something
127 // going wrong with matomo and bootstrapping would not even be possible.
128 Bootstrap::do_bootstrap();
129 Filesystem::deleteAllCacheOnUpdate();
130 }
131
132 if ( ! empty( $_POST[ self::TROUBLESHOOT_UPDATE_GEOIP_DB ] ) ) {
133 $scheduled_tasks = new ScheduledTasks( $this->settings );
134 $scheduled_tasks->update_geo_ip2_db();
135 }
136
137 if ( ! empty( $_POST[ self::TROUBLESHOOT_CLEAR_LOGS ] ) ) {
138 $this->logger->clear_logged_exceptions();
139 }
140
141 if ( ! $this->settings->is_network_enabled() || ! is_network_admin() ) {
142 if ( ! empty( $_POST[ self::TROUBLESHOOT_SYNC_USERS ] ) ) {
143 $sync = new UserSync();
144 $sync->sync_current_users();
145 }
146 if ( ! empty( $_POST[ self::TROUBLESHOOT_SYNC_SITE ] ) ) {
147 $sync = new SiteSync( $this->settings );
148 $sync->sync_current_site();
149 }
150 }
151 if ( $this->settings->is_network_enabled() ) {
152 if ( ! empty( $_POST[ self::TROUBLESHOOT_SYNC_ALL_SITES ] ) ) {
153 $sync = new SiteSync( $this->settings );
154 $sync->sync_all();
155 }
156 if ( ! empty( $_POST[ self::TROUBLESHOOT_SYNC_ALL_USERS ] ) ) {
157 $sync = new UserSync();
158 $sync->sync_all();
159 }
160 }
161 }
162 }
163
164 public function show() {
165 $this->execute_troubleshoot_if_needed();
166
167 $settings = $this->settings;
168
169 $matomo_active_tab = '';
170 if ( isset( $_GET['tab'] ) && in_array( $_GET['tab'], $this->valid_tabs, true ) ) {
171 $matomo_active_tab = $_GET['tab'];
172 }
173
174 $matomo_tables = array();
175 if ( empty( $matomo_active_tab ) ) {
176 $matomo_tables = array(
177 array(
178 'title' => 'Matomo',
179 'rows' => $this->get_matomo_info(),
180 'has_comments' => true,
181 ),
182 array(
183 'title' => 'WordPress',
184 'rows' => $this->get_wordpress_info(),
185 'has_comments' => true,
186 ),
187 array(
188 'title' => 'WordPress Plugins',
189 'rows' => $this->get_plugins_info(),
190 'has_comments' => true,
191 ),
192 array(
193 'title' => 'Server',
194 'rows' => $this->get_server_info(),
195 'has_comments' => true,
196 ),
197 array(
198 'title' => 'Database',
199 'rows' => $this->get_db_info(),
200 'has_comments' => true,
201 ),
202 array(
203 'title' => 'Browser',
204 'rows' => $this->get_browser_info(),
205 'has_comments' => true,
206 ),
207 );
208 }
209 $matomo_tables = apply_filters('matomo_systemreport_tables', $matomo_tables);
210 $matomo_tables = $this->add_errors_first( $matomo_tables );
211 $matomo_has_warning_and_no_errors = $this->has_only_warnings_no_error( $matomo_tables );
212
213 $matomo_has_exception_logs = $this->logger->get_last_logged_entries();
214
215 include dirname( __FILE__ ) . '/views/systemreport.php';
216 }
217
218 private function has_only_warnings_no_error( $report_tables ) {
219 $has_warning = false;
220 $has_error = false;
221 foreach ( $report_tables as $report_table ) {
222 foreach ( $report_table['rows'] as $row ) {
223 if ( ! empty( $row['is_error'] ) ) {
224 $has_error = true;
225 }
226 if ( ! empty( $row['is_warning'] ) ) {
227 $has_warning = true;
228 }
229 }
230 }
231
232 return $has_warning && ! $has_error;
233 }
234
235 private function add_errors_first( $report_tables ) {
236 $errors = array(
237 'title' => 'Errors',
238 'rows' => array(),
239 'has_comments' => true,
240 );
241 foreach ( $report_tables as $report_table ) {
242 foreach ( $report_table['rows'] as $row ) {
243 if ( ! empty( $row['is_error'] ) ) {
244 $errors['rows'][] = $row;
245 }
246 }
247 }
248
249 if ( ! empty( $errors['rows'] ) ) {
250 array_unshift( $report_tables, $errors );
251 }
252
253 return $report_tables;
254 }
255
256 private function check_file_exists_and_writable( $rows, $path_to_check, $title, $required ) {
257 $file_exists = file_exists( $path_to_check );
258 $file_readable = is_readable( $path_to_check );
259 $file_writable = is_writable( $path_to_check );
260 $comment = '"' . $path_to_check . '" ';
261 if ( ! $file_exists ) {
262 $comment .= sprintf( esc_html__( '%s does not exist. ', 'matomo' ), $title );
263 }
264 if ( ! $file_readable ) {
265 $comment .= sprintf( esc_html__( '%s is not readable. ', 'matomo' ), $title );
266 }
267 if ( ! $file_writable ) {
268 $comment .= sprintf( esc_html__( '%s is not writable. ', 'matomo' ), $title );
269 }
270
271 $rows[] = array(
272 'name' => sprintf( esc_html__( '%s exists and is writable.', 'matomo' ), $title ),
273 'value' => $file_exists && $file_readable && $file_writable ? esc_html__( 'Yes', 'matomo' ) : esc_html__( 'No', 'matomo' ),
274 'comment' => $comment,
275 'is_error' => $required && ( ! $file_exists || ! $file_readable ),
276 'is_warning' => ! $required && ( ! $file_exists || ! $file_readable ),
277 );
278
279 return $rows;
280 }
281
282 private function get_matomo_info() {
283 $rows = array();
284
285 $plugin_data = get_plugin_data( MATOMO_ANALYTICS_FILE, $markup = false, $translate = false );
286 $install_time = get_option(Installer::OPTION_NAME_INSTALL_DATE);
287
288 $rows[] = array(
289 'name' => esc_html__( 'Matomo Plugin Version', 'matomo' ),
290 'value' => $plugin_data['Version'],
291 'comment' => '',
292 );
293
294 $paths = new Paths();
295 $path_config_file = $paths->get_config_ini_path();
296 $rows = $this->check_file_exists_and_writable( $rows, $path_config_file, 'Config', true );
297
298 $path_tracker_file = $paths->get_matomo_js_upload_path();
299 $rows = $this->check_file_exists_and_writable( $rows, $path_tracker_file, 'JS Tracker', false );
300
301 $rows[] = array(
302 'name' => esc_html__( 'Plugin directories', 'matomo' ),
303 'value' => ! empty( $GLOBALS['MATOMO_PLUGIN_DIRS'] ) ? 'Yes' : 'No',
304 'comment' => ! empty( $GLOBALS['MATOMO_PLUGIN_DIRS'] ) ? wp_json_encode( $GLOBALS['MATOMO_PLUGIN_DIRS'] ) : '',
305 );
306
307 $tmp_dir = $paths->get_tmp_dir();
308
309 $rows[] = array(
310 'name' => esc_html__( 'Tmp directory writable', 'matomo' ),
311 'value' => is_writable( $tmp_dir ),
312 'comment' => $tmp_dir,
313 );
314
315 if ( ! empty( $_SERVER['MATOMO_WP_ROOT_PATH'] ) ) {
316 $custom_path = rtrim( $_SERVER['MATOMO_WP_ROOT_PATH'], '/' ) . '/wp-load.php';
317 $path_exists = file_exists( $custom_path );
318 $comment = '';
319 if ( ! $path_exists ) {
320 $comment = 'It seems the path does not point to the WP root directory.';
321 }
322
323 $rows[] = array(
324 'name' => 'Custom MATOMO_WP_ROOT_PATH',
325 'value' => $path_exists,
326 'is_error' => ! $path_exists,
327 'comment' => $comment,
328 );
329 }
330
331 $report = null;
332
333 if ( ! \WpMatomo::is_safe_mode() ) {
334 try {
335 Bootstrap::do_bootstrap();
336 /** @var DiagnosticService $service */
337 $service = StaticContainer::get( DiagnosticService::class );
338 $report = $service->runDiagnostics();
339
340 $rows[] = array(
341 'name' => esc_html__( 'Matomo Version', 'matomo' ),
342 'value' => \Piwik\Version::VERSION,
343 'comment' => '',
344 );
345 } catch ( \Exception $e ) {
346 $rows[] = array(
347 'name' => esc_html__( 'Matomo System Check', 'matomo' ),
348 'value' => 'Failed to run Matomo system check.',
349 'comment' => $e->getMessage(),
350 );
351 }
352 }
353
354 $site = new Site();
355 $idsite = $site->get_current_matomo_site_id();
356
357 $rows[] = array(
358 'name' => esc_html__( 'Matomo Blog idSite', 'matomo' ),
359 'value' => $idsite,
360 'comment' => '',
361 );
362
363 $install_date = '';
364 if (!empty($install_time)) {
365 $install_date = 'Install date: '. $this->convert_time_to_date($install_time, true, false);
366 }
367
368 $rows[] = array(
369 'name' => esc_html__( 'Matomo Install Version', 'matomo' ),
370 'value' => get_option(Installer::OPTION_NAME_INSTALL_VERSION),
371 'comment' => $install_date,
372 );
373
374 $rows[] = array(
375 'section' => 'Endpoints',
376 );
377
378 $rows[] = array(
379 'name' => 'Matomo JavaScript Tracker URL',
380 'value' => '',
381 'comment' => $paths->get_js_tracker_url_in_matomo_dir(),
382 );
383
384 $rows[] = array(
385 'name' => 'Matomo JavaScript Tracker - WP Rest API',
386 'value' => '',
387 'comment' => $paths->get_js_tracker_rest_api_endpoint(),
388 );
389
390 $rows[] = array(
391 'name' => 'Matomo HTTP Tracking API',
392 'value' => '',
393 'comment' => $paths->get_tracker_api_url_in_matomo_dir(),
394 );
395
396 $rows[] = array(
397 'name' => 'Matomo HTTP Tracking API - WP Rest API',
398 'value' => '',
399 'comment' => $paths->get_tracker_api_rest_api_endpoint(),
400 );
401
402 $matomo_plugin_dir_name = basename(dirname(MATOMO_ANALYTICS_FILE));
403 if ($matomo_plugin_dir_name !== 'matomo') {
404 $rows[] = array(
405 'name' => 'Matomo Plugin Name is correct',
406 'value' => false,
407 'is_error' => true,
408 'comment' => 'The plugin name should be "matomo" but seems to be "' . $matomo_plugin_dir_name . '". As a result, admin pages and other features might not work. You might need to rename the directory name of this plugin and reactive the plugin.',
409 );
410 } elseif (!is_plugin_active('matomo/matomo.php')) {
411 $rows[] = array(
412 'name' => 'Matomo Plugin not active',
413 'value' => false,
414 'is_error' => true,
415 'comment' => 'It seems WordPress thinks that `matomo/matomo.php` is not active. As a result Matomo reporting and admin pages may not work. You may be able to fix this by deactivating and activating the Matomo Analytics plugin. One of the reasons this could happen is that you used to have Matomo installed in the wrong folder.',
416 );
417 }
418
419 $rows[] = array(
420 'section' => 'Crons',
421 );
422
423 $scheduled_tasks = new ScheduledTasks( $this->settings );
424 $all_events = $scheduled_tasks->get_all_events();
425
426 $rows[] = array(
427 'name' => esc_html__( 'Server time', 'matomo' ),
428 'value' => $this->convert_time_to_date( time(), false ),
429 'comment' => '',
430 );
431
432 $rows[] = array(
433 'name' => esc_html__( 'Blog time', 'matomo' ),
434 'value' => $this->convert_time_to_date( time(), true ),
435 'comment' => esc_html__( 'Below dates are shown in blog timezone', 'matomo' ),
436 );
437
438 foreach ( $all_events as $event_name => $event_config ) {
439 $last_run_before = $scheduled_tasks->get_last_time_before_cron( $event_name );
440 $last_run_after = $scheduled_tasks->get_last_time_after_cron( $event_name );
441
442 $next_scheduled = wp_next_scheduled( $event_name );
443
444 $comment = ' Last started: ' . $this->convert_time_to_date( $last_run_before, true, true ) . '.';
445 $comment .= ' Last ended: ' . $this->convert_time_to_date( $last_run_after, true, true ) . '.';
446 $comment .= ' Interval: ' . $event_config['interval'];
447
448 $rows[] = array(
449 'name' => $event_config['name'],
450 'value' => 'Next run: ' . $this->convert_time_to_date( $next_scheduled, true, true ),
451 'comment' => $comment,
452 );
453 }
454
455 $suports_async = false;
456 if ( ! \WpMatomo::is_safe_mode() && $report ) {
457 $rows[] = array(
458 'section' => esc_html__( 'Mandatory checks', 'matomo' ),
459 );
460
461 $rows = $this->add_diagnostic_results( $rows, $report->getMandatoryDiagnosticResults() );
462
463 $rows[] = array(
464 'section' => esc_html__( 'Optional checks', 'matomo' ),
465 );
466 $rows = $this->add_diagnostic_results( $rows, $report->getOptionalDiagnosticResults() );
467
468 $cli_multi = new CliMulti();
469 $suports_async = $cli_multi->supportsAsync();
470
471 $rows[] = array(
472 'name' => 'Supports Async Archiving',
473 'value' => $suports_async,
474 'comment' => '',
475 );
476
477 $location_provider = LocationProvider::getCurrentProvider();
478 if ($location_provider) {
479 $rows[] = array(
480 'name' => 'Location provider ID',
481 'value' => $location_provider->getId(),
482 'comment' => '',
483 );
484 $rows[] = array(
485 'name' => 'Location provider available',
486 'value' => $location_provider->isAvailable(),
487 'comment' => '',
488 );
489 $rows[] = array(
490 'name' => 'Location provider working',
491 'value' => $location_provider->isWorking(),
492 'comment' => '',
493 );
494 }
495
496 if ( ! \WpMatomo::is_safe_mode() ) {
497 Bootstrap::do_bootstrap();
498 $general = Config::getInstance()->General;
499
500 if (empty($general['proxy_client_headers'])) {
501 foreach (AdvancedSettings::$valid_host_headers as $header) {
502 if (!empty($_SERVER[$header])) {
503 $rows[] = array(
504 'name' => 'Proxy header',
505 'value' => $header,
506 'is_warning' => true,
507 'comment' => 'A proxy header is set which means you maybe need to configure a proxy header in the Advanced settings to make location reporting work. If the location in your reports is detected correctly, you can ignore this warning. Learn more: https://matomo.org/faq/wordpress/how-do-i-fix-the-proxy-header-warning-in-the-matomo-for-wordpress-system-report/',
508 );
509 }
510 }
511 }
512
513 }
514
515 $num_days_check_visits = 5;
516 $had_visits = $this->had_visits_in_last_days($num_days_check_visits);
517 if ($had_visits === false || $had_visits === true) {
518 // do not show info if we could not detect it (had_visits === null)
519 $comment = '';
520 if (!$had_visits) {
521 $comment = 'It looks like there were no visits in the last ' . $num_days_check_visits . ' days. This may be expected if tracking is disabled, you have not added the tracking code, or your website does not have many visitors in general and you exclude your own visits.';
522 }
523
524 $rows[] = array(
525 'name' => 'Had visit in last ' . $num_days_check_visits . ' days',
526 'value' => $had_visits,
527 'is_warning' => !$had_visits && $this->settings->is_tracking_enabled(),
528 'comment' => $comment,
529 );
530 }
531
532 }
533
534 $rows[] = array(
535 'section' => 'Matomo Settings',
536 );
537
538 // always show these settings
539 $global_settings_always_show = array(
540 'track_mode',
541 'track_codeposition',
542 'track_api_endpoint',
543 'track_js_endpoint',
544 );
545 foreach ( $global_settings_always_show as $key ) {
546 $rows[] = array(
547 'name' => ucfirst( str_replace( '_', ' ', $key ) ),
548 'value' => $this->settings->get_global_option( $key ),
549 'comment' => '',
550 );
551 }
552
553 // otherwise show only few customised settings
554 // mostly only numeric values and booleans to not eg accidentally show anything that would store a token etc
555 // like we don't want to show license key etc
556 foreach ( $this->settings->get_customised_global_settings() as $key => $val ) {
557 if ( is_numeric( $val ) || is_bool( $val ) || 'track_content' === $key || 'track_user_id' === $key || 'core_version' === $key || 'version_history' === $key || 'mail_history' === $key ) {
558 if ( is_array( $val ) ) {
559 $val = implode( ', ', $val );
560 }
561
562 $rows[] = array(
563 'name' => ucfirst( str_replace( '_', ' ', $key ) ),
564 'value' => $val,
565 'comment' => '',
566 );
567 }
568 }
569
570 $rows[] = array(
571 'section' => 'Logs',
572 );
573
574 $error_log_entries = $this->logger->get_last_logged_entries();
575
576 if ( ! empty( $error_log_entries ) ) {
577
578 foreach ( $error_log_entries as $error ) {
579 if (!empty($install_time)
580 && is_numeric($install_time)
581 && !empty($error['name'])
582 && !empty($error['value'])
583 && is_numeric($error['value'])
584 && $error['name'] === 'cron_sync'
585 && $error['value'] < ($install_time + 300)) {
586 // the first sync might right after the installation
587 continue;
588 }
589
590 $error['value'] = $this->convert_time_to_date( $error['value'], true, false );
591 $error['is_warning'] = !empty($error['name']) && stripos($error['name'], 'archiv') !== false && $error['name'] !== 'archive_boot';
592 $error['comment'] = matomo_anonymize_value($error['comment']);
593 $rows[] = $error;
594 }
595
596 foreach ( $error_log_entries as $error ) {
597 if ($suports_async
598 && !empty($error['value']) && is_string($error['value'])
599 && strpos($error['value'], __( 'Your PHP installation appears to be missing the MySQL extension which is required by WordPress.' )) > 0) {
600
601 $rows[] = array(
602 'name' => 'Cli has no MySQL',
603 'value' => true,
604 'comment' => 'It looks like MySQL is not available on CLI. Please read our FAQ on how to fix this issue: https://matomo.org/faq/wordpress/how-do-i-fix-the-error-your-php-installation-appears-to-be-missing-the-mysql-extension-which-is-required-by-wordpress-in-matomo-system-report/ ',
605 'is_error' => true
606 );
607 }
608 }
609 } else {
610 $rows[] = array(
611 'name' => __('None', 'matomo'),
612 'value' => '',
613 'comment' => '',
614 );
615 }
616
617
618 if ( ! \WpMatomo::is_safe_mode() ) {
619 Bootstrap::do_bootstrap();
620 $trackfailures = [];
621 try {
622 $tracking_failures = new Failures();
623 $trackfailures = $tracking_failures->getAllFailures();
624 } catch (\Exception $e) {
625 // ignored in case not set up yet etc.
626 }
627 if (!empty($trackfailures)) {
628 $rows[] = array(
629 'section' => 'Tracking failures',
630 );
631 foreach ($trackfailures as $failure) {
632 $comment = sprintf('Solution: %s<br>More info: %s<br>Date: %s<br>Request URL: %s',
633 $failure['solution'], $failure['solution_url'],
634 $failure['pretty_date_first_occurred'], $failure['request_url']);
635 $rows[] = array(
636 'name' => $failure['problem'],
637 'is_warning' => true,
638 'value' => '',
639 'comment' => $comment,
640 );
641 }
642
643 }
644 }
645
646
647 return $rows;
648 }
649
650 private function had_visits_in_last_days($numDays)
651 {
652 global $wpdb;
653
654 if (\WpMatomo::is_safe_mode()) {
655 return null;
656 }
657
658 $days_in_seconds = $numDays * 86400;
659 $db = new \WpMatomo\Db\Settings();
660 $prefix_table = $db->prefix_table_name('log_visit');
661
662 $suppress_errors = $wpdb->suppress_errors;
663 $wpdb->suppress_errors( true );// prevent any of this showing in logs just in case
664
665 try {
666 $time = gmdate( 'Y-m-d H:i:s', time() - $days_in_seconds );
667 $sql = $wpdb->prepare('SELECT idsite from ' . $prefix_table . ' where visit_last_action_time > %s LIMIT 1', $time );
668 $row = $wpdb->get_var( $sql );
669 } catch ( \Exception $e ) {
670 $row = null;
671 }
672
673 $wpdb->suppress_errors( $suppress_errors );
674 // we need to differentiate between
675 // 0 === had no visit
676 // 1 === had visit
677 // null === sum error... eg table was not correctly installed
678 if ($row !== null) {
679 $row = !empty($row);
680 }
681
682 return $row;
683 }
684
685 private function convert_time_to_date( $time, $in_blog_timezone, $print_diff = false ) {
686 if ( empty( $time ) ) {
687 return esc_html__( 'Unknown', 'matomo' );
688 }
689
690 $date = gmdate( 'Y-m-d H:i:s', (int)$time );
691
692 if ( $in_blog_timezone ) {
693 $date = get_date_from_gmt( $date, 'Y-m-d H:i:s' );
694 }
695
696 if ( $print_diff && class_exists( '\Piwik\MetricsFormatter' ) ) {
697 $date .= ' (' . MetricsFormatter::getPrettyTimeFromSeconds( $time - time(), true, false, true ) . ')';
698 }
699
700 return $date;
701 }
702
703 private function add_diagnostic_results( $rows, $results ) {
704 foreach ( $results as $result ) {
705 $comment = '';
706 /** @var DiagnosticResult $result */
707 if ( $result->getStatus() !== DiagnosticResult::STATUS_OK ) {
708 foreach ( $result->getItems() as $item ) {
709 $item_comment = $item->getComment();
710 if ( ! empty( $item_comment ) && is_string( $item_comment ) ) {
711 if ( stripos( $item_comment, 'core:archive' ) > 0 ) {
712 // we only want to keep the first sentence like " Archiving last ran successfully on Wednesday, January 2, 2019 00:00:00 which is 335 days 20:08:11 ago"
713 // but not anything that asks user to set up a cronjob
714 $item_comment = substr( $item_comment, 0, stripos( $item_comment, 'core:archive' ) );
715 if ( strpos( $item_comment, '.' ) > 0 ) {
716 $item_comment = substr( $item_comment, 0, strripos( $item_comment, '.' ) );
717 } else {
718 $item_comment = 'Archiving hasn\'t run in a while.';
719 }
720 }
721 $comment .= $item_comment . '<br/>';
722 }
723 }
724 }
725
726 $rows[] = array(
727 'name' => $result->getLabel(),
728 'value' => $result->getStatus() . ' ' . $result->getLongErrorMessage(),
729 'comment' => $comment,
730 'is_warning' => $result->getStatus() === DiagnosticResult::STATUS_WARNING,
731 'is_error' => $result->getStatus() === DiagnosticResult::STATUS_ERROR,
732 );
733 }
734
735 return $rows;
736 }
737
738 private function get_wordpress_info() {
739 $is_multi_site = is_multisite();
740 $num_blogs = 1;
741 $is_network_enabled = false;
742 if ( $is_multi_site ) {
743 if ( function_exists( 'get_blog_count' ) ) {
744 $num_blogs = get_blog_count();
745 }
746 $settings = new Settings();
747 $is_network_enabled = $settings->is_network_enabled();
748 }
749
750 $rows = array();
751 $rows[] = array(
752 'name' => 'Home URL',
753 'value' => home_url(),
754 );
755 $rows[] = array(
756 'name' => 'Site URL',
757 'value' => site_url(),
758 );
759 $rows[] = array(
760 'name' => 'WordPress Version',
761 'value' => get_bloginfo( 'version' ),
762 );
763 $rows[] = array(
764 'name' => 'Number of blogs',
765 'value' => $num_blogs,
766 );
767 $rows[] = array(
768 'name' => 'Multisite Enabled',
769 'value' => $is_multi_site,
770 );
771 $rows[] = array(
772 'name' => 'Network Enabled',
773 'value' => $is_network_enabled,
774 );
775 $consts = array('WP_DEBUG', 'WP_DEBUG_DISPLAY', 'WP_DEBUG_LOG', 'DISABLE_WP_CRON', 'FORCE_SSL_ADMIN', 'WP_CACHE',
776 'CONCATENATE_SCRIPTS', 'COMPRESS_SCRIPTS', 'COMPRESS_CSS', 'ENFORCE_GZIP', 'WP_LOCAL_DEV',
777 'DIEONDBERROR', 'WPLANG', 'ALTERNATE_WP_CRON', 'WP_CRON_LOCK_TIMEOUT', 'WP_DISABLE_FATAL_ERROR_HANDLER');
778 foreach ($consts as $const) {
779 $rows[] = array(
780 'name' => $const,
781 'value' => defined( $const ) ? constant( $const) : '-',
782 );
783 }
784
785 $rows[] = array(
786 'name' => 'Permalink Structure',
787 'value' => get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default',
788 );
789
790 $rows[] = array(
791 'name' => 'Possibly uses symlink',
792 'value' => strpos( __DIR__, ABSPATH ) === false && strpos( __DIR__, WP_CONTENT_DIR ) === false,
793 );
794
795 if (is_plugin_active('wp-piwik/wp-piwik.php')) {
796 $rows[] = array(
797 'name' => 'WP-Matomo (WP-Piwik) activated',
798 'value' => true,
799 'is_warning' => true,
800 'comment' => 'It is usually not recommended or needed to run Matomo for WordPress and WP-Matomo at the same time. To learn more about the differences between the two plugins view this URL: https://matomo.org/faq/wordpress/why-are-there-two-different-matomo-for-wordpress-plugins-what-is-the-difference-to-wp-matomo-integration-plugin/'
801 );
802
803 $mode = get_option ( 'wp-piwik_global-piwik_mode' );
804 if (function_exists('get_site_option') && is_plugin_active_for_network ( 'wp-piwik/wp-piwik.php' )) {
805 $mode = get_site_option ( 'wp-piwik_global-piwik_mode');
806 }
807 if (!empty($mode)) {
808 $rows[] = array(
809 'name' => 'WP-Matomo mode',
810 'value' => $mode,
811 'is_warning' => $mode === 'php' || $mode === 'PHP',
812 'comment' => 'WP-Matomo is configured in "PHP mode". This is known to cause issues with Matomo for WordPress. We recommend you either deactivate WP-Matomo or you go "Settings => WP-Matomo" and change the "Matomo Mode" in the "Connect to Matomo" section to "Self-hosted HTTP API".'
813 );
814 }
815 }
816
817 $compatible_content_dir = matomo_has_compatible_content_dir();
818 if ($compatible_content_dir === true) {
819 $rows[] = array(
820 'name' => 'Compatible content directory',
821 'value' => true,
822 );
823 } else {
824 $rows[] = array(
825 'name' => 'Compatible content directory',
826 'value' => $compatible_content_dir,
827 'is_warning' => true,
828 'comment' => __( 'It looks like you are maybe using a custom WordPress content directory. The Matomo reporting/admin pages might not work. You may be able to workaround this.', 'matomo' ) . ' ' . __( 'Learn more', 'matomo' ) . ': https://matomo.org/faq/wordpress/how-do-i-make-matomo-for-wordpress-work-when-i-have-a-custom-content-directory/'
829 );
830 }
831
832 return $rows;
833 }
834
835 private function get_server_info() {
836 $rows = array();
837
838 if ( ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) {
839 $rows[] = array(
840 'name' => 'Server Info',
841 'value' => $_SERVER['SERVER_SOFTWARE'],
842 );
843 }
844 if ( PHP_OS ) {
845 $rows[] = array(
846 'name' => 'PHP OS',
847 'value' => PHP_OS,
848 );
849 }
850 $rows[] = array(
851 'name' => 'PHP Version',
852 'value' => phpversion(),
853 );
854 $rows[] = array(
855 'name' => 'PHP SAPI',
856 'value' => php_sapi_name(),
857 );
858 if (defined('PHP_BINARY') && PHP_BINARY) {
859 $rows[] = array(
860 'name' => 'PHP Binary Name',
861 'value' => @basename(PHP_BINARY),
862 );
863 }
864 if (!\WpMatomo::is_safe_mode()) {
865 Bootstrap::do_bootstrap();
866 $cliPhp = new CliMulti\CliPhp();
867 $binary = $cliPhp->findPhpBinary();
868 if (!empty($binary)) {
869 $binary = basename($binary);
870 $rows[] = array(
871 'name' => 'PHP Found Binary',
872 'value' => $binary,
873 );
874 }
875 }
876 $rows[] = array(
877 'name' => 'Timezone',
878 'value' => date_default_timezone_get(),
879 );
880 if (function_exists('wp_timezone_string')) {
881 $rows[] = array(
882 'name' => 'WP timezone',
883 'value' => wp_timezone_string(),
884 );
885 }
886 $rows[] = array(
887 'name' => 'Locale',
888 'value' => get_locale(),
889 );
890 if (function_exists('get_user_locale')) {
891 $rows[] = array(
892 'name' => 'User Locale',
893 'value' => get_user_locale(),
894 );
895 }
896
897 $rows[] = array(
898 'name' => 'Memory Limit',
899 'value' => @ini_get( 'memory_limit' ),
900 'comment' => 'At least 128MB recommended. Depending on your traffic 256MB or more may be needed.',
901 );
902
903 $rows[] = array(
904 'name' => 'WP Memory Limit',
905 'value' => defined( 'WP_MEMORY_LIMIT' ) ? WP_MEMORY_LIMIT : '',
906 'comment' => '',
907 );
908
909 $rows[] = array(
910 'name' => 'WP Max Memory Limit',
911 'value' => defined( 'WP_MAX_MEMORY_LIMIT' ) ? WP_MAX_MEMORY_LIMIT : '',
912 'comment' => '',
913 );
914
915 $rows[] = array(
916 'name' => 'Time',
917 'value' => time(),
918 );
919
920 $rows[] = array(
921 'name' => 'Max Execution Time',
922 'value' => ini_get( 'max_execution_time' ),
923 );
924 $rows[] = array(
925 'name' => 'Max Post Size',
926 'value' => ini_get( 'post_max_size' ),
927 );
928 $rows[] = array(
929 'name' => 'Max Upload Size',
930 'value' => wp_max_upload_size(),
931 );
932 $rows[] = array(
933 'name' => 'Max Input Vars',
934 'value' => ini_get( 'max_input_vars' ),
935 );
936
937 $disabled_functions = ini_get('disable_functions');
938 $rows[] = array(
939 'name' => 'Disabled PHP functions',
940 'value' => !empty($disabled_functions),
941 'comment' => !empty($disabled_functions) ? $disabled_functions : ''
942 );
943
944 $zlib_compression = ini_get( 'zlib.output_compression' );
945 $row = array(
946 'name' => 'zlib.output_compression is off',
947 'value' => $zlib_compression !== '1',
948 );
949
950 if ( $zlib_compression === '1' ) {
951 $row['is_error'] = true;
952 $row['comment'] = 'You need to set "zlib.output_compression" in your php.ini to "Off".';
953 }
954 $rows[] = $row;
955
956 if ( function_exists( 'curl_version' ) ) {
957 $curl_version = curl_version();
958 $curl_version = $curl_version['version'] . ', ' . $curl_version['ssl_version'];
959 $rows[] = array(
960 'name' => 'Curl Version',
961 'value' => $curl_version,
962 );
963 }
964
965 $suhosin_installed = ( extension_loaded( 'suhosin' ) || ( defined( 'SUHOSIN_PATCH' ) && constant( 'SUHOSIN_PATCH' ) ) );
966 $rows[] = array(
967 'name' => 'Suhosin installed',
968 'value' => !empty($suhosin_installed),
969 'comment' => ''
970 );
971
972 return $rows;
973 }
974
975 private function get_browser_info() {
976 $rows = array();
977
978 if (!empty($_SERVER['HTTP_USER_AGENT'])) {
979 $rows[] = array(
980 'name' => 'Browser',
981 'value' => '',
982 'comment' => $_SERVER['HTTP_USER_AGENT']
983 );
984 }
985 if (!\WpMatomo::is_safe_mode()) {
986 Bootstrap::do_bootstrap();
987 try {
988 if (!empty($_SERVER['HTTP_USER_AGENT'])) {
989 $detector = StaticContainer::get(DeviceDetectorFactory::class)->makeInstance($_SERVER['HTTP_USER_AGENT']);
990 $client = $detector->getClient();
991 if (!empty($client['name']) && $client['name'] === 'Microsoft Edge' && (int) $client['version'] >= 85) {
992 $rows[] = array(
993 'name' => 'Browser Compatibility',
994 'is_warning' => true,
995 'value' => 'Yes',
996 'comment' => 'Because you are using MS Edge browser, you may see a warning like "This site has been reported as unsafe" from "Microsoft Defender SmartScreen" when you view the Matomo Reporting, Admin or Tag Manager page. This is a false alert and you can safely ignore this warning by clicking on the icon next to the URL (in the address bar) and choosing either "Report as safe" (preferred) or "Show unsafe content". We are hoping to get this false warning removed in the future.'
997 );
998 }
999 }
1000
1001 } catch (\Exception $e) {
1002
1003 }
1004
1005 $rows[] = array(
1006 'name' => 'Language',
1007 'value' => Common::getBrowserLanguage(),
1008 'comment' => ''
1009 );
1010 }
1011
1012
1013 return $rows;
1014 }
1015
1016 private function get_db_info() {
1017 global $wpdb;
1018 $rows = array();
1019
1020 $rows[] = array(
1021 'name' => 'MySQL Version',
1022 'value' => ! empty( $wpdb->is_mysql ) ? $wpdb->db_version() : '',
1023 'comment' => '',
1024 );
1025
1026 $rows[] = array(
1027 'name' => 'Mysqli Connect',
1028 'value' => function_exists( 'mysqli_connect' ),
1029 'comment' => '',
1030 );
1031 $rows[] = array(
1032 'name' => 'Force MySQL over Mysqli',
1033 'value' => defined( 'WP_USE_EXT_MYSQL' ) && WP_USE_EXT_MYSQL,
1034 'comment' => '',
1035 );
1036
1037 $rows[] = array(
1038 'name' => 'DB Prefix',
1039 'value' => $wpdb->prefix,
1040 );
1041
1042 $rows[] = array(
1043 'name' => 'DB CHARSET',
1044 'value' => defined('DB_CHARSET') ? DB_CHARSET : '',
1045 );
1046
1047 $rows[] = array(
1048 'name' => 'DB COLLATE',
1049 'value' => defined('DB_COLLATE') ? DB_COLLATE : '',
1050 );
1051
1052 $rows[] = array(
1053 'name' => 'SHOW ERRORS',
1054 'value' => !empty($wpdb->show_errors),
1055 );
1056
1057 $rows[] = array(
1058 'name' => 'SUPPRESS ERRORS',
1059 'value' => !empty($wpdb->suppress_errors),
1060 );
1061
1062 if ( method_exists( $wpdb, 'parse_db_host' ) ) {
1063 $host_data = $wpdb->parse_db_host( DB_HOST );
1064 if ( $host_data ) {
1065 list( $host, $port, $socket, $is_ipv6 ) = $host_data;
1066 }
1067
1068 $rows[] = array(
1069 'name' => 'Uses Socket',
1070 'value' => ! empty( $socket ),
1071 );
1072 $rows[] = array(
1073 'name' => 'Uses IPv6',
1074 'value' => ! empty( $is_ipv6 ),
1075 );
1076 }
1077
1078 $rows[] = array(
1079 'name' => 'Matomo tables found',
1080 'value' => $this->get_num_matomo_tables(),
1081 );
1082
1083 foreach (['user', 'site'] as $table) {
1084 $rows[] = array(
1085 'name' => 'Matomo '.$table.'s found',
1086 'value' => $this->get_num_entries_in_table($table),
1087 );
1088 }
1089
1090 $grants = $this->get_db_grants();
1091
1092 // we only show these grants for security reasons as only they are needed and we don't need to know any other ones
1093 $needed_grants = array( 'SELECT', 'INSERT', 'UPDATE', 'INDEX', 'DELETE', 'CREATE', 'DROP', 'ALTER', 'CREATE TEMPORARY TABLES', 'LOCK TABLES' );
1094 if ( in_array( 'ALL PRIVILEGES', $grants, true ) ) {
1095 // ALL PRIVILEGES may be used pre MySQL 8.0
1096 $grants = $needed_grants;
1097 }
1098
1099 $grants_missing = array_diff( $needed_grants, $grants );
1100
1101 if ( empty( $grants )
1102 || ! is_array( $grants )
1103 || count( $grants_missing ) === count( $needed_grants ) ) {
1104 $rows[] = array(
1105 'name' => esc_html__( 'Required permissions', 'matomo' ),
1106 'value' => esc_html__( 'Failed to detect granted permissions', 'matomo' ),
1107 'comment' => esc_html__( 'Please check your MySQL user has these permissions (grants):', 'matomo' ) . '<br />' . implode( ', ', $needed_grants ),
1108 'is_warning' => false,
1109 );
1110 } else {
1111 if ( ! empty( $grants_missing ) ) {
1112 $rows[] = array(
1113 'name' => esc_html__( 'Required permissions', 'matomo' ),
1114 'value' => esc_html__( 'Error', 'matomo' ),
1115 'comment' => esc_html__( 'Missing permissions', 'matomo' ) . ': ' . implode( ', ', $grants_missing ) . '. ' . __( 'Please check if any of these MySQL permission (grants) are missing and add them if needed.', 'matomo' ) . ' ' . __( 'Learn more', 'matomo' ) . ': https://matomo.org/faq/troubleshooting/how-do-i-check-if-my-mysql-user-has-all-required-grants/',
1116 'is_warning' => true,
1117 );
1118 } else {
1119 $rows[] = array(
1120 'name' => esc_html__( 'Required permissions', 'matomo' ),
1121 'value' => esc_html__( 'OK', 'matomo' ),
1122 'comment' => '',
1123 'is_warning' => false,
1124 );
1125 }
1126 }
1127
1128 return $rows;
1129 }
1130
1131 private function get_num_entries_in_table($table) {
1132 global $wpdb;
1133
1134 $db_settings = new \WpMatomo\Db\Settings();
1135 $prefix = $db_settings->prefix_table_name($table);
1136
1137 $results = null;
1138 try {
1139 $results = $wpdb->get_var('select count(*) from '.$prefix);
1140 } catch (\Exception $e) {
1141 }
1142
1143 if (isset($results) && is_numeric($results)) {
1144 return $results;
1145 }
1146
1147 return 'table not exists';
1148 }
1149
1150 private function get_num_matomo_tables() {
1151 global $wpdb;
1152
1153 $db_settings = new \WpMatomo\Db\Settings();
1154 $prefix = $db_settings->prefix_table_name('');
1155
1156 $results = null;
1157 try {
1158 $results = $wpdb->get_results('show tables like "'.$prefix.'%"');
1159 } catch (\Exception $e) {
1160 $this->logger->log('no show tables: ' . $e->getMessage());
1161 }
1162
1163 if (is_array($results)) {
1164 return count($results);
1165 }
1166
1167 return 'show tables not working';
1168 }
1169
1170 private function get_db_grants() {
1171 global $wpdb;
1172
1173 $suppress_errors = $wpdb->suppress_errors;
1174 $wpdb->suppress_errors( true );// prevent any of this showing in logs just in case
1175
1176 try {
1177 $values = $wpdb->get_results( 'SHOW GRANTS', ARRAY_N );
1178 } catch ( \Exception $e ) {
1179 // We ignore any possible error in case of permission or not supported etc.
1180 $values = array();
1181 }
1182
1183 $wpdb->suppress_errors( $suppress_errors );
1184
1185 $grants = array();
1186 foreach ( $values as $index => $value ) {
1187 if ( empty( $value[0] ) || ! is_string( $value[0] ) ) {
1188 continue;
1189 }
1190
1191 if ( stripos( $value[0], 'ALL PRIVILEGES' ) !== false ) {
1192 return array( 'ALL PRIVILEGES' ); // the split on empty string wouldn't work otherwise
1193 }
1194
1195 foreach ( array( ' ON ', ' TO ', ' IDENTIFIED ', ' BY ' ) as $keyword ) {
1196 if ( stripos( $values[ $index ][0], $keyword ) !== false ) {
1197 // make sure to never show by any accident a db user or password by cutting anything after on/to
1198 $values[ $index ][0] = substr( $value[0], 0, stripos( $value[0], $keyword ) );
1199 }
1200 if ( stripos( $values[ $index ][0], 'GRANT' ) !== false ) {
1201 // otherwise we end up having "grant select"... instead of just "select"
1202 $values[ $index ][0] = substr( $value[0], stripos( $values[ $index ][0], 'GRANT' ) + 5 );
1203 }
1204 }
1205 // make sure to never show by any accident a db user or password
1206 $values[ $index ][0] = str_replace( array( DB_USER, DB_PASSWORD ), array( 'DB_USER', 'DB_PASS' ), $values[ $index ][0] );
1207
1208 $grants = array_merge( $grants, explode( ',', $values[ $index ][0] ) );
1209 }
1210 $grants = array_map( 'trim', $grants );
1211 $grants = array_map( 'strtoupper', $grants );
1212 $grants = array_unique( $grants );
1213 return $grants;
1214 }
1215
1216 private function get_plugins_info() {
1217 $rows = array();
1218 $mu_plugins = get_mu_plugins();
1219
1220 if ( ! empty( $mu_plugins ) ) {
1221 $rows[] = array(
1222 'section' => 'MU Plugins',
1223 );
1224
1225 foreach ( $mu_plugins as $mu_pin ) {
1226 $comment = '';
1227 if ( ! empty( $plugin['Network'] ) ) {
1228 $comment = 'Network enabled';
1229 }
1230 $rows[] = array(
1231 'name' => $mu_pin['Name'],
1232 'value' => $mu_pin['Version'],
1233 'comment' => $comment,
1234 );
1235 }
1236
1237 $rows[] = array(
1238 'section' => 'Plugins',
1239 );
1240 }
1241
1242 $plugins = get_plugins();
1243
1244 foreach ( $plugins as $plugin ) {
1245 $comment = '';
1246 if ( ! empty( $plugin['Network'] ) ) {
1247 $comment = 'Network enabled';
1248 }
1249 $rows[] = array(
1250 'name' => $plugin['Name'],
1251 'value' => $plugin['Version'],
1252 'comment' => $comment,
1253 );
1254 }
1255
1256 $active_plugins = get_option( 'active_plugins', array() );
1257
1258 if ( ! empty( $active_plugins ) && is_array( $active_plugins ) ) {
1259 $active_plugins = array_map(
1260 function ( $active_plugin ) {
1261 $parts = explode( '/', trim( $active_plugin ) );
1262 return trim( $parts[0] );
1263 },
1264 $active_plugins
1265 );
1266
1267 $rows[] = array(
1268 'name' => 'Active Plugins',
1269 'value' => count( $active_plugins ),
1270 'comment' => implode( ' ', $active_plugins ),
1271 );
1272
1273 $used_not_compatible = array_intersect( $active_plugins, $this->not_compatible_plugins );
1274 if ( ! empty( $used_not_compatible ) ) {
1275 $rows[] = array(
1276 'name' => __( 'Not compatible plugins', 'matomo' ),
1277 'value' => count( $used_not_compatible ),
1278 'comment' => implode( ', ', $used_not_compatible ) . '<br><br> Matomo may work fine when using these plugins but there may be some issues. For more information see<br>https://matomo.org/faq/wordpress/which-plugins-is-matomo-for-wordpress-known-to-be-not-compatible-with/',
1279 'is_warning' => true,
1280 );
1281 }
1282 }
1283
1284 return $rows;
1285 }
1286
1287
1288 }
1289