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