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