PluginProbe ʕ •ᴥ•ʔ
Matomo Analytics – Powerful, Privacy-First Insights for WordPress / 4.14.2
Matomo Analytics – Powerful, Privacy-First Insights for WordPress v4.14.2
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 4 years ago views 3 years ago AccessSettings.php 4 years ago Admin.php 4 years ago AdminSettings.php 4 years ago AdminSettingsInterface.php 6 years ago AdvancedSettings.php 4 years ago Chart.php 4 years ago CookieConsent.php 4 years ago Dashboard.php 4 years ago ExclusionSettings.php 4 years ago GeolocationSettings.php 4 years ago GetStarted.php 4 years ago ImportWpStatistics.php 4 years ago Info.php 4 years ago InvalidIpException.php 4 years ago Marketplace.php 4 years ago Menu.php 3 years ago PrivacySettings.php 4 years ago SafeModeMenu.php 4 years ago Summary.php 4 years ago SystemReport.php 3 years ago TrackingSettings.php 4 years ago
SystemReport.php
1835 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 Exception;
13 use ITSEC_Modules;
14 use Piwik\CliMulti;
15 use Piwik\CliMulti\CliPhp;
16 use Piwik\Common;
17 use Piwik\Config;
18 use Piwik\Container\StaticContainer;
19 use Piwik\DeviceDetector\DeviceDetectorFactory;
20 use Piwik\Filesystem;
21 use Piwik\Plugin;
22 use Piwik\Plugins\CoreAdminHome\API;
23 use Piwik\Plugins\Diagnostics\Diagnostic\DiagnosticResult;
24 use Piwik\Plugins\Diagnostics\DiagnosticService;
25 use Piwik\Plugins\SitesManager\Model;
26 use Piwik\Plugins\UserCountry\LocationProvider;
27 use Piwik\SettingsPiwik;
28 use Piwik\Tracker\Failures;
29 use Piwik\Version;
30 use WpMatomo;
31 use WpMatomo\Bootstrap;
32 use WpMatomo\Capabilities;
33 use WpMatomo\Installer;
34 use WpMatomo\Logger;
35 use WpMatomo\Paths;
36 use WpMatomo\ScheduledTasks;
37 use WpMatomo\Settings;
38 use WpMatomo\Site;
39 use WpMatomo\Site\Sync as SiteSync;
40 use WpMatomo\Updater;
41 use WpMatomo\User\Sync as UserSync;
42
43 if ( ! defined( 'ABSPATH' ) ) {
44 exit; // if accessed directly
45 }
46
47 /**
48 * error_reporting is required for this page
49 * phpcs:disable WordPress.PHP.DiscouragedPHPFunctions.runtime_configuration_error_reporting
50 *
51 * We want a real data, not something coming from cache
52 * phpcs:disable WordPress.DB.DirectDatabaseQuery.NoCaching
53 *
54 * This is a report error, so silent the possible errors
55 * phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged
56 *
57 * We cannot use parameters of statements as this is the table names we build
58 * phpcs:disable WordPress.DB.DirectDatabaseQuery.DirectQuery
59 * phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
60 */
61 class SystemReport {
62 const NONCE_NAME = 'matomo_troubleshooting';
63 const TROUBLESHOOT_SYNC_USERS = 'matomo_troubleshooting_action_site_users';
64 const TROUBLESHOOT_SYNC_ALL_USERS = 'matomo_troubleshooting_action_all_users';
65 const TROUBLESHOOT_SYNC_SITE = 'matomo_troubleshooting_action_site';
66 const TROUBLESHOOT_SYNC_ALL_SITES = 'matomo_troubleshooting_action_all_sites';
67 const TROUBLESHOOT_CLEAR_MATOMO_CACHE = 'matomo_troubleshooting_action_clear_matomo_cache';
68 const TROUBLESHOOT_ARCHIVE_NOW = 'matomo_troubleshooting_action_archive_now';
69 const TROUBLESHOOT_UPDATE_GEOIP_DB = 'matomo_troubleshooting_action_update_geoipdb';
70 const TROUBLESHOOT_CLEAR_LOGS = 'matomo_troubleshooting_action_clear_logs';
71 const TROUBLESHOOT_RUN_UPDATER = 'matomo_troubleshooting_action_run_updater';
72
73 private $not_compatible_plugins = [
74 'background-manager',
75 // Uses an old version of Twig and plugin is no longer maintained.
76 'all-in-one-event-calendar',
77 // Uses an old version of Twig
78 'tweet-old-post-pro',
79 // uses a newer version of monolog
80 'wp-rss-aggregator',
81 // twig conflict
82 'age-verification-for-woocommerce',
83 // see https://github.com/matomo-org/wp-matomo/issues/428
84 'minify-html-markup',
85 // see https://wordpress.org/support/topic/graphs-are-not-displayed-in-the-visits-overview-widget/#post-14298068
86 'bigbuy-wc-dropshipping-connector',
87 // see https://wordpress.org/support/topic/20-total-errors-during-this-script-execution/
88 'google-listings-and-ads',
89 // see https://wordpress.org/support/topic/20-total-errors-during-this-script-execution/
90 'post-smtp',
91 // see https://wordpress.org/support/topic/activation-of-another-plugin-breaks-matomo/#post-15045079
92 'adshares',
93 // see https://github.com/matomo-org/matomo-for-wordpress/issues/618
94 'bluehost-wordpress-plugin',
95 // see https://wordpress.org/support/topic/archive-error-with-wp-rocket/
96 'wp-rocket',
97 // see https://github.com/matomo-org/matomo-for-wordpress/issues/697
98 'backwpup',
99 // see https://github.com/matomo-org/matomo-for-wordpress/issues/710
100 'fs-poster',
101 ];
102
103 private $valid_tabs = [ 'troubleshooting' ];
104
105 /**
106 * @var Settings
107 */
108 private $settings;
109
110 /**
111 * @var Logger
112 */
113 private $logger;
114
115 private $initial_error_reporting = null;
116
117 private $shell_exec_available;
118 /**
119 * @var \WpMatomo\Db\Settings
120 */
121 public $db_settings;
122 /**
123 * @var string the php binary used by Matomo
124 */
125 private $binary;
126
127 private static $matomo_tables;
128
129 public function __construct( Settings $settings ) {
130 $this->settings = $settings;
131 $this->logger = new Logger();
132 $this->db_settings = new \WpMatomo\Db\Settings();
133 $this->shell_exec_available = function_exists( 'shell_exec' );
134 if ( ! WpMatomo::is_safe_mode() ) {
135 Bootstrap::do_bootstrap();
136 $cli_php = new CliPhp();
137 $this->binary = $cli_php->findPhpBinary();
138 }
139 }
140
141 public function get_not_compatible_plugins() {
142 return $this->not_compatible_plugins;
143 }
144
145 private function execute_troubleshoot_if_needed() {
146 if ( ! empty( $_POST )
147 && is_admin()
148 && check_admin_referer( self::NONCE_NAME )
149 && current_user_can( Capabilities::KEY_SUPERUSER )
150 ) {
151 if ( ! empty( $_POST[ self::TROUBLESHOOT_ARCHIVE_NOW ] ) ) {
152 Bootstrap::do_bootstrap();
153 $scheduled_tasks = new ScheduledTasks( $this->settings );
154
155 if ( ! defined( 'PIWIK_ARCHIVE_NO_TRUNCATE' ) ) {
156 // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
157 define( 'PIWIK_ARCHIVE_NO_TRUNCATE', 1 ); // when triggering it manually, we prefer the full error message
158 }
159
160 try {
161 // force invalidation of archive to ensure it actually will rearchive the data
162 $site = new Site();
163 $idsite = $site->get_current_matomo_site_id();
164 if ( $idsite ) {
165 $timezone = \Piwik\Site::getTimezoneFor( $idsite );
166 $now_string = \Piwik\Date::factory( 'now', $timezone )->toString();
167 foreach ( [ 'day' ] as $period ) {
168 API::getInstance()->invalidateArchivedReports( $idsite, $now_string, $period, false, false );
169 }
170 }
171 } catch ( Exception $e ) {
172 $this->logger->log_exception( 'archive_invalidate', $e );
173 }
174
175 try {
176 $errors = $scheduled_tasks->archive( true, false );
177 } catch ( Exception $e ) {
178 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>';
179 throw $e;
180 }
181
182 if ( ! empty( $errors ) ) {
183 echo '<div class="notice notice-warning"><p>Matomo Archive Warnings: ';
184 foreach ( $errors as $error ) {
185 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_var_export
186 echo nl2br( esc_html( matomo_anonymize_value( var_export( $error, 1 ) ) ) );
187 echo '<br/>';
188 }
189 echo '</p></div>';
190 }
191 }
192
193 if ( ! empty( $_POST[ self::TROUBLESHOOT_CLEAR_MATOMO_CACHE ] ) ) {
194 $paths = new Paths();
195 $paths->clear_cache_dir();
196 // we first delete the cache dir manually just in case there's something
197 // going wrong with matomo and bootstrapping would not even be possible.
198 Bootstrap::do_bootstrap();
199 Filesystem::deleteAllCacheOnUpdate();
200 Updater::unlock();
201 }
202
203 if ( ! empty( $_POST[ self::TROUBLESHOOT_UPDATE_GEOIP_DB ] ) ) {
204 $scheduled_tasks = new ScheduledTasks( $this->settings );
205 $scheduled_tasks->update_geo_ip2_db();
206 }
207
208 if ( ! empty( $_POST[ self::TROUBLESHOOT_CLEAR_LOGS ] ) ) {
209 $this->logger->clear_logged_exceptions();
210 }
211
212 if ( ! $this->settings->is_network_enabled() || ! is_network_admin() ) {
213 if ( ! empty( $_POST[ self::TROUBLESHOOT_SYNC_USERS ] ) ) {
214 $sync = new UserSync();
215 $sync->sync_current_users();
216 }
217 if ( ! empty( $_POST[ self::TROUBLESHOOT_SYNC_SITE ] ) ) {
218 $sync = new SiteSync( $this->settings );
219 $sync->sync_current_site();
220 }
221 if ( ! empty( $_POST[ self::TROUBLESHOOT_RUN_UPDATER ] ) ) {
222 Updater::unlock();
223 $sync = new Updater( $this->settings );
224 $sync->update();
225 }
226 }
227 if ( $this->settings->is_network_enabled() ) {
228 if ( ! empty( $_POST[ self::TROUBLESHOOT_SYNC_ALL_SITES ] ) ) {
229 $sync = new SiteSync( $this->settings );
230 $sync->sync_all();
231 }
232 if ( ! empty( $_POST[ self::TROUBLESHOOT_SYNC_ALL_USERS ] ) ) {
233 $sync = new UserSync();
234 $sync->sync_all();
235 }
236 }
237 }
238 }
239
240 private function get_error_tables() {
241 $matomo_tables = self::$matomo_tables;
242
243 if ( ! $matomo_tables ) {
244 $matomo_tables = [
245 [
246 'title' => 'Matomo',
247 'rows' => $this->get_matomo_info(),
248 'has_comments' => true,
249 ],
250 [
251 'title' => 'WordPress',
252 'rows' => $this->get_wordpress_info(),
253 'has_comments' => true,
254 ],
255 [
256 'title' => 'WordPress Plugins',
257 'rows' => $this->get_plugins_info(),
258 'has_comments' => true,
259 ],
260 [
261 'title' => 'Server',
262 'rows' => $this->get_server_info(),
263 'has_comments' => true,
264 ],
265 [
266 'title' => 'PHP cli',
267 'rows' => $this->get_phpcli_info(),
268 'has_comments' => true,
269 ],
270 [
271 'title' => 'Database',
272 'rows' => $this->get_db_info(),
273 'has_comments' => true,
274 ],
275 [
276 'title' => 'Browser',
277 'rows' => $this->get_browser_info(),
278 'has_comments' => true,
279 ],
280 ];
281 self::$matomo_tables = $matomo_tables;
282 }
283
284 return $matomo_tables;
285 }
286
287 public function errors_present() {
288 $matomo_tables = $this->get_error_tables();
289
290 $matomo_tables = apply_filters( 'matomo_systemreport_tables', $matomo_tables );
291 $matomo_tables = $this->add_errors_first( $matomo_tables );
292
293 foreach ( $matomo_tables as $report_table ) {
294 foreach ( $report_table['rows'] as $row ) {
295 if ( ! empty( $row['is_error'] ) || ! empty( $row['is_warning'] ) ) {
296 return true;
297 }
298 }
299 }
300
301 return false;
302 }
303
304 public function show() {
305 $this->execute_troubleshoot_if_needed();
306
307 $settings = $this->settings;
308
309 $matomo_active_tab = '';
310
311 if ( isset( $_GET['tab'] ) ) {
312 $tab = sanitize_text_field( wp_unslash( $_GET['tab'] ) );
313 if ( in_array( $tab, $this->valid_tabs, true ) ) {
314 $matomo_active_tab = $tab;
315 }
316 }
317
318 $matomo_tables = [];
319 if ( empty( $matomo_active_tab ) ) {
320 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting
321 $this->initial_error_reporting = @error_reporting();
322 $matomo_tables = $this->get_error_tables();
323 }
324 $matomo_tables = apply_filters( 'matomo_systemreport_tables', $matomo_tables );
325 $matomo_tables = $this->add_errors_first( $matomo_tables );
326 $matomo_has_warning_and_no_errors = $this->has_only_warnings_no_error( $matomo_tables );
327
328 $matomo_has_exception_logs = $this->logger->get_last_logged_entries();
329
330 include dirname( __FILE__ ) . '/views/systemreport.php';
331 }
332
333 private function has_only_warnings_no_error( $report_tables ) {
334 $has_warning = false;
335 $has_error = false;
336 foreach ( $report_tables as $report_table ) {
337 foreach ( $report_table['rows'] as $row ) {
338 if ( ! empty( $row['is_error'] ) ) {
339 $has_error = true;
340 }
341 if ( ! empty( $row['is_warning'] ) ) {
342 $has_warning = true;
343 }
344 }
345 }
346
347 return $has_warning && ! $has_error;
348 }
349
350 private function add_errors_first( $report_tables ) {
351 $errors = [
352 'title' => 'Errors',
353 'rows' => [],
354 'has_comments' => true,
355 ];
356 foreach ( $report_tables as $report_table ) {
357 foreach ( $report_table['rows'] as $row ) {
358 if ( ! empty( $row['is_error'] ) ) {
359 $errors['rows'][] = $row;
360 }
361 }
362 }
363
364 if ( ! empty( $errors['rows'] ) ) {
365 array_unshift( $report_tables, $errors );
366 }
367
368 return $report_tables;
369 }
370
371 private function check_file_exists_and_writable( $rows, $path_to_check, $title, $required ) {
372 $file_exists = file_exists( $path_to_check );
373 $file_readable = is_readable( $path_to_check );
374 $file_writable = is_writable( $path_to_check );
375 $comment = '"' . $path_to_check . '" ';
376 if ( ! $file_exists ) {
377 $comment .= sprintf( esc_html__( '%s does not exist. ', 'matomo' ), $title );
378 }
379 if ( ! $file_readable ) {
380 $comment .= sprintf( esc_html__( '%s is not readable. ', 'matomo' ), $title );
381 }
382 if ( ! $file_writable ) {
383 $comment .= sprintf( esc_html__( '%s is not writable. ', 'matomo' ), $title );
384 }
385
386 $rows[] = [
387 'name' => sprintf( esc_html__( '%s exists and is writable.', 'matomo' ), $title ),
388 'value' => $file_exists && $file_readable && $file_writable ? esc_html__( 'Yes', 'matomo' ) : esc_html__( 'No', 'matomo' ),
389 'comment' => $comment,
390 'is_error' => $required && ( ! $file_exists || ! $file_readable ),
391 'is_warning' => ! $required && ( ! $file_exists || ! $file_readable ),
392 ];
393
394 return $rows;
395 }
396
397 private function get_phpcli_info() {
398 $rows = [];
399
400 if ( $this->shell_exec_available ) {
401 $phpcli_version = $this->get_phpcli_output( '-v | grep built | cut -d " " -f 2' );
402 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
403 global $piwik_minimumPHPVersion;
404 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase
405 if ( version_compare( $phpcli_version, $piwik_minimumPHPVersion ) <= 0 ) {
406 $is_error = true;
407 $comment = sprintf( esc_html__( 'Your PHP cli version is not compatible with the %s. Please upgrade your PHP cli version, otherwise, you might have some archiving errors', 'matomo' ), sprintf( '<a href="%s" target="_blank">%s</a>', 'https://matomo.org/faq/on-premise/matomo-requirements/', esc_html__( 'Matomo requirements', 'matomo' ) ) );
408 } else {
409 $is_error = false;
410 $comment = '';
411 }
412 $rows[] = [
413 'name' => esc_html__( 'PHP cli Version', 'matomo' ),
414 'value' => $phpcli_version,
415 'comment' => $comment,
416 'is_error' => $is_error,
417 ];
418
419 switch ( $this->get_phpcli_output( '-m | grep mysqli' ) ) {
420 case 'mysqli':
421 $is_error = false;
422 $value = __( 'ok', 'matomo' );
423 $comment = '';
424 break;
425 default:
426 $value = __( 'missing', 'matomo' );
427 $is_error = true;
428 $comment = esc_html__( 'Your PHP cli does not load the MySQLi extension. You might have archiving problems in Matomo but also others problems in your WordPress cron tasks. You should enable this extension', 'matomo' );
429 }
430
431 $rows[] = [
432 'name' => esc_html__( 'MySQLi support', 'matomo' ),
433 'value' => $value,
434 'comment' => $comment,
435 'is_error' => $is_error,
436 ];
437 }
438
439 return $rows;
440 }
441
442 private function get_phpcli_output( $phpcli_params ) {
443 $output = '';
444 if ( $this->shell_exec_available && $this->binary ) {
445 // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.system_calls_shell_exec
446 $output = trim( '' . @shell_exec( $this->binary . ' ' . $phpcli_params ) );
447 }
448
449 return $output;
450 }
451
452 private function get_matomo_info() {
453 $rows = [];
454
455 $plugin_data = get_plugin_data( MATOMO_ANALYTICS_FILE, $markup = false, $translate = false );
456 $install_time = get_option( Installer::OPTION_NAME_INSTALL_DATE );
457
458 $rows[] = [
459 'name' => esc_html__( 'Matomo Plugin Version', 'matomo' ),
460 'value' => $plugin_data['Version'],
461 'comment' => '',
462 ];
463
464 $paths = new Paths();
465 $path_config_file = $paths->get_config_ini_path();
466 $rows = $this->check_file_exists_and_writable( $rows, $path_config_file, 'Config', true );
467
468 $path_tracker_file = $paths->get_matomo_js_upload_path();
469 $rows = $this->check_file_exists_and_writable( $rows, $path_tracker_file, 'JS Tracker', false );
470
471 $rows[] = [
472 'name' => esc_html__( 'Plugin directories', 'matomo' ),
473 'value' => ! empty( $GLOBALS['MATOMO_PLUGIN_DIRS'] ) ? 'Yes' : 'No',
474 'comment' => ! empty( $GLOBALS['MATOMO_PLUGIN_DIRS'] ) ? wp_json_encode( $GLOBALS['MATOMO_PLUGIN_DIRS'] ) : '',
475 ];
476
477 $tmp_dir = $paths->get_tmp_dir();
478
479 $rows[] = [
480 'name' => esc_html__( 'Tmp directory writable', 'matomo' ),
481 'value' => is_writable( $tmp_dir ),
482 'comment' => $tmp_dir,
483 ];
484
485 if ( ! empty( $_SERVER['MATOMO_WP_ROOT_PATH'] ) ) {
486 // we can have / in this value
487 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
488 $custom_path = rtrim( $_SERVER['MATOMO_WP_ROOT_PATH'], '/' ) . '/wp-load.php';
489 $path_exists = file_exists( $custom_path );
490 $comment = '';
491 if ( ! $path_exists ) {
492 $comment = 'It seems the path does not point to the WP root directory.';
493 }
494
495 $rows[] = [
496 'name' => 'Custom MATOMO_WP_ROOT_PATH',
497 'value' => $path_exists,
498 'is_error' => ! $path_exists,
499 'comment' => $comment,
500 ];
501 }
502
503 $report = null;
504
505 if ( ! WpMatomo::is_safe_mode() ) {
506 try {
507 Bootstrap::do_bootstrap();
508 /** @var DiagnosticService $service */
509 $service = StaticContainer::get( DiagnosticService::class );
510 $report = $service->runDiagnostics();
511
512 $rows[] = [
513 'name' => esc_html__( 'Matomo Version', 'matomo' ),
514 'value' => \Piwik\Version::VERSION,
515 'comment' => '',
516 ];
517 } catch ( Exception $e ) {
518 $rows[] = [
519 'name' => esc_html__( 'Matomo System Check', 'matomo' ),
520 'value' => 'Failed to run Matomo system check.',
521 'comment' => $e->getMessage(),
522 ];
523 }
524 }
525
526 $site = new Site();
527 $idsite = $site->get_current_matomo_site_id();
528
529 $rows[] = [
530 'name' => esc_html__( 'Matomo Blog idSite', 'matomo' ),
531 'value' => $idsite,
532 'comment' => '',
533 ];
534
535 $install_date = '';
536 if ( ! empty( $install_time ) ) {
537 $install_date = 'Install date: ' . $this->convert_time_to_date( $install_time, true, false );
538 }
539
540 $rows[] = [
541 'name' => esc_html__( 'Matomo Install Version', 'matomo' ),
542 'value' => get_option( Installer::OPTION_NAME_INSTALL_VERSION ),
543 'comment' => $install_date,
544 ];
545
546 $wpmatomo_updater = new \WpMatomo\Updater( $this->settings );
547 if ( ! WpMatomo::is_safe_mode() ) {
548 $outstanding_updates = $wpmatomo_updater->get_plugins_requiring_update();
549 $upgrade_in_progress = $wpmatomo_updater->is_upgrade_in_progress();
550 $rows[] = [
551 'name' => 'Upgrades outstanding',
552 'value' => ! empty( $outstanding_updates ),
553 'comment' => ! empty( $outstanding_updates ) ? wp_json_encode( $outstanding_updates ) : '',
554 ];
555 $rows[] = [
556 'name' => 'Upgrade in progress',
557 'value' => $upgrade_in_progress,
558 'comment' => '',
559 ];
560 }
561
562 if ( ! $wpmatomo_updater->load_plugin_functions() ) {
563 // this should actually never happen...
564 $rows[] = [
565 'name' => 'Matomo Upgrade Plugin Functions',
566 'is_warning' => true,
567 'value' => false,
568 'comment' => 'Function "get_plugin_data" not available. There may be an issue with upgrades not being executed. Please reach out to us.',
569 ];
570 }
571
572 $rows[] = [
573 'section' => 'Endpoints',
574 ];
575
576 $rows[] = [
577 'name' => 'Matomo JavaScript Tracker URL',
578 'value' => '',
579 'comment' => $paths->get_js_tracker_url_in_matomo_dir(),
580 ];
581
582 $rows[] = [
583 'name' => 'Matomo JavaScript Tracker - WP Rest API',
584 'value' => '',
585 'comment' => $paths->get_js_tracker_rest_api_endpoint(),
586 ];
587
588 $rows[] = [
589 'name' => 'Matomo HTTP Tracking API',
590 'value' => '',
591 'comment' => $paths->get_tracker_api_url_in_matomo_dir(),
592 ];
593
594 $rows[] = [
595 'name' => 'Matomo HTTP Tracking API - WP Rest API',
596 'value' => '',
597 'comment' => $paths->get_tracker_api_rest_api_endpoint(),
598 ];
599
600 $matomo_plugin_dir_name = basename( dirname( MATOMO_ANALYTICS_FILE ) );
601 if ( 'matomo' !== $matomo_plugin_dir_name ) {
602 $rows[] = [
603 'name' => 'Matomo Plugin Name is correct',
604 'value' => false,
605 'is_error' => true,
606 '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.',
607 ];
608 } elseif ( ! is_plugin_active( 'matomo/matomo.php' ) ) {
609 $rows[] = [
610 'name' => 'Matomo Plugin not active',
611 'value' => false,
612 'is_error' => true,
613 '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.',
614 ];
615 }
616
617 $rows[] = [
618 'section' => 'Crons',
619 ];
620
621 $scheduled_tasks = new ScheduledTasks( $this->settings );
622 $all_events = $scheduled_tasks->get_all_events();
623
624 $rows[] = [
625 'name' => esc_html__( 'Server time', 'matomo' ),
626 'value' => $this->convert_time_to_date( time(), false ),
627 'comment' => '',
628 ];
629
630 $rows[] = [
631 'name' => esc_html__( 'Blog time', 'matomo' ),
632 'value' => $this->convert_time_to_date( time(), true ),
633 'comment' => esc_html__( 'Below dates are shown in blog timezone', 'matomo' ),
634 ];
635
636 foreach ( $all_events as $event_name => $event_config ) {
637 $last_run_before = $scheduled_tasks->get_last_time_before_cron( $event_name );
638 $last_run_after = $scheduled_tasks->get_last_time_after_cron( $event_name );
639
640 $next_scheduled = wp_next_scheduled( $event_name );
641
642 $comment = ' Last started: ' . $this->convert_time_to_date( $last_run_before, true, true ) . '.';
643 $comment .= ' Last ended: ' . $this->convert_time_to_date( $last_run_after, true, true ) . '.';
644 $comment .= ' Interval: ' . $event_config['interval'];
645
646 $rows[] = [
647 'name' => $event_config['name'],
648 'value' => 'Next run: ' . $this->convert_time_to_date( $next_scheduled, true, true ),
649 'comment' => $comment,
650 ];
651 }
652
653 $suports_async = false;
654 if ( ! WpMatomo::is_safe_mode() && $report ) {
655 $rows[] = [
656 'section' => esc_html__( 'Mandatory checks', 'matomo' ),
657 ];
658
659 $rows = $this->add_diagnostic_results( $rows, $report->getMandatoryDiagnosticResults() );
660
661 $rows[] = [
662 'section' => esc_html__( 'Optional checks', 'matomo' ),
663 ];
664 $rows = $this->add_diagnostic_results( $rows, $report->getOptionalDiagnosticResults() );
665
666 $cli_multi = new CliMulti();
667 $suports_async = $cli_multi->supportsAsync();
668
669 $rows[] = [
670 'name' => 'Supports Async Archiving',
671 'value' => $suports_async,
672 'comment' => '',
673 ];
674
675 $location_provider = LocationProvider::getCurrentProvider();
676 if ( $location_provider ) {
677 $rows[] = [
678 'name' => 'Location provider ID',
679 'value' => $location_provider->getId(),
680 'comment' => '',
681 ];
682 $rows[] = [
683 'name' => 'Location provider available',
684 'value' => $location_provider->isAvailable(),
685 'comment' => '',
686 ];
687 $rows[] = [
688 'name' => 'Location provider working',
689 'value' => $location_provider->isWorking(),
690 'comment' => '',
691 ];
692 }
693
694 if ( ! WpMatomo::is_safe_mode() ) {
695 Bootstrap::do_bootstrap();
696 $general = Config::getInstance()->General;
697
698 if ( empty( $general['proxy_client_headers'] ) ) {
699 foreach ( AdvancedSettings::$valid_host_headers as $header ) {
700 if ( ! empty( $_SERVER[ $header ] ) ) {
701 $rows[] = [
702 'name' => 'Proxy header',
703 'value' => $header,
704 'is_warning' => true,
705 'comment' => sprintf( esc_html__( '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. %s', 'matomo' ), sprintf( '<a href="%s" target="_blank">%s</a>', 'https://matomo.org/faq/wordpress/how-do-i-fix-the-proxy-header-warning-in-the-matomo-for-wordpress-system-report/', esc_html__( 'Learn more', 'matomo' ) ) ),
706 ];
707 }
708 }
709 }
710 $incompatible_plugins = Plugin\Manager::getInstance()->getIncompatiblePlugins( Version::VERSION );
711 if ( ! empty( $incompatible_plugins ) ) {
712 $rows[] = [
713 'section' => esc_html__( 'Incompatible Matomo plugins', 'matomo' ),
714 ];
715 foreach ( $incompatible_plugins as $plugin ) {
716 $rows[] = [
717 'name' => 'Plugin has missing dependencies',
718 'value' => $plugin->getPluginName(),
719 'is_error' => true,
720 'comment' => sprintf( esc_html__( '%s If the plugin requires a different Matomo version you may need to update it. If you no longer use it consider uninstalling it.', 'matomo' ), $plugin->getMissingDependenciesAsString( Version::VERSION ) ),
721 ];
722 }
723 }
724 }
725
726 $num_days_check_visits = 5;
727 $had_visits = $this->had_visits_in_last_days( $num_days_check_visits );
728
729 if ( false === $had_visits || true === $had_visits ) {
730 // do not show info if we could not detect it (had_visits === null)
731 $comment = '';
732 if ( ! $had_visits ) {
733 $comment = sprintf( esc_html__( 'It looks like there were no visits in the last %s 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.', 'matomo' ), $num_days_check_visits );
734 }
735
736 $rows[] = [
737 'name' => 'Had visit in last ' . $num_days_check_visits . ' days',
738 'value' => $had_visits,
739 'is_warning' => ! $had_visits && $this->settings->is_tracking_enabled(),
740 'comment' => $comment,
741 ];
742 }
743
744 if ( ! WpMatomo::is_safe_mode() ) {
745 Bootstrap::do_bootstrap();
746 $matomo_url = SettingsPiwik::getPiwikUrl();
747 $rows[] = [
748 'name' => 'Matomo URL',
749 'comment' => $matomo_url,
750 'value' => ! empty( $matomo_url ),
751 ];
752 }
753 }
754
755 $rows[] = [
756 'section' => 'Matomo Settings',
757 ];
758
759 // always show these settings
760 $global_settings_always_show = [
761 'track_mode',
762 'track_codeposition',
763 'track_api_endpoint',
764 'track_js_endpoint',
765 ];
766 foreach ( $global_settings_always_show as $key ) {
767 $rows[] = [
768 'name' => ucfirst( str_replace( '_', ' ', $key ) ),
769 'value' => $this->settings->get_global_option( $key ),
770 'comment' => '',
771 ];
772 }
773
774 // otherwise show only few customised settings
775 // mostly only numeric values and booleans to not eg accidentally show anything that would store a token etc
776 // like we don't want to show license key etc
777 foreach ( $this->settings->get_customised_global_settings() as $key => $val ) {
778 if ( is_numeric( $val ) || is_bool( $val ) || 'track_content' === $key || 'track_user_id' === $key || 'core_version' === $key || 'version_history' === $key || 'mail_history' === $key ) {
779 if ( is_array( $val ) ) {
780 $val = implode( ', ', $val );
781 }
782
783 $rows[] = [
784 'name' => ucfirst( str_replace( '_', ' ', $key ) ),
785 'value' => $val,
786 'comment' => '',
787 ];
788 }
789 }
790
791 $rows[] = [
792 'section' => 'Logs',
793 ];
794
795 $error_log_entries = $this->logger->get_last_logged_entries();
796
797 if ( ! empty( $error_log_entries ) ) {
798 foreach ( $error_log_entries as $error ) {
799 if ( ! empty( $install_time )
800 && is_numeric( $install_time )
801 && ! empty( $error['name'] )
802 && ! empty( $error['value'] )
803 && is_numeric( $error['value'] )
804 && 'cron_sync' === $error['name']
805 && $error['value'] < ( $install_time + 300 )
806 ) {
807 // the first sync might right after the installation
808 continue;
809 }
810
811 // we only consider plugin_updates as errors only if there are still outstanding updates
812 $is_plugin_update_error = ! empty( $error['name'] ) && 'plugin_update' === $error['name']
813 && ! empty( $outstanding_updates );
814
815 $skip_plugin_update = ! empty( $error['name'] ) && 'plugin_update' === $error['name']
816 && empty( $outstanding_updates );
817
818 if ( empty( $error['comment'] ) && '0' !== $error['comment'] ) {
819 $error['comment'] = '';
820 }
821
822 if ( strpos( $error['comment'], '<head>' ) ) {
823 $error['comment'] = esc_html( $error['comment'] );
824 $error['comment'] = $this->replace_hexadecimal_colors( $error['comment'] );
825 }
826
827 $error['value'] = $this->convert_time_to_date( $error['value'], true, false );
828 $error['is_warning'] = ! empty( $error['name'] ) && stripos( $error['name'], 'archiv' ) !== false && 'archive_boot' !== $error['name'];
829 $error['is_error'] = $is_plugin_update_error;
830 if ( $is_plugin_update_error ) {
831 $error['comment'] = sprintf( esc_html__( 'Please reach out to us and include the copied system report (%s)<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.', 'matomo' ), sprintf( '<a href="%s" target="_blank">%s</a>', 'https://matomo.org/faq/wordpress/how-do-i-troubleshoot-a-failed-database-upgrade-in-matomo-for-wordpress/', esc_html__( 'more info', 'matomo' ) ) ) . $error['comment'];
832 } elseif ( $skip_plugin_update ) {
833 $error['comment'] = esc_html__( 'As there are no outstanding plugin updates it looks like this log can be ignored.', 'matomo' ) . '<br><br>' . $error['comment'];
834 }
835 $error['comment'] = matomo_anonymize_value( $error['comment'] );
836 $rows[] = $error;
837 }
838
839 foreach ( $error_log_entries as $error ) {
840 if ( $suports_async
841 && ! empty( $error['value'] )
842 && is_string( $error['value'] )
843 && strpos( $error['value'], __( 'Your PHP installation appears to be missing the MySQL extension which is required by WordPress.', 'matomo' ) ) > 0
844 ) {
845 $rows[] = [
846 'name' => 'Cli has no MySQL',
847 'value' => true,
848 'comment' => sprintf( esc_html__( 'It looks like MySQL is not available on CLI. Please read our FAQ on how to %s', 'matomo' ), sprintf( ' <a href="%s" target="_blank">%s</a>', '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/', esc_html__( 'fix this issue', 'matomo' ) ) ),
849 'is_error' => true,
850 ];
851 }
852 }
853 } else {
854 $rows[] = [
855 'name' => __( 'None', 'matomo' ),
856 'value' => '',
857 'comment' => '',
858 ];
859 }
860
861 if ( ! WpMatomo::is_safe_mode() ) {
862 Bootstrap::do_bootstrap();
863 $trackfailures = [];
864 try {
865 $tracking_failures = new Failures();
866 $trackfailures = $tracking_failures->getAllFailures();
867 // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
868 } catch ( Exception $e ) {
869 // ignored in case not set up yet etc.
870 }
871 if ( ! empty( $trackfailures ) ) {
872 $rows[] = [
873 'section' => 'Tracking failures',
874 ];
875 foreach ( $trackfailures as $failure ) {
876 $comment = sprintf(
877 'Solution: %s<br>More info: %s<br>Date: %s<br>Request URL: %s',
878 $failure['solution'],
879 $failure['solution_url'],
880 $failure['pretty_date_first_occurred'],
881 $failure['request_url']
882 );
883 // do not esc_html the comment: we want the br
884 $rows[] = [
885 'name' => $failure['problem'],
886 'is_warning' => true,
887 'value' => '',
888 'comment' => $comment,
889 ];
890 }
891 }
892 }
893
894 return $rows;
895 }
896
897 private function had_visits_in_last_days( $num_days ) {
898 global $wpdb;
899
900 if ( WpMatomo::is_safe_mode() ) {
901 return null;
902 }
903
904 $days_in_seconds = $num_days * 86400;
905
906 $prefix_table = $this->db_settings->prefix_table_name( 'log_visit' );
907
908 $suppress_errors = $wpdb->suppress_errors;
909 $wpdb->suppress_errors( true );// prevent any of this showing in logs just in case
910
911 try {
912 $time = gmdate( 'Y-m-d H:i:s', time() - $days_in_seconds );
913 $sql = $wpdb->prepare( 'SELECT idsite from ' . $prefix_table . ' where visit_last_action_time > %s LIMIT 1', $time );
914 $row = $wpdb->get_var( $sql );
915 } catch ( Exception $e ) {
916 $row = null;
917 }
918
919 $wpdb->suppress_errors( $suppress_errors );
920 // we need to differentiate between
921 // 0 === had no visit
922 // 1 === had visit
923 // null === sum error... eg table was not correctly installed
924 if ( null !== $row ) {
925 $row = ! empty( $row );
926 }
927
928 return $row;
929 }
930
931 private function convert_time_to_date( $time, $in_blog_timezone, $print_diff = false ) {
932 if ( empty( $time ) ) {
933 return esc_html__( 'Unknown', 'matomo' );
934 }
935
936 $date = gmdate( 'Y-m-d H:i:s', (int) $time );
937
938 if ( $in_blog_timezone ) {
939 $date = get_date_from_gmt( $date, 'Y-m-d H:i:s' );
940 }
941
942 if ( $print_diff && class_exists( '\Piwik\Metrics\Formatter' ) ) {
943 $formatter = new \Piwik\Metrics\Formatter();
944 $date .= ' (' . $formatter->getPrettyTimeFromSeconds( $time - time(), true, false ) . ')';
945 }
946
947 return $date;
948 }
949
950 private function add_diagnostic_results( $rows, $results ) {
951 foreach ( $results as $result ) {
952 $comment = '';
953 /** @var DiagnosticResult $result */
954 if ( $result->getStatus() !== DiagnosticResult::STATUS_OK ) {
955 foreach ( $result->getItems() as $item ) {
956 $item_comment = $item->getComment();
957 if ( ! empty( $item_comment ) && is_string( $item_comment ) ) {
958 if ( stripos( $item_comment, 'core:archive' ) > 0 ) {
959 // 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"
960 // but not anything that asks user to set up a cronjob
961 $item_comment = substr( $item_comment, 0, stripos( $item_comment, 'core:archive' ) );
962 if ( strpos( $item_comment, '.' ) > 0 ) {
963 $item_comment = substr( $item_comment, 0, strripos( $item_comment, '.' ) );
964 } else {
965 $item_comment = 'Archiving hasn\'t run in a while.';
966 }
967 }
968 $comment .= $item_comment . '<br/>';
969 }
970 }
971 }
972
973 $rows[] = [
974 'name' => $result->getLabel(),
975 'value' => $result->getStatus() . ' ' . $result->getLongErrorMessage(),
976 'comment' => $comment,
977 'is_warning' => $result->getStatus() === DiagnosticResult::STATUS_WARNING,
978 'is_error' => $result->getStatus() === DiagnosticResult::STATUS_ERROR,
979 ];
980 }
981
982 return $rows;
983 }
984
985 private function get_wordpress_info() {
986 $is_multi_site = is_multisite();
987 $num_blogs = 1;
988 $is_network_enabled = false;
989 $matomo_id_sites_number = 1;
990 if ( $is_multi_site ) {
991 if ( function_exists( 'get_blog_count' ) ) {
992 $num_blogs = get_blog_count();
993 }
994 $settings = new Settings();
995 $is_network_enabled = $settings->is_network_enabled();
996 } else {
997 $sites_manager_model = new Model();
998 $matomo_id_sites_number = count( $sites_manager_model->getSitesId() );
999 }
1000
1001 $rows = [];
1002 $rows[] = [
1003 'name' => 'Home URL',
1004 'value' => home_url(),
1005 ];
1006 $rows[] = [
1007 'name' => 'Site URL',
1008 'value' => site_url(),
1009 ];
1010 $rows[] = [
1011 'name' => 'WordPress Version',
1012 'value' => get_bloginfo( 'version' ),
1013 ];
1014 $rows[] = [
1015 'name' => 'Number of blogs',
1016 'value' => $num_blogs,
1017 ];
1018 $rows[] = [
1019 'name' => 'Multisite Enabled',
1020 'value' => $is_multi_site,
1021 ];
1022 $rows[] = [
1023 'name' => 'Network Enabled',
1024 'value' => $is_network_enabled,
1025 ];
1026 $consts = [
1027 'WP_DEBUG',
1028 'WP_DEBUG_DISPLAY',
1029 'WP_DEBUG_LOG',
1030 'DISABLE_WP_CRON',
1031 'FORCE_SSL_ADMIN',
1032 'WP_CACHE',
1033 'CONCATENATE_SCRIPTS',
1034 'COMPRESS_SCRIPTS',
1035 'COMPRESS_CSS',
1036 'ENFORCE_GZIP',
1037 'WP_LOCAL_DEV',
1038 'WP_CONTENT_URL',
1039 'WP_CONTENT_DIR',
1040 'UPLOADS',
1041 'BLOGUPLOADDIR',
1042 'DIEONDBERROR',
1043 'WPLANG',
1044 'ALTERNATE_WP_CRON',
1045 'WP_CRON_LOCK_TIMEOUT',
1046 'WP_DISABLE_FATAL_ERROR_HANDLER',
1047 'MATOMO_SUPPORT_ASYNC_ARCHIVING',
1048 'MATOMO_TRIGGER_BROWSER_ARCHIVING',
1049 'MATOMO_ENABLE_TAG_MANAGER',
1050 'MATOMO_SUPPRESS_DB_ERRORS',
1051 'MATOMO_ENABLE_AUTO_UPGRADE',
1052 'MATOMO_DEBUG',
1053 'MATOMO_SAFE_MODE',
1054 'MATOMO_GLOBAL_UPLOAD_DIR',
1055 'MATOMO_LOGIN_REDIRECT',
1056 ];
1057 foreach ( $consts as $const ) {
1058 $rows[] = [
1059 'name' => $const,
1060 'value' => defined( $const ) ? constant( $const ) : '-',
1061 ];
1062 }
1063
1064 $rows[] = [
1065 'name' => 'Permalink Structure',
1066 'value' => get_option( 'permalink_structure' ) ? get_option( 'permalink_structure' ) : 'Default',
1067 ];
1068
1069 $rows[] = [
1070 'name' => 'Possibly uses symlink',
1071 'value' => strpos( __DIR__, ABSPATH ) === false && strpos( __DIR__, WP_CONTENT_DIR ) === false,
1072 ];
1073
1074 $upload_dir = wp_upload_dir();
1075 $rows[] = [
1076 'name' => 'Upload base url',
1077 'value' => $upload_dir['baseurl'],
1078 ];
1079
1080 $rows[] = [
1081 'name' => 'Upload base dir',
1082 'value' => $upload_dir['basedir'],
1083 ];
1084
1085 $rows[] = [
1086 'name' => 'Upload url',
1087 'value' => $upload_dir['url'],
1088 ];
1089
1090 foreach ( [ 'upload_path', 'upload_url_path' ] as $option_read ) {
1091 $rows[] = [
1092 'name' => 'Custom ' . $option_read,
1093 'value' => get_option( $option_read ),
1094 ];
1095 }
1096
1097 if ( is_plugin_active( 'wp-piwik/wp-piwik.php' ) ) {
1098 $rows[] = [
1099 'name' => 'WP-Matomo (WP-Piwik) activated',
1100 'value' => true,
1101 'is_warning' => true,
1102 'comment' => sprintf( esc_html__( '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 %s', 'matomo' ), sprintf( '<a href="%s" target="_blank">%s</a>', 'https://matomo.org/faq/wordpress/why-are-there-two-different-matomo-for-wordpress-plugins-what-is-the-difference-to-wp-matomo-integration-plugin/', esc_html__( 'URL', 'matomo' ) ) ),
1103 ];
1104
1105 $mode = get_option( 'wp-piwik_global-piwik_mode' );
1106 if ( function_exists( 'get_site_option' ) && is_plugin_active_for_network( 'wp-piwik/wp-piwik.php' ) ) {
1107 $mode = get_site_option( 'wp-piwik_global-piwik_mode' );
1108 }
1109 if ( ! empty( $mode ) ) {
1110 $rows[] = [
1111 'name' => 'WP-Matomo mode',
1112 'value' => $mode,
1113 'is_warning' => 'php' === $mode || 'PHP' === $mode,
1114 'comment' => esc_html__( '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".', 'matomo' ),
1115 ];
1116 }
1117 }
1118
1119 $compatible_content_dir = matomo_has_compatible_content_dir();
1120 if ( true === $compatible_content_dir ) {
1121 $rows[] = [
1122 'name' => 'Compatible content directory',
1123 'value' => true,
1124 ];
1125 } else {
1126 $rows[] = [
1127 'name' => 'Compatible content directory',
1128 'value' => $compatible_content_dir,
1129 'is_warning' => true,
1130 'comment' => esc_html__( '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' ) . ' ' . sprintf( '<a href="%s" target="_blank">%s</a>', 'https://matomo.org/faq/wordpress/how-do-i-make-matomo-for-wordpress-work-when-i-have-a-custom-content-directory/', esc_html__( 'Learn more', 'matomo' ) ),
1131 ];
1132 }
1133
1134 return $rows;
1135 }
1136
1137 private function add_maxminddb_row( &$rows, $maxmind_db_loaded ) {
1138 $rows[] = [
1139 'name' => esc_html__( 'PHP Maxmind DB extension', 'matomo' ),
1140 'value' => $maxmind_db_loaded ? __( 'Loaded', 'matomo' ) : __( 'Not loaded', 'matomo' ),
1141 'comment' => $maxmind_db_loaded ? sprintf( esc_html__( 'You may encounter %s', 'matomo' ), sprintf( '<a href="%s" target="_blank">%s</a>', 'https://matomo.org/faq/troubleshooting/how-do-i-fix-the-error-call-to-undefined-method-maxminddbreadergetwithprefixlen/', esc_html__( 'the following problem', 'matomo' ) ) ) : '',
1142 'is_warning' => $maxmind_db_loaded,
1143 ];
1144 }
1145
1146 private function get_server_info() {
1147 $rows = [];
1148
1149 if ( ! empty( $_SERVER['SERVER_SOFTWARE'] ) ) {
1150 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
1151 $server_software = sanitize_text_field( $_SERVER['SERVER_SOFTWARE'] );
1152 $rows[] = [
1153 'name' => 'Server Info',
1154 'value' => $server_software,
1155 ];
1156 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
1157 if ( strpos( $server_software, 'Apache' ) !== false ) {
1158 $url = plugins_url( 'app', MATOMO_ANALYTICS_FILE ) . '/index.php';
1159 $result = wp_remote_post(
1160 $url,
1161 array(
1162 'method' => 'GET',
1163 'sslverify' => false,
1164 'timeout' => 2,
1165 )
1166 );
1167 if ( is_array( $result ) ) {
1168 // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents
1169 $file_content = file_get_contents( dirname( MATOMO_ANALYTICS_FILE ) . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . '.htaccess' );
1170 if ( strpos( $file_content, 'AddHandler' ) && ! strpos( $file_content, '# AddHandler' ) ) {
1171 switch ( (int) $result['response']['code'] ) {
1172 case 500:
1173 $value = __( 'To be confirmed', 'matomo' );
1174 $comment = sprintf( esc_html__( 'The AddHandler Apache directive maybe disabled. If you get a 500 error code when accessing Matomo, please read this %s', 'matomo' ), sprintf( '<a href="%s" target="_blank">%s<a/>', 'https://matomo.org/faq/wordpress/how-do-i-fix-the-error-addhandler-not-allowed-here/', esc_html__( 'FAQ', 'matomo' ) ) );
1175 $is_error = true;
1176 break;
1177 default:
1178 $value = __( 'Supported', 'matomo' );
1179 $comment = '';
1180 $is_error = false;
1181 }
1182 $rows[] = [
1183 'name' => 'Apache AddHandler support',
1184 'value' => $value,
1185 'comment' => $comment,
1186 'is_error' => $is_error,
1187 ];
1188 }
1189 }
1190 }
1191 }
1192
1193 if ( PHP_OS ) {
1194 $rows[] = [
1195 'name' => 'PHP OS',
1196 'value' => PHP_OS,
1197 ];
1198 }
1199 $rows[] = [
1200 'name' => 'PHP Version',
1201 'value' => phpversion(),
1202 ];
1203 $rows[] = [
1204 'name' => 'PHP SAPI',
1205 'value' => php_sapi_name(),
1206 ];
1207 if ( defined( 'PHP_BINARY' ) && PHP_BINARY ) {
1208 $rows[] = [
1209 'name' => 'PHP Binary Name',
1210 'value' => PHP_BINARY,
1211 ];
1212 }
1213
1214 $this->add_maxminddb_row( $rows, extension_loaded( 'maxminddb' ) );
1215
1216 // we report error reporting before matomo bootstraped and after to see if Matomo changed it successfully etc
1217 $rows[] = [
1218 'name' => 'PHP Error Reporting',
1219 // phpcs:ignore WordPress.PHP.DevelopmentFunctions.prevent_path_disclosure_error_reporting
1220 'value' => $this->initial_error_reporting . ' After bootstrap: ' . @error_reporting(),
1221 ];
1222
1223 if ( ! empty( $this->binary ) ) {
1224 $rows[] = [
1225 'name' => 'PHP Found Binary',
1226 'value' => $this->binary,
1227 ];
1228 }
1229 $rows[] = [
1230 'name' => 'Timezone',
1231 'value' => date_default_timezone_get(),
1232 ];
1233 if ( function_exists( 'wp_timezone_string' ) ) {
1234 $rows[] = [
1235 'name' => 'WP timezone',
1236 'value' => wp_timezone_string(),
1237 ];
1238 }
1239 $rows[] = [
1240 'name' => 'Locale',
1241 'value' => get_locale(),
1242 ];
1243 if ( function_exists( 'get_user_locale' ) ) {
1244 $rows[] = [
1245 'name' => 'User Locale',
1246 'value' => get_user_locale(),
1247 ];
1248 }
1249
1250 $rows[] = [
1251 'name' => 'Memory Limit',
1252 'value' => @ini_get( 'memory_limit' ),
1253 'comment' => sprintf( esc_html__( 'At least %1$dMB recommended. Depending on your traffic %2$dMB or more may be needed.', 'matomo' ), 128, 256 ),
1254 ];
1255
1256 $rows[] = [
1257 'name' => 'WP Memory Limit',
1258 'value' => defined( 'WP_MEMORY_LIMIT' ) ? WP_MEMORY_LIMIT : '',
1259 'comment' => '',
1260 ];
1261
1262 $rows[] = [
1263 'name' => 'WP Max Memory Limit',
1264 'value' => defined( 'WP_MAX_MEMORY_LIMIT' ) ? WP_MAX_MEMORY_LIMIT : '',
1265 'comment' => '',
1266 ];
1267
1268 if ( function_exists( 'timezone_version_get' ) ) {
1269 $rows[] = [
1270 'name' => 'Timezone version',
1271 'value' => timezone_version_get(),
1272 ];
1273 }
1274
1275 $rows[] = [
1276 'name' => 'Time',
1277 'value' => time(),
1278 ];
1279
1280 $rows[] = [
1281 'name' => 'Max Execution Time',
1282 'value' => ini_get( 'max_execution_time' ),
1283 ];
1284 $rows[] = [
1285 'name' => 'Max Post Size',
1286 'value' => ini_get( 'post_max_size' ),
1287 ];
1288 $rows[] = [
1289 'name' => 'Max Upload Size',
1290 'value' => wp_max_upload_size(),
1291 ];
1292 $rows[] = [
1293 'name' => 'Max Input Vars',
1294 'value' => ini_get( 'max_input_vars' ),
1295 ];
1296
1297 $disabled_functions = ini_get( 'disable_functions' );
1298 $rows[] = [
1299 'name' => 'Disabled PHP functions',
1300 'value' => ! empty( $disabled_functions ),
1301 'comment' => ! empty( $disabled_functions ) ? $disabled_functions : '',
1302 ];
1303
1304 $zlib_compression = ini_get( 'zlib.output_compression' );
1305 $row = [
1306 'name' => 'zlib.output_compression is off',
1307 'value' => '1' !== $zlib_compression,
1308 ];
1309
1310 if ( '1' === $zlib_compression ) {
1311 $row['is_error'] = true;
1312 $row['comment'] = esc_html__( 'You need to set "zlib.output_compression" in your php.ini to "Off".', 'matomo' );
1313 }
1314 $rows[] = $row;
1315
1316 if ( function_exists( 'curl_version' ) ) {
1317 $curl_version = curl_version();
1318 $curl_version = $curl_version['version'] . ', ' . $curl_version['ssl_version'];
1319 $rows[] = [
1320 'name' => 'Curl Version',
1321 'value' => $curl_version,
1322 ];
1323 }
1324
1325 $suhosin_installed = ( extension_loaded( 'suhosin' ) || ( defined( 'SUHOSIN_PATCH' ) && constant( 'SUHOSIN_PATCH' ) ) );
1326 $rows[] = [
1327 'name' => 'Suhosin installed',
1328 'value' => ! empty( $suhosin_installed ),
1329 'comment' => '',
1330 ];
1331
1332 return $rows;
1333 }
1334
1335 private function get_browser_info() {
1336 $rows = [];
1337
1338 if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1339 $rows[] = [
1340 'name' => 'Browser',
1341 'value' => '',
1342 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
1343 'comment' => sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ),
1344 ];
1345 }
1346 if ( ! WpMatomo::is_safe_mode() ) {
1347 Bootstrap::do_bootstrap();
1348 try {
1349 if ( ! empty( $_SERVER['HTTP_USER_AGENT'] ) ) {
1350 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash
1351 $detector = StaticContainer::get( DeviceDetectorFactory::class )->makeInstance( sanitize_text_field( $_SERVER['HTTP_USER_AGENT'] ) );
1352 $client = $detector->getClient();
1353 if ( ! empty( $client['name'] ) && 'Microsoft Edge' === $client['name'] && (int) $client['version'] >= 85 ) {
1354 $rows[] = [
1355 'name' => 'Browser Compatibility',
1356 'is_warning' => true,
1357 'value' => 'Yes',
1358 'comment' => esc_html__( '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.', 'matomo' ),
1359 ];
1360 }
1361 }
1362 } catch ( Exception $e ) {
1363 $this->logger->log( $e->getMessage() );
1364 }
1365
1366 $rows[] = [
1367 'name' => 'Language',
1368 'value' => Common::getBrowserLanguage(),
1369 'comment' => '',
1370 ];
1371 }
1372
1373 return $rows;
1374 }
1375
1376 private function get_db_info() {
1377 global $wpdb;
1378 $rows = [];
1379
1380 $rows[] = [
1381 'name' => 'MySQL Version',
1382 'value' => ! empty( $wpdb->is_mysql ) ? $wpdb->db_version() : '',
1383 'comment' => '',
1384 ];
1385
1386 $rows[] = [
1387 'name' => 'Mysqli Connect',
1388 'value' => function_exists( 'mysqli_connect' ),
1389 'comment' => '',
1390 ];
1391 $rows[] = [
1392 'name' => 'Force MySQL over Mysqli',
1393 'value' => defined( 'WP_USE_EXT_MYSQL' ) && WP_USE_EXT_MYSQL,
1394 'comment' => '',
1395 ];
1396
1397 $rows[] = [
1398 'name' => 'DB Prefix',
1399 'value' => $wpdb->prefix,
1400 ];
1401
1402 $rows[] = [
1403 'name' => 'DB CHARSET',
1404 'value' => defined( 'DB_CHARSET' ) ? DB_CHARSET : '',
1405 ];
1406
1407 $rows[] = [
1408 'name' => 'DB COLLATE',
1409 'value' => defined( 'DB_COLLATE' ) ? DB_COLLATE : '',
1410 ];
1411
1412 $rows[] = [
1413 'name' => 'SHOW ERRORS',
1414 'value' => ! empty( $wpdb->show_errors ),
1415 ];
1416
1417 $rows[] = [
1418 'name' => 'SUPPRESS ERRORS',
1419 'value' => ! empty( $wpdb->suppress_errors ),
1420 ];
1421
1422 if ( method_exists( $wpdb, 'parse_db_host' ) ) {
1423 $host_data = $wpdb->parse_db_host( DB_HOST );
1424 if ( $host_data ) {
1425 list( $host, $port, $socket, $is_ipv6 ) = $host_data;
1426 }
1427
1428 $rows[] = [
1429 'name' => 'Uses Socket',
1430 'value' => ! empty( $socket ),
1431 ];
1432 $rows[] = [
1433 'name' => 'Uses IPv6',
1434 'value' => ! empty( $is_ipv6 ),
1435 ];
1436 }
1437
1438 $rows[] = [
1439 'name' => 'Matomo tables found',
1440 'value' => $this->get_num_matomo_tables(),
1441 ];
1442
1443 $missing_tables = $this->get_missing_tables();
1444 $has_missing_tables = ( count( $missing_tables ) > 0 );
1445 $rows[] = [
1446 'name' => 'DB tables exist',
1447 'value' => ( ! $has_missing_tables ),
1448 'comment' => $has_missing_tables ? sprintf( esc_html__( 'Some tables may be missing: %s', 'matomo' ), implode( ', ', $missing_tables ) ) : '',
1449 'is_error' => $has_missing_tables,
1450 ];
1451
1452 foreach ( [ 'user', 'site' ] as $table ) {
1453 $row = [
1454 'name' => 'Matomo ' . $table . 's found',
1455 'value' => $this->get_num_entries_in_table( $table ),
1456 ];
1457 if ( 'site' === $table ) {
1458 if ( ( ! is_multisite() ) && ( $row['value'] > 1 ) ) {
1459 $row['is_warning'] = true;
1460 $row['comment'] = esc_html__( 'There is an error in your Matomo records. Please contact wordpress@matomo.org', 'matomo' );
1461 }
1462 }
1463 $rows[] = $row;
1464 }
1465
1466 $grants = $this->get_db_grants();
1467
1468 // we only show these grants for security reasons as only they are needed and we don't need to know any other ones
1469 $needed_grants = [
1470 'SELECT',
1471 'INSERT',
1472 'UPDATE',
1473 'INDEX',
1474 'DELETE',
1475 'CREATE',
1476 'DROP',
1477 'ALTER',
1478 'CREATE TEMPORARY TABLES',
1479 'LOCK TABLES',
1480 ];
1481 if ( in_array( 'ALL PRIVILEGES', $grants, true ) ) {
1482 // ALL PRIVILEGES may be used pre MySQL 8.0
1483 $grants = $needed_grants;
1484 }
1485
1486 $grants_missing = array_diff( $needed_grants, $grants );
1487
1488 if ( empty( $grants )
1489 || ! is_array( $grants )
1490 || count( $grants_missing ) === count( $needed_grants )
1491 ) {
1492 $rows[] = [
1493 'name' => esc_html__( 'Required permissions', 'matomo' ),
1494 'value' => esc_html__( 'Failed to detect granted permissions', 'matomo' ),
1495 'comment' => esc_html__( 'Please check your MySQL user has these permissions (grants):', 'matomo' ) . '<br />' . implode( ', ', $needed_grants ),
1496 'is_warning' => false,
1497 ];
1498 } else {
1499 if ( ! empty( $grants_missing ) ) {
1500 $rows[] = [
1501 'name' => esc_html__( 'Required permissions', 'matomo' ),
1502 'value' => esc_html__( 'Error', 'matomo' ),
1503 'comment' => esc_html__( 'Missing permissions', 'matomo' ) . ': ' . implode( ', ', $grants_missing ) . '. ' . esc_html__( 'Please check if any of these MySQL permission (grants) are missing and add them if needed.', 'matomo' ) . ' ' . sprintf( '<a href="https://matomo.org/faq/troubleshooting/how-do-i-check-if-my-mysql-user-has-all-required-grants/" target="_blank">%s</a>', __( 'Learn more', 'matomo' ) ),
1504 'is_warning' => true,
1505 ];
1506 } else {
1507 $rows[] = [
1508 'name' => esc_html__( 'Required permissions', 'matomo' ),
1509 'value' => esc_html__( 'OK', 'matomo' ),
1510 'comment' => '',
1511 'is_warning' => false,
1512 ];
1513 }
1514 }
1515
1516 return $rows;
1517 }
1518
1519 /**
1520 * @return string[]
1521 */
1522 public function get_missing_tables() {
1523 global $wpdb;
1524
1525 $required_matomo_tables = $this->db_settings->get_matomo_tables();
1526 $required_matomo_tables = array_map( [ $this->db_settings, 'prefix_table_name' ], $required_matomo_tables );
1527
1528 $existing_tables = [];
1529 try {
1530 $prefix = $this->db_settings->prefix_table_name( '' );
1531 $existing_tables = $wpdb->get_col( 'SHOW TABLES LIKE "' . $prefix . '%"' );
1532 } catch ( Exception $e ) {
1533 $this->logger->log( 'no show tables: ' . $e->getMessage() );
1534 }
1535
1536 return array_diff( $required_matomo_tables, $existing_tables );
1537 }
1538
1539 private function get_num_entries_in_table( $table ) {
1540 global $wpdb;
1541
1542 $prefix = $this->db_settings->prefix_table_name( $table );
1543
1544 $results = null;
1545 try {
1546 $results = $wpdb->get_var( 'select count(*) from ' . $prefix );
1547 } catch ( Exception $e ) {
1548 $this->logger->log( 'no count(*): ' . $e->getMessage() );
1549 }
1550
1551 if ( isset( $results ) && is_numeric( $results ) ) {
1552 return $results;
1553 }
1554
1555 return 'table not exists';
1556 }
1557
1558 private function get_num_matomo_tables() {
1559 global $wpdb;
1560
1561 $prefix = $this->db_settings->prefix_table_name( '' );
1562
1563 $results = null;
1564 try {
1565 $results = $wpdb->get_results( 'show tables like "' . $prefix . '%"' );
1566 } catch ( Exception $e ) {
1567 $this->logger->log( 'no show tables: ' . $e->getMessage() );
1568 }
1569
1570 if ( is_array( $results ) ) {
1571 return count( $results );
1572 }
1573
1574 return 'show tables not working';
1575 }
1576
1577 private function get_db_grants() {
1578 global $wpdb;
1579
1580 $suppress_errors = $wpdb->suppress_errors;
1581 $wpdb->suppress_errors( true );// prevent any of this showing in logs just in case
1582
1583 try {
1584 $values = $wpdb->get_results( 'SHOW GRANTS', ARRAY_N );
1585 } catch ( Exception $e ) {
1586 // We ignore any possible error in case of permission or not supported etc.
1587 $values = [];
1588 }
1589
1590 $wpdb->suppress_errors( $suppress_errors );
1591
1592 $grants = [];
1593 foreach ( $values as $index => $value ) {
1594 if ( empty( $value[0] ) || ! is_string( $value[0] ) ) {
1595 continue;
1596 }
1597
1598 if ( stripos( $value[0], 'ALL PRIVILEGES' ) !== false ) {
1599 return [ 'ALL PRIVILEGES' ]; // the split on empty string wouldn't work otherwise
1600 }
1601
1602 foreach ( [ ' ON ', ' TO ', ' IDENTIFIED ', ' BY ' ] as $keyword ) {
1603 if ( stripos( $values[ $index ][0], $keyword ) !== false ) {
1604 // make sure to never show by any accident a db user or password by cutting anything after on/to
1605 $values[ $index ][0] = substr( $values[ $index ][0], 0, stripos( $values[ $index ][0], $keyword ) );
1606 }
1607 if ( stripos( $values[ $index ][0], 'GRANT' ) !== false ) {
1608 // otherwise we end up having "grant select"... instead of just "select"
1609 $values[ $index ][0] = substr( $values[ $index ][0], stripos( $values[ $index ][0], 'GRANT' ) + 5 );
1610 }
1611 }
1612 // make sure to never show by any accident a db user or password
1613 $values[ $index ][0] = str_replace(
1614 [ DB_USER, DB_PASSWORD ],
1615 [
1616 'DB_USER',
1617 'DB_PASS',
1618 ],
1619 $values[ $index ][0]
1620 );
1621
1622 $grants = array_merge( $grants, explode( ',', $values[ $index ][0] ) );
1623 }
1624 $grants = array_map( 'trim', $grants );
1625 $grants = array_map( 'strtoupper', $grants );
1626 $grants = array_unique( $grants );
1627
1628 return $grants;
1629 }
1630
1631 /**
1632 * @return string[]
1633 */
1634 private function get_actives_plugins() {
1635 $active_plugins = get_option( 'active_plugins', [] );
1636 if ( ! empty( $active_plugins ) && is_array( $active_plugins ) ) {
1637 $active_plugins = array_map(
1638 function ( $active_plugin ) {
1639 $parts = explode( '/', trim( $active_plugin ) );
1640
1641 return trim( $parts[0] );
1642 },
1643 $active_plugins
1644 );
1645 }
1646
1647 return $active_plugins;
1648 }
1649
1650 private function get_plugins_info() {
1651 $rows = [];
1652 $mu_plugins = get_mu_plugins();
1653
1654 if ( ! empty( $mu_plugins ) ) {
1655 $rows[] = [
1656 'section' => 'MU Plugins',
1657 ];
1658
1659 foreach ( $mu_plugins as $mu_pin ) {
1660 $comment = '';
1661 if ( ! empty( $plugin['Network'] ) ) {
1662 $comment = esc_html__( 'Network enabled', 'matomo' );
1663 }
1664 $rows[] = [
1665 'name' => $mu_pin['Name'],
1666 'value' => $mu_pin['Version'],
1667 'comment' => $comment,
1668 ];
1669 }
1670
1671 $rows[] = [
1672 'section' => 'Plugins',
1673 ];
1674 }
1675
1676 $plugins = get_plugins();
1677
1678 foreach ( $plugins as $plugin ) {
1679 $comment = '';
1680 if ( ! empty( $plugin['Network'] ) ) {
1681 $comment = esc_html__( 'Network enabled', 'matomo' );
1682 }
1683 $rows[] = [
1684 'name' => $plugin['Name'],
1685 'value' => $plugin['Version'],
1686 'comment' => $comment,
1687 ];
1688 }
1689
1690 $active_plugins = $this->get_actives_plugins();
1691
1692 if ( ! empty( $active_plugins ) && is_array( $active_plugins ) ) {
1693 $rows[] = [
1694 'name' => 'Active Plugins',
1695 'value' => count( $active_plugins ),
1696 'comment' => implode( ' ', $active_plugins ),
1697 ];
1698
1699 $used_not_compatible = array_intersect( $active_plugins, $this->not_compatible_plugins );
1700 if ( in_array( 'wp-rocket', $used_not_compatible, true ) ) {
1701 if ( defined( 'WP_ROCKET_VERSION' ) && ( version_compare( WP_ROCKET_VERSION, '3.11.5' ) <= 0 ) ) {
1702 unset( $used_not_compatible[ array_search( 'wp-rocket', $used_not_compatible, true ) ] );
1703 }
1704 }
1705
1706 if ( ! empty( $used_not_compatible ) ) {
1707 $additional_comment = '';
1708 if ( in_array( 'tweet-old-post-pro', $used_not_compatible, true ) ) {
1709 $additional_comment .= '<br><br>' . esc_html__( 'A workaround for Revive Old Posts Pro may be to add the following line to your "wp-config.php"', 'matomo' ) . '<br><code>define( \'MATOMO_SUPPORT_ASYNC_ARCHIVING\', false );</code>.';
1710 }
1711 if ( in_array( 'post-smtp', $used_not_compatible, true ) ) {
1712 $additional_comment .= '<br><br>' . esc_html__( 'The PDF report files from the email reports will be missing when the PostSMTP mode is selected but it works when the PHPMailer mode is selected.', 'matomo' );
1713 }
1714 if ( in_array( 'wp-rocket', $used_not_compatible, true ) ) {
1715 $additional_comment .= '<br><br>' . sprintf( esc_html__( 'WP-Rocket is incompatible from version 3.12. Until fixes, please reinstall version 3.11.5 if you have a newer version. For more information please visit %s', 'matomo' ), sprintf( '<a href="%s" target="_blank">%s</a>', 'https://github.com/matomo-org/matomo-for-wordpress/wiki/Downgrade-wp-rocket-to-a-version-compatible-with-the-Matomo-plugin', esc_html__( 'How to downgrade Wp-rocket to be compatible with Matomo', 'matomo' ) ) );
1716 }
1717 $is_warning = false;
1718 $is_error = false;
1719 if ( count( $used_not_compatible ) ) {
1720 $is_warning = true;
1721 $is_error = false;
1722 if ( in_array( 'cookiebot', $used_not_compatible, true ) ) {
1723 $is_warning = false;
1724 $is_error = true;
1725 }
1726
1727 $rows[] = [
1728 'name' => __( 'Not compatible plugins', 'matomo' ),
1729 'value' => count( $used_not_compatible ),
1730 'comment' => implode( ', ', $used_not_compatible ) . '<br><br>' . sprintf( esc_html__( 'Matomo may work fine when using these plugins but there may be some issues. For more information %1$sSee %2$s', 'matomo' ), '<br/>', sprintf( '<a href="%s" target="_blank">%s</a>', 'https://matomo.org/faq/wordpress/which-plugins-is-matomo-for-wordpress-known-to-be-not-compatible-with/', esc_html__( 'this FAQ', 'matomo' ) ) ) . $additional_comment,
1731 'is_warning' => $is_warning,
1732 'is_error' => $is_error,
1733 ];
1734 }
1735 }
1736 }
1737
1738 $rows[] = [
1739 'name' => 'Theme',
1740 'value' => function_exists( 'get_template' ) ? get_template() : '',
1741 'comment' => get_option( 'stylesheet' ),
1742 ];
1743
1744 if ( is_plugin_active( 'better-wp-security/better-wp-security.php' ) ) {
1745 if ( class_exists( 'ITSEC_Modules' ) ) {
1746 if ( method_exists( '\ITSEC_Modules', 'get_setting' ) ) {
1747 $input = ITSEC_Modules::get_settings( 'system-tweaks' );
1748 // old plugin versions
1749 $long_url_strings_options = [ 'long_url_strings', 'st_longurl' ];
1750 $long_url_strings_enabled = false;
1751 foreach ( $long_url_strings_options as $option ) {
1752 if ( isset( $input[ $option ] ) && $input[ $option ] ) {
1753 $long_url_strings_enabled = true;
1754 }
1755 }
1756 if ( $long_url_strings_enabled ) {
1757 $rows[] = [
1758 'name' => "iThemes Security 'Long URLs' Enabled",
1759 'value' => true,
1760 'comment' => esc_html__( 'Tracking might not work because it looks like you have Long URLs disabled in iThemes Security. To fix this please contact ithemes security support.', 'matomo' ),
1761 'is_error' => true,
1762 ];
1763 }
1764 if ( $input['plugins_php'] ) {
1765 $rows[] = [
1766 'name' => "iThemes Security 'Disable PHP in plugins' Enabled",
1767 'value' => true,
1768 'comment' => esc_html__( 'You have disabled the PHP usage in the plugins folder from your ithemes security plugin. Matomo won\'t work in this configuration. You must uncheck the checkbox "Security > Settings > Advanced > System tweaks > Disable PHP in plugins."', 'matomo' ),
1769 'is_error' => true,
1770 ];
1771 }
1772 }
1773 }
1774 }
1775
1776 if ( is_plugin_active( 'secupress/secupress.php' ) ) {
1777 if ( function_exists( 'secupress_is_submodule_active' ) ) {
1778 $blocked_methods = (int) secupress_is_submodule_active( 'firewall', 'request-methods-header' );
1779 if ( $blocked_methods ) {
1780 if ( ! defined( 'MATOMO_SUPPORT_ASYNC_ARCHIVING' ) || MATOMO_SUPPORT_ASYNC_ARCHIVING ) {
1781 $rows[] = [
1782 'name' => "Secupress 'Block Bad Request Methods' Enabled",
1783 'value' => true,
1784 'comment' => esc_html__( "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\"", 'matomo' ) . ": <br><code>define( 'MATOMO_SUPPORT_ASYNC_ARCHIVING', false );</code>.",
1785 'is_error' => true,
1786 ];
1787 }
1788 }
1789 }
1790 }
1791
1792 return $rows;
1793 }
1794
1795 /**
1796 * Convert the hexadecimal colors in the content into their rgb values
1797 *
1798 * @param string $content
1799 *
1800 * @return string
1801 */
1802 private function replace_hexadecimal_colors( $content ) {
1803 $matches = array();
1804 if ( preg_match_all( '/ (#(([a-f0-9]{8})|([a-f0-9]{4}[ ;])))/i', $content, $matches ) ) {
1805 foreach ( $matches[1] as $hexadecimal_color ) {
1806 switch ( strlen( $hexadecimal_color ) ) {
1807 case 9:
1808 list( $r, $g, $b, $a ) = sscanf( $hexadecimal_color, '#%02x%02x%02x%02x' );
1809 break;
1810 case 6:
1811 $hexadecimal_color = substr( $hexadecimal_color, 0, 5 );
1812 list( $r, $g, $b, $a ) = sscanf( $hexadecimal_color, '#%01x%01x%01x%01x' );
1813 break;
1814 }
1815 $content = str_replace( $hexadecimal_color, 'rgb(' . $r . ',' . $g . ',' . $b . ',' . $a . ')', $content );
1816 }
1817 }
1818 if ( preg_match_all( '/ (#(([a-f0-9]{6})|([a-f0-9]{3})))/i', $content, $matches ) ) {
1819 foreach ( $matches[1] as $hexadecimal_color ) {
1820 switch ( strlen( $hexadecimal_color ) ) {
1821 case 7:
1822 list( $r, $g, $b ) = sscanf( $hexadecimal_color, '#%02x%02x%02x' );
1823 break;
1824 case 4:
1825 list( $r, $g, $b ) = sscanf( $hexadecimal_color, '#%01x%01x%01x' );
1826 break;
1827 }
1828 $content = str_replace( $hexadecimal_color, 'rgb(' . $r . ',' . $g . ',' . $b . ')', $content );
1829 }
1830 }
1831
1832 return $content;
1833 }
1834 }
1835