. * @since 1.0.0 */ namespace Decalog\Plugin; use Decalog\Plugin\Feature\Autolog; use Decalog\Plugin\Feature\BootstrapManager; use Decalog\System\APCu; use Decalog\System\Blog; use Decalog\System\SharedMemory; use Decalog\Plugin\Feature\Log; use Decalog\Plugin\Feature\EventViewer; use Decalog\Plugin\Feature\HandlerTypes; use Decalog\Plugin\Feature\ProcessorTypes; use Decalog\Plugin\Feature\LoggerFactory; use Decalog\Plugin\Feature\Events; use Decalog\Plugin\Feature\Traces; use Decalog\Plugin\Feature\InlineHelp; use Decalog\Listener\ListenerFactory; use Decalog\System\Assets; use Decalog\System\UUID; use Decalog\System\Option; use Decalog\System\Form; use Decalog\System\Role; use Decalog\System\GeoIP; use Decalog\System\Environment; use DLMonolog\Logger; use PerfOpsOne\Menus; use PerfOpsOne\AdminBar; use Decalog\Plugin\Feature\DLogger; use Decalog\Plugin\Feature\TraceViewer; use Decalog\System\Cache; /** * The admin-specific functionality of the plugin. * * @package Plugin * @author Pierre Lannoy . * @since 1.0.0 */ class Decalog_Admin { /** * The assets manager that's responsible for handling all assets of the plugin. * * @since 1.0.0 * @var Assets $assets The plugin assets manager. */ protected $assets; /** * The internal logger. * * @since 1.0.0 * @var DLogger $logger The plugin admin logger. */ protected $logger; /** * The current logger. * * @since 1.0.0 * @var array $current_logger The current logger. */ protected $current_logger; /** * The current handler. * * @since 1.0.0 * @var array $current_handler The current handler. */ protected $current_handler; /** * The current view. * * @since 1.0.0 * @var object $current_view The current view. */ protected $current_view = null; /** * The logger will be created. * * @since 3.0.0 * @var boolean $creation_mode The form mode (creation / editing). */ protected $creation_mode = false; /** * Initialize the class and set its properties. * * @since 1.0.0 */ public function __construct() { $this->assets = new Assets(); $this->logger = Log::bootstrap( 'plugin', DECALOG_PRODUCT_SHORTNAME, DECALOG_VERSION ); } /** * Register the stylesheets for the admin area. * * @since 1.0.0 */ public function register_styles() { $this->assets->register_style( DECALOG_ASSETS_ID, DECALOG_ADMIN_URL, 'css/decalog.min.css' ); $this->assets->register_style( DECALOG_LIVELOG_ID, DECALOG_ADMIN_URL, 'css/livelog.min.css' ); } /** * Register the JavaScript for the admin area. * * @since 1.0.0 */ public function register_scripts() { $this->assets->register_script( DECALOG_ASSETS_ID, DECALOG_ADMIN_URL, 'js/decalog.min.js', [ 'jquery' ] ); $this->assets->register_script( DECALOG_LIVELOG_ID, DECALOG_ADMIN_URL, 'js/livelog.min.js', [ 'jquery' ] ); } /** * The reference to the class that orchestrates the hooks with the plugin. * * @since 2.0.0 */ public function disable_wp_emojis() { if ( 'decalog-console' === filter_input( INPUT_GET, 'page' ) ) { remove_action( 'wp_head', 'print_emoji_detection_script', 7 ); remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); remove_action( 'wp_print_styles', 'print_emoji_styles' ); remove_action( 'admin_print_styles', 'print_emoji_styles' ); remove_filter( 'the_content_feed', 'wp_staticize_emoji' ); remove_filter( 'comment_text_rss', 'wp_staticize_emoji' ); remove_filter( 'wp_mail', 'wp_staticize_emoji_for_email' ); } } /** * Sets the help action for the settings page. * * @param string $hook_suffix The hook suffix. * @since 1.0.0 */ public function set_settings_help( $hook_suffix ) { add_action( 'load-' . $hook_suffix, [ new InlineHelp(), 'set_contextual_settings' ] ); } /** * Sets the help action (and boxes settings) for the viewer. * * @param string $hook_suffix The hook suffix. * @since 1.0.0 */ public function set_viewer_help( $hook_suffix ) { $this->current_view = null; add_action( 'load-' . $hook_suffix, [ new InlineHelp(), 'set_contextual_viewer' ] ); $logid = filter_input( INPUT_GET, 'logid', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $eventid = filter_input( INPUT_GET, 'eventid', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $traceid = filter_input( INPUT_GET, 'traceid', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); if ( 'decalog-viewer' === filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ) { if ( isset( $logid ) && isset( $eventid ) && 0 !== $eventid ) { $this->current_view = new EventViewer( $logid, $eventid, $this->logger ); add_action( 'load-' . $hook_suffix, [ $this->current_view, 'add_metaboxes_options' ] ); add_action( 'admin_footer-' . $hook_suffix, [ $this->current_view, 'add_footer' ] ); add_filter( 'screen_settings', [ $this->current_view, 'display_screen_settings' ], 10, 2 ); } } if ( 'decalog-tviewer' === filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ) { if ( isset( $logid ) && isset( $traceid ) && 0 !== $traceid ) { $this->current_view = new TraceViewer( $logid, $traceid, $this->logger ); add_action( 'load-' . $hook_suffix, [ $this->current_view, 'add_metaboxes_options' ] ); add_action( 'admin_footer-' . $hook_suffix, [ $this->current_view, 'add_footer' ] ); add_filter( 'screen_settings', [ $this->current_view, 'display_screen_settings' ], 10, 2 ); } } } /** * Init PerfOps admin menus. * * @param array $perfops The already declared menus. * @return array The completed menus array. * @since 1.0.0 */ public function init_perfopsone_admin_menus( $perfops ) { if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { $perfops['settings'][] = [ 'name' => DECALOG_PRODUCT_NAME, 'description' => '', 'icon_callback' => [ \Decalog\Plugin\Core::class, 'get_base64_logo' ], 'slug' => 'decalog-settings', /* translators: as in the sentence "DecaLog Settings" or "WordPress Settings" */ 'page_title' => sprintf( decalog_esc_html__( '%s Settings', 'decalog' ), DECALOG_PRODUCT_NAME ), 'menu_title' => DECALOG_PRODUCT_NAME, 'capability' => 'manage_options', 'callback' => [ $this, 'get_settings_page' ], 'plugin' => DECALOG_SLUG, 'version' => DECALOG_VERSION, 'activated' => true, 'remedy' => '', 'statistics' => [ '\Decalog\System\Statistics', 'sc_get_raw' ], 'post_callback' => [ $this, 'set_settings_help' ], ]; } if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() || Role::LOCAL_ADMIN === Role::admin_type() || Role::override_privileges() ) { if ( Events::loggers_count() > 0 ) { $perfops['records'][] = [ 'name' => decalog_esc_html__( 'Events Log', 'decalog' ), /* translators: as in the sentence "Check the events that occurred on your network." or "Check the events that occurred on your website." */ 'description' => sprintf( decalog_esc_html__( 'Check the events that occurred on your %s.', 'decalog' ), Environment::is_wordpress_multisite() ? decalog_esc_html__( 'network', 'decalog' ) : decalog_esc_html__( 'website', 'decalog' ) ), 'icon_callback' => [ \Decalog\Plugin\Core::class, 'get_base64_logo' ], 'slug' => 'decalog-viewer', /* translators: as in the sentence "DecaLog Events Viewer" */ 'page_title' => sprintf( decalog_esc_html__( '%s Events Viewer', 'decalog' ), DECALOG_PRODUCT_NAME ), 'menu_title' => decalog_esc_html__( 'Events Log', 'decalog' ), 'capability' => 'read_private_pages', 'callback' => [ $this, 'get_events_page' ], 'plugin' => DECALOG_SLUG, 'activated' => true, 'remedy' => '', 'post_callback' => [ $this, 'set_viewer_help' ], ]; } if ( Traces::loggers_count() > 0 ) { $perfops['records'][] = [ 'name' => decalog_esc_html__( 'Traces', 'decalog' ), /* translators: as in the sentence "Check the traces that are recorded on your network." or "Check the traces that are recorded on your website." */ 'description' => sprintf( decalog_esc_html__( 'Check the traces that are recorded on your %s.', 'decalog' ), Environment::is_wordpress_multisite() ? decalog_esc_html__( 'network', 'decalog' ) : decalog_esc_html__( 'website', 'decalog' ) ), 'icon_callback' => [ \Decalog\Plugin\Core::class, 'get_base64_logo' ], 'slug' => 'decalog-tviewer', /* translators: as in the sentence "DecaLog Traces Viewer" */ 'page_title' => sprintf( decalog_esc_html__( '%s Traces Viewer', 'decalog' ), DECALOG_PRODUCT_NAME ), 'menu_title' => decalog_esc_html__( 'Traces', 'decalog' ), 'capability' => 'read_private_pages', 'callback' => [ $this, 'get_traces_page' ], 'plugin' => DECALOG_SLUG, 'activated' => true, 'remedy' => '', 'post_callback' => [ $this, 'set_viewer_help' ], ]; } } if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() || Role::override_privileges() ) { $perfops['consoles'][] = [ 'name' => decalog_esc_html__( 'Live Events', 'decalog' ), /* translators: as in the sentence "Check the events that occurred on your network." or "Check the events that occurred on your website." */ 'description' => sprintf( decalog_esc_html__( 'Displays events as soon as they occur on your %s.', 'decalog' ), Environment::is_wordpress_multisite() ? decalog_esc_html__( 'network', 'decalog' ) : decalog_esc_html__( 'website', 'decalog' ) ), 'icon_callback' => [ \Decalog\Plugin\Core::class, 'get_base64_logo' ], 'slug' => 'decalog-console', /* translators: as in the sentence "DecaLog Viewer" */ 'page_title' => sprintf( decalog_esc_html__( '%s Live Events', 'decalog' ), DECALOG_PRODUCT_NAME ), 'menu_title' => decalog_esc_html__( 'Live Events', 'decalog' ), 'capability' => 'read_private_pages', 'callback' => [ $this, 'get_console_page' ], 'plugin' => DECALOG_SLUG, 'activated' => SharedMemory::$available, 'remedy' => esc_url( admin_url( 'admin.php?page=decalog-settings&tab=misc' ) ), ]; } return $perfops; } /** * Init PerfOps admin bar. * * @param array $perfops The already declared items. * @return array The completed items array. * @since 3.2.0 */ public function init_perfopsone_admin_bar( $perfops ) { if ( ! ( $action = filter_input( INPUT_GET, 'action' ) ) ) { $action = filter_input( INPUT_POST, 'action' ); } if ( ! ( $tab = filter_input( INPUT_GET, 'tab' ) ) ) { $tab = filter_input( INPUT_POST, 'tab' ); } $early_signal = ( 'misc' === $tab && 'do-save' === $action ) && ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ); $early_signal &= ( ! empty( $_POST ) && array_key_exists( 'submit', $_POST ) ); $early_signal &= ( array_key_exists( '_wpnonce', $_POST ) && wp_verify_nonce( $_POST['_wpnonce'], 'decalog-plugin-options' ) ); if ( $early_signal ) { Option::network_set( 'adminbar', array_key_exists( 'decalog_plugin_options_adminbar', $_POST ) ); } if ( Option::network_get( 'adminbar' ) && ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() || Role::LOCAL_ADMIN === Role::admin_type() || Role::override_privileges() ) ) { if ( Events::loggers_count() > 0 ) { $perfops[] = [ 'id' => 'decalog-events-viewer', 'title' => '' .decalog__( 'Logs', 'decalog' ) . '  ➜  ' .decalog__( 'View Events', 'decalog' ), 'href' => esc_url( admin_url( 'admin.php?page=decalog-viewer' . ( Environment::is_wordpress_multisite() ? '&site_id=' . Blog::get_current_blog_id() : '' ) ) ), 'meta' => false, ]; } if ( Traces::loggers_count() > 0 ) { $perfops[] = [ 'id' => 'decalog-traces-viewer', 'title' => '' .decalog__( 'Logs', 'decalog' ) . '  ➜  ' .decalog__( 'View Traces', 'decalog' ), 'href' => esc_url( admin_url( 'admin.php?page=decalog-tviewer' . ( Environment::is_wordpress_multisite() ? '&site_id=' . Blog::get_current_blog_id() : '' ) ) ), 'meta' => false, ]; } } return $perfops; } /** * Dispatch the items in the settings menu. * * @since 2.0.0 */ public function finalize_admin_menus() { Menus::finalize(); } /** * Removes unneeded items from the settings menu. * * @since 2.0.0 */ public function normalize_admin_menus() { Menus::normalize(); } /** * Set the items in the settings menu. * * @since 1.0.0 */ public function init_admin_menus() { if ( 'decalog-settings' === filter_input( INPUT_GET, 'page', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ) { remove_action( 'admin_print_scripts', 'print_emoji_detection_script' ); remove_action( 'admin_print_styles', 'print_emoji_styles' ); } add_filter( 'init_perfopsone_admin_menus', [ $this, 'init_perfopsone_admin_menus' ] ); add_filter( 'init_perfopsone_admin_bar', [ $this, 'init_perfopsone_admin_bar' ] ); Menus::initialize(); AdminBar::initialize(); } /** * Get actions links for myblogs_blog_actions hook. * * @param string $actions The HTML site link markup. * @param object $user_blog An object containing the site data. * @return string The action string. * @since 1.2.0 */ public function blog_action( $actions, $user_blog ) { if ( Role::override_privileges() || Role::SUPER_ADMIN === Role::admin_type() || Role::LOCAL_ADMIN === Role::admin_type() ) { if ( Events::loggers_count() > 0 ) { $actions .= " | userblog_id ) ) . "'>" .decalog__( 'Events', 'decalog' ) . ''; } if ( Traces::loggers_count() > 0 ) { $actions .= " | userblog_id ) ) . "'>" .decalog__( 'Traces', 'decalog' ) . ''; } } return $actions; } /** * Get actions for manage_sites_action_links hook. * * @param string[] $actions An array of action links to be displayed. * @param int $blog_id The site ID. * @param string $blogname Site path, formatted depending on whether it is a sub-domain * or subdirectory multisite installation. * @return array The actions. * @since 1.2.0 */ public function site_action( $actions, $blog_id, $blogname ) { if ( Role::override_privileges() || Role::SUPER_ADMIN === Role::admin_type() || Role::LOCAL_ADMIN === Role::admin_type() ) { if ( Events::loggers_count() > 0 ) { $actions['events_log'] = "" .decalog__( 'Events', 'decalog' ) . ''; } if ( Traces::loggers_count() > 0 ) { $actions['traces_log'] = "" .decalog__( 'traces', 'decalog' ) . ''; } } return $actions; } /** * Initializes settings sections. * * @since 1.0.0 */ public function init_settings_sections() { add_settings_section( 'decalog_loggers_options_section', decalog_esc_html__( 'Loggers options', 'decalog' ), [ $this, 'loggers_options_section_callback' ], 'decalog_loggers_options_section' ); add_settings_section( 'decalog_plugin_features_section', decalog_esc_html__( 'Plugin features', 'decalog' ), [ $this, 'plugin_features_section_callback' ], 'decalog_plugin_features_section' ); add_settings_section( 'decalog_plugin_options_section', decalog_esc_html__( 'Plugin options', 'decalog' ), [ $this, 'plugin_options_section_callback' ], 'decalog_plugin_options_section' ); add_settings_section( 'decalog_listeners_options_section', null, [ $this, 'listeners_options_section_callback' ], 'decalog_listeners_options_section' ); add_settings_section( 'decalog_listeners_settings_section', null, [ $this, 'listeners_settings_section_callback' ], 'decalog_listeners_settings_section' ); add_settings_section( 'decalog_logger_misc_section', null, [ $this, 'logger_misc_section_callback' ], 'decalog_logger_misc_section' ); add_settings_section( 'decalog_logger_delete_section', null, [ $this, 'logger_delete_section_callback' ], 'decalog_logger_delete_section' ); add_settings_section( 'decalog_logger_specific_section', null, [ $this, 'logger_specific_section_callback' ], 'decalog_logger_specific_section' ); add_settings_section( 'decalog_logger_privacy_section', decalog_esc_html__( 'Privacy options', 'decalog' ), [ $this, 'logger_privacy_section_callback' ], 'decalog_logger_privacy_section' ); add_settings_section( 'decalog_logger_details_section', decalog_esc_html__( 'Reported details', 'decalog' ), [ $this, 'logger_details_section_callback' ], 'decalog_logger_details_section' ); if ( apply_filters( 'perfopsone_show_advanced', false ) ) { add_settings_section( 'decalog_plugin_advanced_section', decalog_esc_html__( 'Plugin advanced options', 'decalog' ), [ $this, 'plugin_advanced_section_callback' ], 'decalog_plugin_advanced_section' ); } } /** * Add links in the "Actions" column on the plugins view page. * * @param string[] $actions An array of plugin action links. By default this can include 'activate', * 'deactivate', and 'delete'. * @param string $plugin_file Path to the plugin file relative to the plugins directory. * @param array $plugin_data An array of plugin data. See `get_plugin_data()`. * @param string $context The plugin context. By default this can include 'all', 'active', 'inactive', * 'recently_activated', 'upgrade', 'mustuse', 'dropins', and 'search'. * @return array Extended list of links to print in the "Actions" column on the Plugins page. * @since 1.0.0 */ public function add_actions_links( $actions, $plugin_file, $plugin_data, $context ) { $actions[] = sprintf( '%s', admin_url( 'admin.php?page=decalog-settings' ), decalog_esc_html__( 'Settings', 'decalog' ) ); if ( Events::loggers_count() > 0 ) { $actions[] = sprintf( '%s', admin_url( 'admin.php?page=decalog-viewer' ), decalog_esc_html__( 'Events', 'decalog' ) ); } if ( Traces::loggers_count() > 0 ) { $actions[] = sprintf( '%s', admin_url( 'admin.php?page=decalog-tviewer' ), decalog_esc_html__( 'Traces', 'decalog' ) ); } return $actions; } /** * Add links in the "Description" column on the Plugins page. * * @param array $links List of links to print in the "Description" column on the Plugins page. * @param string $file Path to the plugin file relative to the plugins directory. * @return array Extended list of links to print in the "Description" column on the Plugins page. * @since 1.3.0 */ public function add_row_meta( $links, $file ) { if ( 0 === strpos( $file, DECALOG_SLUG . '/' ) ) { $links[] = '' .decalog__( 'Support', 'decalog' ) . ''; } return $links; } /** * Get the content of the events page. * * @since 1.0.0 */ public function get_events_page() { if ( isset( $this->current_view ) ) { $this->current_view->get(); } else { include DECALOG_ADMIN_DIR . 'partials/decalog-admin-view-events.php'; } } /** * Get the content of the traces page. * * @since 1.0.0 */ public function get_traces_page() { if ( isset( $this->current_view ) ) { wp_enqueue_style( DECALOG_ASSETS_ID ); $this->current_view->get(); } else { include DECALOG_ADMIN_DIR . 'partials/decalog-admin-view-traces.php'; } } /** * Get the content of the console page. * * @since 1.0.0 */ public function get_console_page() { if ( isset( $this->current_view ) ) { $this->current_view->get(); } else { include DECALOG_ADMIN_DIR . 'partials/decalog-admin-view-console.php'; } } /** * Get the content of the settings page. * * @since 1.0.0 */ public function get_settings_page() { $this->current_handler = null; $this->current_logger = null; if ( ! ( $action = filter_input( INPUT_GET, 'action' ) ) ) { $action = filter_input( INPUT_POST, 'action' ); } if ( ! ( $tab = filter_input( INPUT_GET, 'tab' ) ) ) { $tab = filter_input( INPUT_POST, 'tab' ); } if ( ! ( $handler = filter_input( INPUT_GET, 'handler' ) ) ) { $handler = filter_input( INPUT_POST, 'handler' ); } if ( ! ( $uuid = filter_input( INPUT_GET, 'uuid' ) ) ) { $uuid = filter_input( INPUT_POST, 'uuid' ); } $nonce = filter_input( INPUT_GET, 'nonce' ); if ( $uuid ) { $loggers = Option::network_get( 'loggers' ); if ( array_key_exists( $uuid, $loggers ) ) { $this->current_logger = $loggers[ $uuid ]; $this->current_logger['uuid'] = $uuid; } } if ( $handler ) { $handlers = new HandlerTypes(); $this->current_handler = $handlers->get( $handler ); } elseif ( $this->current_logger ) { $handlers = new HandlerTypes(); $this->current_handler = $handlers->get( $this->current_logger['handler'] ); } if ( $this->current_handler && ! $this->current_logger ) { $this->creation_mode = true; $this->current_logger = [ 'uuid' => $uuid = UUID::generate_v4(), 'name' => decalog_esc_html__( 'New logger', 'decalog' ), 'handler' => $this->current_handler['id'], 'running' => Option::network_get( 'logger_autostart' ), 'level' => Logger::INFO, ]; } if ( $this->current_logger ) { $factory = new LoggerFactory(); $this->current_logger = $factory->check( $this->current_logger ); } $view = 'decalog-admin-settings-main'; if ( $action && $tab ) { switch ( $tab ) { case 'loggers': switch ( $action ) { case 'form-edit': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { $current_logger = $this->current_logger; $current_handler = $this->current_handler; $args = compact( 'current_logger', 'current_handler' ); $view = 'decalog-admin-settings-logger-edit'; } if ( 'system' === $current_handler['class'] ) { $view = 'decalog-admin-settings-main'; $message = decalog_esc_html__( 'You can not modify or remove a system logger.', 'decalog' ); $code = 403; $this->logger->error( $message, $code ); add_settings_error( 'decalog_error', $code, $message, 'error' ); } break; case 'form-delete': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { $current_logger = $this->current_logger; $current_handler = $this->current_handler; $args = compact( 'current_logger', 'current_handler' ); $view = 'decalog-admin-settings-logger-delete'; } if ( 'system' === $current_handler['class'] ) { $view = 'decalog-admin-settings-main'; $message = decalog_esc_html__( 'You can not modify or remove a system logger.', 'decalog' ); $code = 403; $this->logger->error( $message, $code ); add_settings_error( 'decalog_error', $code, $message, 'error' ); } break; case 'do-edit': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { $this->save_current(); } break; case 'do-delete': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { $this->delete_current(); } break; case 'start': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { if ( $nonce && $uuid && wp_verify_nonce( $nonce, 'decalog-logger-start-' . $uuid ) ) { $loggers = Option::network_get( 'loggers' ); if ( array_key_exists( $uuid, $loggers ) && 'system' !== $this->current_handler['class'] ) { $loggers[ $uuid ]['running'] = true; Option::network_set( 'loggers', $loggers ); $this->logger = Log::bootstrap( 'plugin', DECALOG_PRODUCT_SHORTNAME, DECALOG_VERSION ); $message = sprintf( decalog_esc_html__( 'Logger %s has started.', 'decalog' ), '' . $loggers[ $uuid ]['name'] . '' ); $code = 0; add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); $this->logger->info( sprintf( 'Logger "%s" has started.', $loggers[ $uuid ]['name'] ), $code ); } else { $message = decalog_esc_html__( 'You can not start or pause a system logger.', 'decalog' ); $code = 403; $this->logger->error( $message, $code ); add_settings_error( 'decalog_error', $code, $message, 'error' ); } } } break; case 'pause': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { if ( $nonce && $uuid && wp_verify_nonce( $nonce, 'decalog-logger-pause-' . $uuid ) ) { $loggers = Option::network_get( 'loggers' ); if ( array_key_exists( $uuid, $loggers ) && 'system' !== $this->current_handler['class'] ) { $message = sprintf( decalog_esc_html__( 'Logger %s has been paused.', 'decalog' ), '' . $loggers[ $uuid ]['name'] . '' ); $code = 0; $this->logger->notice( sprintf( 'Logger "%s" has been paused.', $loggers[ $uuid ]['name'] ), $code ); $loggers[ $uuid ]['running'] = false; Option::network_set( 'loggers', $loggers ); $this->logger = Log::bootstrap( 'plugin', DECALOG_PRODUCT_SHORTNAME, DECALOG_VERSION ); add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); } else { $message = decalog_esc_html__( 'You can not start or pause a system logger.', 'decalog' ); $code = 403; $this->logger->error( $message, $code ); add_settings_error( 'decalog_error', $code, $message, 'error' ); } } } case 'test': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { if ( $nonce && $uuid && wp_verify_nonce( $nonce, 'decalog-logger-test-' . $uuid ) ) { $loggers = Option::network_get( 'loggers' ); if ( array_key_exists( $uuid, $loggers ) ) { $test = Log::bootstrap( 'plugin', DECALOG_PRODUCT_SHORTNAME, DECALOG_VERSION, $uuid ); $done = true; foreach ( [ 'DEBUG', 'INFO', 'NOTICE', 'WARNING', 'ERROR', 'CRITICAL', 'ALERT', 'EMERGENCY' ] as $level ) { $done &= $test->log( $level, ucfirst( strtolower( $level ) ) . ' test message.', 210871 ); } if ( $done ) { $message = sprintf( decalog_esc_html__( 'Test messages have been sent to logger %s.', 'decalog' ), '' . $loggers[ $uuid ]['name'] . '' ); $code = 0; $this->logger->info( sprintf( 'Logger "%s" has been tested.', $loggers[ $uuid ]['name'] ), $code ); add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); } else { $message = sprintf( decalog_esc_html__( 'Test messages have not been sent to logger %s. Please check the logger\'s settings.', 'decalog' ), '' . $loggers[ $uuid ]['name'] . '' ); $code = 1; $this->logger->warning( sprintf( 'Logger "%s" has been unsuccessfully tested.', $loggers[ $uuid ]['name'] ), $code ); add_settings_error( 'decalog_error', $code, $message, 'error' ); } } } } } break; case 'misc': switch ( $action ) { case 'do-save': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { if ( ! empty( $_POST ) && array_key_exists( 'submit', $_POST ) ) { $this->save_options(); } elseif ( ! empty( $_POST ) && array_key_exists( 'reset-to-defaults', $_POST ) ) { $this->reset_options(); } } break; case 'install-podd': if ( class_exists( 'PerfOpsOne\Installer' ) && $nonce && wp_verify_nonce( $nonce, $action ) ) { $result = \PerfOpsOne\Installer::do( 'device-detector', true ); if ( '' === $result ) { add_settings_error( 'decalog_no_error', '', decalog_esc_html__( 'Plugin successfully installed and activated with default settings.', 'decalog' ), 'info' ); } else { add_settings_error( 'decalog_install_error', '', sprintf( decalog_esc_html__( 'Unable to install or activate the plugin. Error message: %s.', 'decalog' ), $result ), 'error' ); } } break; case 'install-iplocator': if ( class_exists( 'PerfOpsOne\Installer' ) && $nonce && wp_verify_nonce( $nonce, $action ) ) { $result = \PerfOpsOne\Installer::do( 'ip-locator', true ); if ( '' === $result ) { add_settings_error( 'decalog_no_error', '', decalog_esc_html__( 'Plugin successfully installed and activated with default settings.', 'decalog' ), 'info' ); } else { add_settings_error( 'decalog_install_error', '', sprintf( decalog_esc_html__( 'Unable to install or activate the plugin. Error message: %s.', 'decalog' ), $result ), 'error' ); } } break; } break; case 'listeners': switch ( $action ) { case 'do-save': if ( Role::SUPER_ADMIN === Role::admin_type() || Role::SINGLE_ADMIN === Role::admin_type() ) { if ( ! empty( $_POST ) && array_key_exists( 'submit', $_POST ) ) { $this->save_listeners(); } elseif ( ! empty( $_POST ) && array_key_exists( 'reset-to-defaults', $_POST ) ) { $this->reset_listeners(); } } break; } break; } } include DECALOG_ADMIN_DIR . 'partials/' . $view . '.php'; } /** * Save the listeners options. * * @since 1.0.0 */ private function save_listeners() { if ( ! empty( $_POST ) ) { if ( array_key_exists( '_wpnonce', $_POST ) && wp_verify_nonce( $_POST['_wpnonce'], 'decalog-listeners-options' ) ) { Option::network_set( 'autolisteners', 'auto' === filter_input( INPUT_POST, 'decalog_listeners_options_auto',FILTER_SANITIZE_FULL_SPECIAL_CHARS ) ); $list = []; $listeners = ListenerFactory::$infos; foreach ( $listeners as $listener ) { if ( array_key_exists( 'decalog_listeners_settings_' . $listener['id'], $_POST ) ) { $list[] = $listener['id']; } } Option::network_set( 'listeners', $list ); $message = decalog_esc_html__( 'Listeners settings have been saved.', 'decalog' ); $code = 0; add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); $this->logger->info( 'Listeners settings updated.', $code ); } else { $message = decalog_esc_html__( 'Listeners settings have not been saved. Please try again.', 'decalog' ); $code = 2; add_settings_error( 'decalog_nonce_error', $code, $message, 'error' ); $this->logger->warning( 'Listeners settings not updated.', $code ); } } } /** * Reset the listeners options. * * @since 1.0.0 */ private function reset_listeners() { if ( ! empty( $_POST ) ) { if ( array_key_exists( '_wpnonce', $_POST ) && wp_verify_nonce( $_POST['_wpnonce'], 'decalog-listeners-options' ) ) { Option::network_set( 'autolisteners', true ); $message = decalog_esc_html__( 'Listeners settings have been reset to defaults.', 'decalog' ); $code = 0; add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); $this->logger->info( 'Listeners settings reset to defaults.', $code ); } else { $message = decalog_esc_html__( 'Listeners settings have not been reset to defaults. Please try again.', 'decalog' ); $code = 2; add_settings_error( 'decalog_nonce_error', $code, $message, 'error' ); $this->logger->warning( 'Listeners settings not reset to defaults.', $code ); } } } /** * Save the plugin options. * * @since 1.0.0 */ private function save_options() { if ( ! empty( $_POST ) ) { if ( array_key_exists( '_wpnonce', $_POST ) && wp_verify_nonce( $_POST['_wpnonce'], 'decalog-plugin-options' ) ) { Option::network_set( 'use_cdn', array_key_exists( 'decalog_plugin_options_usecdn', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_options_usecdn' ) : false ); Option::network_set( 'display_nag', array_key_exists( 'decalog_plugin_options_nag', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_options_nag' ) : false ); Option::network_set( 'adminbar', array_key_exists( 'decalog_plugin_options_adminbar', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_options_adminbar' ) : false ); Option::network_set( 'download_favicons', array_key_exists( 'decalog_plugin_options_favicons', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_options_favicons' ) : false ); Option::network_set( 'earlyloading', array_key_exists( 'decalog_plugin_features_earlyloading', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_features_earlyloading' ) : false ); Option::network_set( 'logger_autostart', array_key_exists( 'decalog_loggers_options_autostart', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_loggers_options_autostart' ) : false ); Option::network_set( 'pseudonymization', array_key_exists( 'decalog_loggers_options_pseudonymization', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_loggers_options_pseudonymization' ) : false ); Option::network_set( 'respect_wp_debug', array_key_exists( 'decalog_loggers_options_wpdebug', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_loggers_options_wpdebug' ) : false ); Option::network_set( 'env_substitution', array_key_exists( 'decalog_loggers_options_env_substitution', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_loggers_options_env_substitution' ) : false ); Option::network_set( 'privileges', array_key_exists( 'decalog_plugin_options_privileges', $_POST ) ? (string) filter_input( INPUT_POST, 'decalog_plugin_options_privileges', FILTER_SANITIZE_NUMBER_INT ) : Option::network_get( 'privileges' ) ); Option::network_set( 'metrics_authent', array_key_exists( 'decalog_plugin_features_metrics_authent', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_features_metrics_authent' ) : false ); Option::network_set( 'slow_query_warn', array_key_exists( 'decalog_plugin_features_slowqueries', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_features_slowqueries' ) : false ); Option::network_set( 'unknown_metrics_warn', array_key_exists( 'decalog_plugin_features_unknownmetrics', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_features_unknownmetrics' ) : false ); Option::network_set( 'trace_query', array_key_exists( 'decalog_plugin_features_tracequeries', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_features_tracequeries' ) : false ); Option::network_set( 'unbuffered_cli', array_key_exists( 'decalog_plugin_advanced_unbuffered_cli', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_advanced_unbuffered_cli' ) : false ); Option::network_set( 'buffer_size', array_key_exists( 'decalog_plugin_advanced_buffer_size', $_POST ) ? (string) filter_input( INPUT_POST, 'decalog_plugin_advanced_buffer_size', FILTER_SANITIZE_NUMBER_INT ) : Option::network_get( 'buffer_size' ) ); $autolog = array_key_exists( 'decalog_plugin_features_livelog', $_POST ) ? (bool) filter_input( INPUT_POST, 'decalog_plugin_features_livelog' ) : false; if ( $autolog ) { Autolog::activate(); } else { Autolog::deactivate(); } $message = decalog_esc_html__( 'Plugin settings have been saved.', 'decalog' ); $code = 0; add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); $this->logger->info( 'Plugin settings updated.', $code ); } else { $message = decalog_esc_html__( 'Plugin settings have not been saved. Please try again.', 'decalog' ); $code = 2; add_settings_error( 'decalog_nonce_error', $code, $message, 'error' ); $this->logger->warning( 'Plugin settings not updated.', $code ); } } } /** * Reset the plugin options. * * @since 1.0.0 */ private function reset_options() { if ( ! empty( $_POST ) ) { if ( array_key_exists( '_wpnonce', $_POST ) && wp_verify_nonce( $_POST['_wpnonce'], 'decalog-plugin-options' ) ) { Option::reset_to_defaults(); $message = decalog_esc_html__( 'Plugin settings have been reset to defaults.', 'decalog' ); $code = 0; add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); $this->logger->info( 'Plugin settings reset to defaults.', $code ); } else { $message = decalog_esc_html__( 'Plugin settings have not been reset to defaults. Please try again.', 'decalog' ); $code = 2; add_settings_error( 'decalog_nonce_error', $code, $message, 'error' ); $this->logger->warning( 'Plugin settings not reset to defaults.', $code ); } } } /** * Save the current logger as new or modified logger. * * @since 1.0.0 */ private function save_current() { if ( ! empty( $_POST ) ) { if ( array_key_exists( '_wpnonce', $_POST ) && wp_verify_nonce( $_POST['_wpnonce'], 'decalog-logger-edit' ) ) { if ( array_key_exists( 'submit', $_POST ) ) { $this->current_logger['name'] = ( array_key_exists( 'decalog_logger_misc_name', $_POST ) ? filter_input( INPUT_POST, 'decalog_logger_misc_name', FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : $this->current_logger['name'] ); $this->current_logger['level'] = ( array_key_exists( 'decalog_logger_misc_level', $_POST ) ? filter_input( INPUT_POST, 'decalog_logger_misc_level', FILTER_SANITIZE_NUMBER_INT ) : $this->current_logger['level'] ); $this->current_logger['privacy']['obfuscation'] = ( array_key_exists( 'decalog_logger_privacy_ip', $_POST ) ? true : false ); $this->current_logger['privacy']['pseudonymization'] = ( array_key_exists( 'decalog_logger_privacy_name', $_POST ) ? true : false ); $this->current_logger['processors'] = []; $proc = new ProcessorTypes(); foreach ( array_reverse( $proc->get_all() ) as $processor ) { if ( array_key_exists( 'decalog_logger_details_' . strtolower( $processor['id'] ), $_POST ) ) { $this->current_logger['processors'][] = $processor['id']; } } foreach ( $this->current_handler['configuration'] as $key => $configuration ) { $id = 'decalog_logger_details_' . strtolower( $key ); if ( 'boolean' === $configuration['control']['cast'] ) { $this->current_logger['configuration'][ $key ] = ( array_key_exists( $id, $_POST ) ? true : false ); } if ( 'integer' === $configuration['control']['cast'] ) { $this->current_logger['configuration'][ $key ] = ( array_key_exists( $id, $_POST ) ? filter_input( INPUT_POST, $id, FILTER_SANITIZE_NUMBER_INT ) : $this->current_logger['configuration'][ $key ] ); } if ( 'string' === $configuration['control']['cast'] ) { $this->current_logger['configuration'][ $key ] = ( array_key_exists( $id, $_POST ) ? filter_input( INPUT_POST, $id, FILTER_SANITIZE_FULL_SPECIAL_CHARS ) : $this->current_logger['configuration'][ $key ] ); } if ( 'password' === $configuration['control']['cast'] ) { $this->current_logger['configuration'][ $key ] = ( array_key_exists( $id, $_POST ) ? filter_input( INPUT_POST, $id, FILTER_UNSAFE_RAW ) : $this->current_logger['configuration'][ $key ] ); } } $uuid = $this->current_logger['uuid']; $loggers = Option::network_get( 'loggers' ); $factory = new LoggerFactory(); $loggers[ $uuid ] = $factory->check( $this->current_logger, true ); if ( array_key_exists( 'uuid', $loggers[ $uuid ] ) ) { unset( $loggers[ $uuid ]['uuid'] ); } Option::network_set( 'loggers', $loggers ); $this->logger = Log::bootstrap( 'plugin', DECALOG_PRODUCT_SHORTNAME, DECALOG_VERSION ); $message = sprintf( decalog_esc_html__( 'Logger %s has been saved.', 'decalog' ), '' . $this->current_logger['name'] . '' ); $code = 0; add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); $this->logger->info( sprintf( 'Logger "%s" has been saved.', $this->current_logger['name'] ), $code ); } } else { $message = sprintf( decalog_esc_html__( 'Logger %s has not been saved. Please try again.', 'decalog' ), '' . $this->current_logger['name'] . '' ); $code = 2; add_settings_error( 'decalog_nonce_error', $code, $message, 'error' ); $this->logger->warning( sprintf( 'Logger "%s" has not been saved.', $this->current_logger['name'] ), $code ); } } } /** * Delete the current logger. * * @since 1.0.0 */ private function delete_current() { if ( ! empty( $_POST ) ) { if ( array_key_exists( '_wpnonce', $_POST ) && wp_verify_nonce( $_POST['_wpnonce'], 'decalog-logger-delete' ) ) { if ( array_key_exists( 'submit', $_POST ) ) { $uuid = $this->current_logger['uuid']; $loggers = Option::network_get( 'loggers' ); $factory = new LoggerFactory(); $factory->destroy( $this->current_logger ); unset( $loggers[ $uuid ] ); Option::network_set( 'loggers', $loggers ); $this->logger = Log::bootstrap( 'plugin', DECALOG_PRODUCT_SHORTNAME, DECALOG_VERSION ); $message = sprintf( decalog_esc_html__( 'Logger %s has been removed.', 'decalog' ), '' . $this->current_logger['name'] . '' ); $code = 0; add_settings_error( 'decalog_no_error', $code, $message, 'updated' ); $this->logger->notice( sprintf( 'Logger "%s" has been removed.', $this->current_logger['name'] ), $code ); } } else { $message = sprintf( decalog_esc_html__( 'Logger %s has not been removed. Please try again.', 'decalog' ), '' . $this->current_logger['name'] . '' ); $code = 2; add_settings_error( 'decalog_nonce_error', $code, $message, 'error' ); $this->logger->warning( sprintf( 'Logger "%s" has not been removed.', $this->current_logger['name'] ), $code ); } } } /** * Callback for listeners options section. * * @since 1.0.0 */ public function listeners_options_section_callback() { $form = new Form(); add_settings_field( 'decalog_listeners_options_auto', __( 'Activate', 'decalog' ), [ $form, 'echo_field_select' ], 'decalog_listeners_options_section', 'decalog_listeners_options_section', [ 'list' => [ 0 => [ 'manual', decalog_esc_html__( 'Selected listeners', 'decalog' ) ], 1 => [ 'auto', decalog_esc_html__( 'All available listeners (recommended)', 'decalog' ) ], ], 'id' => 'decalog_listeners_options_auto', 'value' => Option::network_get( 'autolisteners' ) ? 'auto' : 'manual', 'description' => decalog_esc_html__( 'Automatically or selectively choose which sources to listen.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_listeners_options_section', 'decalog_listeners_options_autostart' ); } /** * Callback for listeners settings section. * * @since 1.0.0 */ public function listeners_settings_section_callback() { $standard = []; $plugin = []; $theme = []; $library = []; $listeners = ListenerFactory::$infos; usort( $listeners, function( $a, $b ) { return strcmp( strtolower( $a['name'] ), strtolower( $b['name'] ) ); } ); foreach ( $listeners as $listener ) { if ( 'plugin' === $listener['class'] && $listener['available'] ) { $plugin[] = $listener; } elseif ( 'theme' === $listener['class'] && $listener['available'] ) { $theme[] = $listener; } elseif ( 'library' === $listener['class'] && $listener['available'] ) { $library[] = $listener; } elseif ( $listener['available'] ) { $standard[] = $listener; } } $main = [ decalog_esc_html__( 'Standard listeners', 'decalog' ) => $standard, decalog_esc_html__( 'Plugin listeners', 'decalog' ) => $plugin, decalog_esc_html__( 'Library listeners', 'decalog' ) => $library, decalog_esc_html__( 'Theme listeners', 'decalog' ) => $theme, ]; $form = new Form(); foreach ( $main as $name => $items ) { $title = true; foreach ( $items as $item ) { add_settings_field( 'decalog_listeners_settings_' . $item['id'], $title ? $name : null, [ $form, 'echo_field_checkbox' ], 'decalog_listeners_settings_section', 'decalog_listeners_settings_section', [ 'text' => sprintf( '%s (%s %s)', $item['name'], $item['product'], $item['version'] ), 'id' => 'decalog_listeners_settings_' . $item['id'], 'checked' => in_array( $item['id'], Option::network_get( 'listeners' ), true ), 'description' => null, 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_listeners_settings_section', 'decalog_listeners_settings_' . $item['id'] ); $title = false; } } } /** * Callback for loggers options section. * * @since 1.0.0 */ public function loggers_options_section_callback() { $form = new Form(); add_settings_field( 'decalog_loggers_options_autostart', __( 'New logger', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_loggers_options_section', 'decalog_loggers_options_section', [ 'text' => decalog_esc_html__( 'Auto-start', 'decalog' ), 'id' => 'decalog_loggers_options_autostart', 'checked' => Option::network_get( 'logger_autostart' ), 'description' => decalog_esc_html__( 'If checked, when a new logger is added it automatically starts.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_loggers_options_section', 'decalog_loggers_options_autostart' ); add_settings_field( 'decalog_loggers_options_pseudonymization', __( 'Events messages', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_loggers_options_section', 'decalog_loggers_options_section', [ 'text' => decalog_esc_html__( 'Respect privacy', 'decalog' ), 'id' => 'decalog_loggers_options_pseudonymization', 'checked' => Option::network_get( 'pseudonymization' ), 'description' => decalog_esc_html__( 'If checked, DecaLog will try to obfuscate personal information in events messages.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_loggers_options_section', 'decalog_loggers_options_pseudonymization' ); add_settings_field( 'decalog_loggers_options_wpdebug', __( 'Rules', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_loggers_options_section', 'decalog_loggers_options_section', [ 'text' => decalog_esc_html__( 'Respect WP_DEBUG', 'decalog' ), 'id' => 'decalog_loggers_options_wpdebug', 'checked' => Option::network_get( 'respect_wp_debug' ), 'description' => decalog_esc_html__( 'If checked, the value of WP_DEBUG will override each logger\'s settings for minimal level of logging.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_loggers_options_section', 'decalog_loggers_options_wpdebug' ); add_settings_field( 'decalog_loggers_options_env_substitution', __( 'Parameters', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_loggers_options_section', 'decalog_loggers_options_section', [ 'text' => decalog_esc_html__( 'Variables substitution', 'decalog' ), 'id' => 'decalog_loggers_options_env_substitution', 'checked' => Option::network_get( 'env_substitution' ), 'description' => decalog_esc_html__( 'If checked, DecaLog will replace strings between curly braces by the corresponding environment variables or PHP-defined constants.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_loggers_options_section', 'decalog_loggers_options_env_substitution' ); } /** * Get the available privileges overriding. * * @return array An array containing the privileges overriding. * @since 2.4.0 */ protected function get_privileges_array() { $result = []; $result[] = [ 0, decalog_esc_html__( 'Never override privileges', 'decalog' ) ]; $result[] = [ 1, decalog_esc_html__( 'Override privileges for development environments', 'decalog' ) ]; $result[] = [ 2, decalog_esc_html__( 'Override privileges for staging environments', 'decalog' ) ]; $result[] = [ 3, decalog_esc_html__( 'Override privileges for staging and development environments', 'decalog' ) ]; return $result; } /** * Callback for plugin options section. * * @since 1.0.0 */ public function plugin_options_section_callback() { $form = new Form(); if ( function_exists( 'wp_get_environment_type' ) ) { add_settings_field( 'decalog_plugin_options_privileges', decalog_esc_html__( 'Logs accesses', 'decalog' ), [ $form, 'echo_field_select' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'list' => $this->get_privileges_array(), 'id' => 'decalog_plugin_options_privileges', 'value' => Option::network_get( 'privileges' ), 'description' => decalog_esc_html__( 'Allows other users than administrators to access live console and local events logs depending of environments.', 'decalog' ) . '
' . decalog_esc_html__( 'Note: choosing something other than "Never override privileges" grants access to all users having "read_private_pages" capability and may have privacy and security implications.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_options_section', 'decalog_plugin_options_privileges' ); } add_settings_field( 'decalog_plugin_options_adminbar', __( 'Quick actions', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'text' => decalog_esc_html__( 'Display in admin bar', 'decalog' ), 'id' => 'decalog_plugin_options_adminbar', 'checked' => Option::network_get( 'adminbar' ), 'description' => decalog_esc_html__( 'If checked, DecaLog will display in admin bar the most important actions, if any.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_options_section', 'decalog_plugin_options_adminbar' ); add_settings_field( 'decalog_plugin_options_favicons', __( 'Favicons', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'text' => decalog_esc_html__( 'Download and display', 'decalog' ), 'id' => 'decalog_plugin_options_favicons', 'checked' => Option::network_get( 'download_favicons' ), 'description' => decalog_esc_html__( 'If checked, DecaLog will download favicons of websites to display them in reports.', 'decalog' ) . '
' . decalog_esc_html__( 'Note: This feature uses the (free) Google Favicon Service.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_options_section', 'decalog_plugin_options_favicons' ); if ( class_exists( 'PODeviceDetector\API\Device' ) ) { $help = ' '; $help .= sprintf( decalog_esc_html__('Your site is currently using %s.', 'decalog' ), 'Device Detector v' . PODD_VERSION .'' ); } else { $help = ' '; $help .= sprintf( decalog_esc_html__('Your site does not use any device detection mechanism. To handle user-agents and callers reporting in DecaLog, I recommend you to install the excellent (and free) %s. But it is not mandatory.', 'decalog' ), 'Device Detector' ); if ( class_exists( 'PerfOpsOne\Installer' ) && ! Environment::is_wordpress_multisite() ) { $help .= '
  ' . decalog_esc_html__('Install It Now', 'decalog' ) . ''; } } add_settings_field( 'decalog_plugin_options_podd', __( 'Device Detection', 'decalog' ), [ $form, 'echo_field_simple_text' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'text' => $help ] ); register_setting( 'decalog_plugin_options_section', 'decalog_plugin_options_podd' ); $geo_ip = new GeoIP(); if ( $geo_ip->is_installed() ) { $help = ' '; $help .= sprintf( decalog_esc_html__('Your site is currently using %s.', 'decalog' ), '' . $geo_ip->get_full_name() .'' ); } else { $help = ' '; $help .= sprintf( decalog_esc_html__('Your site does not use any IP geographic information plugin. To display callers geographical details in DecaLog, I recommend you to install the excellent (and free) %s. But it is not mandatory.', 'decalog' ), 'IP Locator' ); if ( class_exists( 'PerfOpsOne\Installer' ) && ! Environment::is_wordpress_multisite() ) { $help .= '
  ' . decalog_esc_html__('Install It Now', 'decalog' ) . ''; } } add_settings_field( 'decalog_plugin_options_geoip', __( 'IP information', 'decalog' ), [ $form, 'echo_field_simple_text' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'text' => $help ] ); register_setting( 'decalog_plugin_options_section', 'decalog_plugin_options_geoip' ); if ( SharedMemory::$available ) { $help = ' '; $help .= decalog_esc_html__('Shared memory is available on your server: you can use live console.', 'decalog' ); } else { $help = ' '; $help .= sprintf( decalog_esc_html__('Shared memory is not available on your server. To use live console you must activate %s PHP module.', 'decalog' ), 'shmop' ); } add_settings_field( 'decalog_plugin_options_shmop', __( 'Shared memory', 'decalog' ), [ $form, 'echo_field_simple_text' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'text' => $help ] ); if ( Cache::$apcu_available) { $help = ' '; $help .= decalog_esc_html__('APCu is available on your server: you can use high peformance storage mechanism.', 'decalog' ); } else { $help = ' '; $help .= sprintf( decalog_esc_html__('APCu is not available on your server. To use high peformance storage mechanism you must activate %s PHP module.', 'decalog' ), 'apcu' ); } add_settings_field( 'decalog_plugin_options_apcu', __( 'APCu storage', 'decalog' ), [ $form, 'echo_field_simple_text' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'text' => $help ] ); register_setting( 'decalog_plugin_options_section', 'decalog_plugin_options_shmop' ); add_settings_field( 'decalog_plugin_options_usecdn', __( 'Resources', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'text' => decalog_esc_html__( 'Use public CDN', 'decalog' ), 'id' => 'decalog_plugin_options_usecdn', 'checked' => Option::network_get( 'use_cdn' ), 'description' => decalog_esc_html__( 'Use CDN (jsDelivr) to serve DecaLog scripts and stylesheets.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_options_section', 'decalog_plugin_options_usecdn' ); add_settings_field( 'decalog_plugin_options_nag', __( 'Admin notices', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_plugin_options_section', 'decalog_plugin_options_section', [ 'text' => decalog_esc_html__( 'Display', 'decalog' ), 'id' => 'decalog_plugin_options_nag', 'checked' => Option::network_get( 'display_nag' ), 'description' => decalog_esc_html__( 'Allows DecaLog to display admin notices throughout the admin dashboard.', 'decalog' ) . '
' . decalog_esc_html__( 'Note: DecaLog respects DISABLE_NAG_NOTICES flag.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_options_section', 'decalog_plugin_options_nag' ); } /** * Callback for plugin features section. * * @since 1.0.0 */ public function plugin_features_section_callback() { $form = new Form(); add_settings_field( 'decalog_plugin_features_earlyloading', __( 'Initialization', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_plugin_features_section', 'decalog_plugin_features_section', [ 'text' => decalog_esc_html__( 'Activate early loading', 'decalog' ), 'id' => 'decalog_plugin_features_earlyloading', 'checked' => ( 3 === \decalog_get_psr_log_version() ) ? Option::network_get( 'earlyloading' ) : false, 'description' => ( 3 === \decalog_get_psr_log_version() ) ? decalog_esc_html__( 'If checked, DecaLog will be loaded before all other plugins (recommended).', 'decalog' ) : decalog_esc_html__( 'Option not available.', 'decalog' ) . ' ' . sprintf( decalog_esc_html__( '%s is running in PSR-3 v1 compatibility mode due to an obsolete or outdated third-party plugin or theme.', 'decalog' ), DECALOG_PRODUCT_NAME ) . '
' . sprintf(decalog__( 'Please, do not hesitate to take part in discussion.', 'decalog' ), 'https://github.com/Pierre-Lannoy/wp-decalog/discussions/63' ), 'full_width' => false, 'enabled' => ( 3 === \decalog_get_psr_log_version() ), ] ); register_setting( 'decalog_plugin_features_section', 'decalog_plugin_features_earlyloading' ); if ( SharedMemory::$available ) { add_settings_field( 'decalog_plugin_features_livelog', __( 'Live console', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_plugin_features_section', 'decalog_plugin_features_section', [ 'text' => decalog_esc_html__( 'Activate auto-logging', 'decalog' ), 'id' => 'decalog_plugin_features_livelog', 'checked' => Autolog::is_enabled(), 'description' => decalog_esc_html__( 'If checked, DecaLog will silently start the features needed by live console.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_features_section', 'decalog_plugin_features_livelog' ); } add_settings_field( 'decalog_plugin_features_slowqueries', __( 'Database', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_plugin_features_section', 'decalog_plugin_features_section', [ 'text' => decalog_esc_html__( 'Warn about slow queries', 'decalog' ), 'id' => 'decalog_plugin_features_slowqueries', 'checked' => Option::network_get( 'slow_query_warn' ), 'description' => sprintf( decalog_esc_html__( 'If checked, a warning will be triggered for each SQL query that takes %.1F ms or more to execute.', 'decalog' ), Option::network_get( 'slow_query_ms', 50 ) ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_features_section', 'decalog_plugin_features_slowqueries' ); add_settings_field( 'decalog_plugin_features_tracequeries', '', [ $form, 'echo_field_checkbox' ], 'decalog_plugin_features_section', 'decalog_plugin_features_section', [ 'text' => decalog_esc_html__( 'Trace queries', 'decalog' ), 'id' => 'decalog_plugin_features_tracequeries', 'checked' => Option::network_get( 'trace_query' ), 'description' => decalog_esc_html__( 'If checked, all SQL queries will be traced and pushed to running trace loggers.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_features_section', 'decalog_plugin_features_tracequeries' ); add_settings_field( 'decalog_plugin_features_unknownmetrics', __( 'Metrics', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_plugin_features_section', 'decalog_plugin_features_section', [ 'text' => decalog_esc_html__( 'Warn about non-existent metrics', 'decalog' ), 'id' => 'decalog_plugin_features_unknownmetrics', 'checked' => Option::network_get( 'unknown_metrics_warn' ), 'description' => decalog_esc_html__( 'If checked, an error will be triggered when a process try to set a value for a metric which is not defined.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_features_section', 'decalog_plugin_features_unknownmetrics' ); add_settings_field( 'decalog_plugin_features_metrics_authent', '', [ $form, 'echo_field_checkbox' ], 'decalog_plugin_features_section', 'decalog_plugin_features_section', [ 'text' => decalog_esc_html__( 'Authenticated endpoint', 'decalog' ), 'id' => 'decalog_plugin_features_metrics_authent', 'checked' => Option::network_get( 'metrics_authent' ), 'description' => sprintf( decalog_esc_html__( 'If checked, DecaLog will require authentication to serve %s calls.', 'decalog' ), '' . htmlentities( '/wp-json/' . DECALOG_REST_NAMESPACE . '/metrics' ) . '' ) . '
' . sprintf( decalog_esc_html__( 'Note: if you activate authentication, you must generate an application password for a user having %s capability.', 'decalog' ), 'read_private_pages' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_features_section', 'decalog_plugin_features_metrics_authent' ); } /** * Callback for plugin advanced section. * * @since 4.3.0 */ public function plugin_advanced_section_callback() { $form = new Form(); add_settings_field( 'decalog_plugin_advanced_unbuffered_cli', 'Force unbuffering', [ $form, 'echo_field_checkbox' ], 'decalog_plugin_advanced_section', 'decalog_plugin_advanced_section', [ 'text' => 'WP CLI events', 'id' => 'decalog_plugin_advanced_unbuffered_cli', 'checked' => Option::network_get( 'unbuffered_cli' ), 'description' => 'If checked, events triggered via WP CLI will not be buffered.', 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_advanced_section', 'decalog_plugin_advanced_unbuffered_cli' ); add_settings_field( 'decalog_plugin_advanced_buffer_size', 'Events buffer size', [ $form, 'echo_field_input_integer' ], 'decalog_plugin_advanced_section', 'decalog_plugin_advanced_section', [ 'id' => 'decalog_plugin_advanced_buffer_size', 'value' => Option::network_get( 'buffer_size' ), 'min' => 10, 'max' => 2000, 'step' => 10, 'description' => 'Buffer size, in number of events.', 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_plugin_advanced_section', 'decalog_plugin_advanced_buffer_size' ); } /** * Callback for logger misc section. * * @since 1.0.0 */ public function logger_misc_section_callback() { $icon = ''; $title = $this->current_handler['name']; echo '

' . $icon . ' ' . $title . '

'; echo '

' . $this->current_handler['help'] . '

'; $form = new Form(); add_settings_field( 'decalog_logger_misc_name', __( 'Name', 'decalog' ), [ $form, 'echo_field_input_text' ], 'decalog_logger_misc_section', 'decalog_logger_misc_section', [ 'id' => 'decalog_logger_misc_name', 'value' => $this->current_logger['name'], 'description' => decalog_esc_html__( 'Used only in admin dashboard.', 'decalog' ), 'full_width' => false, 'placeholder' => '', 'enabled' => true, ] ); register_setting( 'decalog_logger_misc_section', 'decalog_logger_misc_name' ); if ( in_array( $this->current_handler['class'], [ 'alerting', 'logging', 'debugging', 'analytics' ], true ) ) { add_settings_field( 'decalog_logger_misc_level', __( 'Minimal level', 'decalog' ), [ $form, 'echo_field_select' ], 'decalog_logger_misc_section', 'decalog_logger_misc_section', [ 'list' => Log::get_levels( $this->current_handler['minimal'], true ), 'id' => 'decalog_logger_misc_level', 'value' => $this->current_logger['level'], 'description' => decalog_esc_html__( 'Minimal reported level. May be overridden by the "respect WP_DEBUG directive" option.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_logger_misc_section', 'decalog_logger_misc_level' ); } } /** * Callback for logger delete section. * * @since 1.0.0 */ public function logger_delete_section_callback() { $icon = ''; $title = $this->current_handler['name']; echo '

' . $icon . ' ' . $title . '

'; echo '

' . $this->current_handler['help'] . '

'; $form = new Form(); add_settings_field( 'decalog_logger_delete_name', __( 'Name', 'decalog' ), [ $form, 'echo_field_input_text' ], 'decalog_logger_delete_section', 'decalog_logger_delete_section', [ 'id' => 'decalog_logger_delete_name', 'value' => $this->current_logger['name'], 'description' => null, 'full_width' => false, 'placeholder' => '', 'enabled' => false, ] ); register_setting( 'decalog_logger_delete_section', 'decalog_logger_delete_name' ); if ( in_array( $this->current_handler['class'], [ 'alerting', 'logging', 'debugging', 'analytics' ], true ) ) { add_settings_field( 'decalog_logger_delete_level', __( 'Minimal level', 'decalog' ), [ $form, 'echo_field_select' ], 'decalog_logger_delete_section', 'decalog_logger_delete_section', [ 'list' => Log::get_levels( $this->current_handler['minimal'] ), 'id' => 'decalog_logger_delete_level', 'value' => $this->current_logger['level'], 'description' => null, 'full_width' => false, 'enabled' => false, ] ); register_setting( 'decalog_logger_delete_section', 'decalog_logger_delete_level' ); } } /** * Callback for logger specific section. * * @since 1.0.0 */ public function logger_specific_section_callback() { $form = new Form(); if ( 'ErrorLogHandler' === $this->current_logger['handler'] ) { add_settings_field( 'decalog_logger_specific_dummy', __( 'Log file', 'decalog' ), [ $form, 'echo_field_input_text' ], 'decalog_logger_specific_section', 'decalog_logger_specific_section', [ 'id' => 'decalog_logger_specific_dummy', 'value' => ini_get( 'error_log' ), 'description' => decalog_esc_html__( 'Value set in php.ini file.', 'decalog' ), 'full_width' => false, 'placeholder' => '', 'enabled' => false, ] ); register_setting( 'decalog_logger_specific_section', 'decalog_logger_specific_dummy' ); } foreach ( $this->current_handler['configuration'] as $key => $configuration ) { if ( ! $configuration['show'] ) { continue; } $id = 'decalog_logger_details_' . strtolower( $key ); $args = [ 'id' => $id, 'text' => decalog_esc_html__( 'Enabled', 'decalog' ), 'checked' => (bool) $this->current_logger['configuration'][ $key ], 'value' => $this->current_logger['configuration'][ $key ], 'description' => $configuration['help'], 'full_width' => false, 'enabled' => $configuration['control']['enabled'], 'placeholder' => $configuration['default'], 'list' => ( array_key_exists( 'list', $configuration['control'] ) ? $configuration['control']['list'] : [] ), ]; foreach ( $configuration['control'] as $index => $control ) { if ( 'type' !== $index && 'cast' !== $index ) { if ( 0 === strpos( $key, 'constant-' ) && ! $this->creation_mode && 'enabled' === $index ) { $control = false; } $args[ $index ] = $control; } } add_settings_field( $id, $configuration['name'], [ $form, 'echo_' . $configuration['control']['type'] ], 'decalog_logger_specific_section', 'decalog_logger_specific_section', $args ); register_setting( 'decalog_logger_specific_section', $id ); } } /** * Callback for logger privacy section. * * @since 1.0.0 */ public function logger_privacy_section_callback() { $form = new Form(); add_settings_field( 'decalog_logger_privacy_ip', __( 'Remote IPs', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_logger_privacy_section', 'decalog_logger_privacy_section', [ 'text' => decalog_esc_html__( 'Obfuscation', 'decalog' ), 'id' => 'decalog_logger_privacy_ip', 'checked' => $this->current_logger['privacy']['obfuscation'], 'description' => decalog_esc_html__( 'If checked, logged details will contain hashes instead of real IPs.', 'decalog' ) . '
' . decalog_esc_html__( 'Note: it concerns everything except events messages.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_logger_privacy_section', 'decalog_logger_privacy_ip' ); add_settings_field( 'decalog_logger_privacy_name', __( 'Users', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_logger_privacy_section', 'decalog_logger_privacy_section', [ 'text' => decalog_esc_html__( 'Pseudonymisation', 'decalog' ), 'id' => 'decalog_logger_privacy_name', 'checked' => $this->current_logger['privacy']['pseudonymization'], 'description' => decalog_esc_html__( 'If checked, logged details will contain hashes instead of user IDs & names.', 'decalog' ) . '
' . decalog_esc_html__( 'Note: it concerns everything except events messages.', 'decalog' ), 'full_width' => false, 'enabled' => true, ] ); register_setting( 'decalog_logger_privacy_section', 'decalog_logger_privacy_name' ); } /** * Callback for logger privacy section. * * @since 1.0.0 */ public function logger_details_section_callback() { $form = new Form(); $id = 'decalog_logger_details_dummy'; add_settings_field( $id, __( 'Standard', 'decalog' ), [ $form, 'echo_field_checkbox' ], 'decalog_logger_details_section', 'decalog_logger_details_section', [ 'text' => decalog_esc_html__( 'Included', 'decalog' ), 'id' => $id, 'checked' => true, 'description' => decalog_esc_html__( 'Allows to log standard DecaLog information.', 'decalog' ), 'full_width' => false, 'enabled' => false, ] ); register_setting( 'decalog_logger_details_section', $id ); $proc = new ProcessorTypes( 'tracing' === $this->current_handler['class'] ); foreach ( array_reverse( $proc->get_all() ) as $processor ) { $id = 'decalog_logger_details_' . strtolower( $processor['id'] ); add_settings_field( $id, $processor['name'], [ $form, 'echo_field_checkbox' ], 'decalog_logger_details_section', 'decalog_logger_details_section', [ 'text' => decalog_esc_html__( 'Included', 'decalog' ), 'id' => $id, 'checked' => in_array( $processor['id'], $this->current_logger['processors'], true ), 'description' => $processor['help'], 'full_width' => false, 'enabled' => ! in_array( $processor['id'], array_merge( $this->current_handler['processors']['excluded'] ?? [], $this->current_handler['processors']['included'] ?? [] ), true ), ] ); register_setting( 'decalog_logger_details_section', $id ); } } }