Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
| Total | |
20.91% |
23 / 110 |
|
0.00% |
0 / 6 |
CRAP | |
0.00% |
0 / 1 |
| Logs_Page | |
20.91% |
23 / 110 |
|
0.00% |
0 / 6 |
360.45 | |
0.00% |
0 / 1 |
| __construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
| set_page_title | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
20 | |||
| add_page | |
0.00% |
0 / 17 |
|
0.00% |
0 / 1 |
30 | |||
| display_page | |
0.00% |
0 / 61 |
|
0.00% |
0 / 1 |
110 | |||
| enqueue_scripts | |
93.33% |
14 / 15 |
|
0.00% |
0 / 1 |
3.00 | |||
| enqueue_styles | |
90.00% |
9 / 10 |
|
0.00% |
0 / 1 |
3.01 | |||
| 1 | <?php |
| 2 | /** |
| 3 | * The UI around the logs table. |
| 4 | * |
| 5 | * E.g. /wp-admin/admin.php?page=bh-wp-logger-development-plugin-logs. |
| 6 | * |
| 7 | * TODO: Add "send to plugin developer" button. |
| 8 | * TODO: Add copy to clipboard button. |
| 9 | * |
| 10 | * @package brianhenryie/bh-wp-logger |
| 11 | */ |
| 12 | |
| 13 | namespace BrianHenryIE\WP_Logger\Admin; |
| 14 | |
| 15 | use BrianHenryIE\WP_Logger\API\BH_WP_PSR_Logger; |
| 16 | use BrianHenryIE\WP_Logger\API_Interface; |
| 17 | use BrianHenryIE\WP_Logger\Logger_Settings_Interface; |
| 18 | use BrianHenryIE\WP_Logger\WP_Includes\Plugin_Logger_Actions; |
| 19 | use Psr\Log\LoggerAwareTrait; |
| 20 | use Psr\Log\LogLevel; |
| 21 | use Psr\Log\NullLogger; |
| 22 | |
| 23 | /** |
| 24 | * Functions for registering a "hidden menu" item, to add the wp-admin page to display the logs. |
| 25 | * |
| 26 | * @see Plugin_Logger_Actions::add_admin_ui_logs_page_hooks() |
| 27 | * |
| 28 | * @uses Logger_Settings_Interface::get_plugin_slug() |
| 29 | */ |
| 30 | class Logs_Page { |
| 31 | use LoggerAwareTrait; |
| 32 | |
| 33 | /** |
| 34 | * Logs_Page constructor. |
| 35 | * |
| 36 | * @param API_Interface $api The main functions of the logger. Used to get the list of log files. Needed to instantiate the table. |
| 37 | * @param Logger_Settings_Interface $settings The configuration used to set up the logger. The logger settings. i.e. what is the plugin slug this logger is for. |
| 38 | * @param ?BH_WP_PSR_Logger $logger The logger itself, for logging. |
| 39 | */ |
| 40 | public function __construct( |
| 41 | protected API_Interface $api, |
| 42 | protected Logger_Settings_Interface $settings, |
| 43 | ?BH_WP_PSR_Logger $logger = null |
| 44 | ) { |
| 45 | $this->setLogger( $logger ?? new NullLogger() ); |
| 46 | } |
| 47 | |
| 48 | /** |
| 49 | * Set the `global $title` before it is used to avoid a null error. |
| 50 | * |
| 51 | * `Deprecated: strip_tags(): Passing null to parameter #1 ($string) of type string is deprecated in /.../wp-admin/admin-header.php on line 41` |
| 52 | * |
| 53 | * @hooked admin_init |
| 54 | */ |
| 55 | public function set_page_title(): void { |
| 56 | /** |
| 57 | * Data is never written. |
| 58 | * phpcs:disable WordPress.Security.NonceVerification.Recommended |
| 59 | */ |
| 60 | if ( |
| 61 | ! isset( $_REQUEST['page'] ) |
| 62 | || ! is_string( $_REQUEST['page'] ) |
| 63 | || $this->settings->get_plugin_slug() . '-logs' !== sanitize_key( wp_unslash( $_REQUEST['page'] ) ) |
| 64 | ) { |
| 65 | return; |
| 66 | } |
| 67 | |
| 68 | /** |
| 69 | * @see strip_tags() |
| 70 | * |
| 71 | * phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited |
| 72 | */ |
| 73 | global $title; |
| 74 | $title = $this->settings->get_plugin_name() . ' Logs page'; |
| 75 | } |
| 76 | |
| 77 | /** |
| 78 | * Add a WordPress admin UI page, but without any menu linking to it. |
| 79 | * |
| 80 | * @hooked admin_menu |
| 81 | * |
| 82 | * @see wp-admin/menu.php |
| 83 | */ |
| 84 | public function add_page(): void { |
| 85 | |
| 86 | $logs_slug = "{$this->settings->get_plugin_slug()}-logs"; |
| 87 | $menu_title = 'Logs'; |
| 88 | |
| 89 | $parent_slug = ''; |
| 90 | |
| 91 | /** @var array<int,string[]> $menu */ |
| 92 | global $menu; |
| 93 | foreach ( $menu as $menu_item ) { |
| 94 | if ( stristr( $menu_item[0], 'logs' ) || stristr( $menu_item[2], 'logs' ) || stristr( $menu_item[3], 'logs' ) ) { |
| 95 | $parent_slug = $menu_item[2]; |
| 96 | $menu_title = $this->settings->get_plugin_name(); |
| 97 | break; |
| 98 | } |
| 99 | } |
| 100 | |
| 101 | add_submenu_page( |
| 102 | $parent_slug, |
| 103 | __( 'Logs', 'bh-wp-logger' ), |
| 104 | $menu_title, |
| 105 | 'manage_options', |
| 106 | $logs_slug, |
| 107 | array( $this, 'display_page' ) |
| 108 | ); |
| 109 | } |
| 110 | |
| 111 | /** |
| 112 | * Display the page. |
| 113 | * Record the last visited time. |
| 114 | * |
| 115 | * Registered above. |
| 116 | * |
| 117 | * @see add_page() |
| 118 | */ |
| 119 | public function display_page(): void { |
| 120 | |
| 121 | echo '<div class="wrap">'; |
| 122 | |
| 123 | echo '<h1>'; |
| 124 | echo esc_html( $this->settings->get_plugin_name() ); |
| 125 | echo '</h1>'; |
| 126 | |
| 127 | $log_files = $this->api->get_log_files(); |
| 128 | |
| 129 | if ( empty( $log_files ) ) { |
| 130 | // This will occur e.g. immediately after deleting all logs. |
| 131 | echo '<p>No logs to display.</p>'; |
| 132 | echo '</div>'; |
| 133 | return; |
| 134 | } |
| 135 | |
| 136 | $logs_table = new Logs_List_Table( $this->api, $this->settings, $this->logger ); |
| 137 | |
| 138 | // Set date here? |
| 139 | |
| 140 | // Show a list of date to switch between dates. |
| 141 | echo '<label for="log_date">Log date:</label>'; |
| 142 | echo '<select name="log_date" id="log_date">'; |
| 143 | |
| 144 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended |
| 145 | $chosen_date = isset( $_GET['log_date'] ) && is_string( $_GET['log_date'] ) ? sanitize_key( $_GET['log_date'] ) : array_key_last( $log_files ); |
| 146 | |
| 147 | // Maybe should use set file? |
| 148 | $logs_table->set_date( $chosen_date ); |
| 149 | |
| 150 | // TODO: Allow filtering here to add external log files, e.g. from Authorize.net SDK. |
| 151 | foreach ( $log_files as $date => $path ) { |
| 152 | $date_formatted = $date; |
| 153 | echo '<option value="' . esc_attr( $date ) . '"'; |
| 154 | if ( $date === $chosen_date ) { |
| 155 | echo ' selected'; |
| 156 | } |
| 157 | echo '>' . esc_html( $date_formatted ) . '</option>'; |
| 158 | } |
| 159 | echo '</select>'; |
| 160 | |
| 161 | echo '<button name="deleteButton" id="deleteButton" data-date="' . esc_attr( $chosen_date ) . '" class="button logs-page button-primary">Delete ' . esc_html( $chosen_date ) . ' logs</button>'; |
| 162 | echo '<button name="deleteAllButton" id="deleteAllButton" class="button logs-page button-secondary">Delete all logs</button>'; |
| 163 | |
| 164 | wp_nonce_field( 'bh-wp-logger-delete', 'delete_logs_wpnonce' ); |
| 165 | |
| 166 | echo '<p>Current log level: <b>' . esc_html( ucfirst( $this->settings->get_log_level() ) ) . '</b></p>'; |
| 167 | |
| 168 | // If this is in the logger's private-uploads directory, then it already should be accessible, but if it's in the wc-logs folder, it will not be. |
| 169 | $download_url = wp_nonce_url( admin_url( 'admin.php?page=' . $this->settings->get_plugin_slug() . '&date=' . $date . '&download-log=true' ), 'bh-wp-logger-download' ); |
| 170 | $filepath = $log_files[ $chosen_date ]; |
| 171 | $filename = basename( $filepath ); |
| 172 | // TODO: Show file size here. Show number of entries. |
| 173 | echo '<p>Displaying log file at <a href="' . esc_url( $download_url ) . '" download="' . esc_attr( $filename ) . '"><code>' . esc_html( $filepath ) . '</code></a></p>'; |
| 174 | |
| 175 | echo '<p>Display levels: '; |
| 176 | |
| 177 | $log_level_counts = array( |
| 178 | LogLevel::ERROR => 0, |
| 179 | LogLevel::WARNING => 0, |
| 180 | LogLevel::NOTICE => 0, |
| 181 | LogLevel::INFO => 0, |
| 182 | LogLevel::DEBUG => 0, |
| 183 | ); |
| 184 | foreach ( $logs_table->get_data() as $datum ) { |
| 185 | ++$log_level_counts[ strtolower( $datum['level'] ) ]; |
| 186 | } |
| 187 | |
| 188 | $checkboxes_first = true; |
| 189 | foreach ( $log_level_counts as $log_level => $log_level_count ) { |
| 190 | if ( ! $checkboxes_first ) { |
| 191 | echo ' • '; |
| 192 | } |
| 193 | $checkboxes_first = false; |
| 194 | |
| 195 | $disabled = 0 === $log_level_count ? 'disabled' : ''; |
| 196 | $friendly_level = ucfirst( $log_level ); |
| 197 | printf( |
| 198 | '<input %s class="log_level_display_checkbox" type="checkbox" id="log_level_display_checkbox_%s" name="log_level_display_checkbox_%s" checked>' |
| 199 | . '<label for="log_level_display_checkbox_%s">%s (%s)</label>', |
| 200 | esc_attr( $disabled ), |
| 201 | esc_attr( $log_level ), |
| 202 | esc_attr( $log_level ), |
| 203 | esc_attr( $log_level ), |
| 204 | esc_html( $friendly_level ), |
| 205 | intval( $log_level_count ) |
| 206 | ); |
| 207 | } |
| 208 | |
| 209 | echo '</p>'; |
| 210 | |
| 211 | // TODO: Add an action here for other plugins to add controls. |
| 212 | |
| 213 | $logs_table->prepare_items(); |
| 214 | $logs_table->display(); |
| 215 | |
| 216 | echo '</div>'; |
| 217 | |
| 218 | $this->api->set_last_logs_view_time(); |
| 219 | } |
| 220 | |
| 221 | /** |
| 222 | * Enqueue the logs page JavaScript for changing date and deleting logs. |
| 223 | * Checks the plugin slug and only adds the script on the logs page for this plugin. |
| 224 | * |
| 225 | * @hooked admin_enqueue_scripts |
| 226 | */ |
| 227 | public function enqueue_scripts(): void { |
| 228 | |
| 229 | $slug = $this->settings->get_plugin_slug(); |
| 230 | $page_suffix = "_{$slug}-logs"; |
| 231 | |
| 232 | $current_page = get_current_screen(); |
| 233 | |
| 234 | /** |
| 235 | * `$current_page->id` will begin with `admin_page` or `$parent_slug` determined in `add_page()`. |
| 236 | * |
| 237 | * @see Logs_Page::add_page() |
| 238 | */ |
| 239 | if ( is_null( $current_page ) || ! str_ends_with( $current_page->id, $page_suffix ) ) { |
| 240 | return; |
| 241 | } |
| 242 | |
| 243 | // This is the bh-wp-logger JavaScript version, not the plugin version. |
| 244 | $version = '1.1.0'; |
| 245 | |
| 246 | $js_path = realpath( __DIR__ . '/../../' ) . '/assets/bh-wp-logger-admin.js'; |
| 247 | $js_url = plugin_dir_url( $js_path ) . 'bh-wp-logger-admin.js'; |
| 248 | |
| 249 | wp_enqueue_script( 'bh-wp-logger-admin-logs-page-' . $slug, $js_url, array( 'jquery', 'renderjson', 'colresizable' ), $version, true ); |
| 250 | |
| 251 | $renderjson_js_path = realpath( __DIR__ . '/../../' ) . '/assets/vendor/renderjson/renderjson.js'; |
| 252 | $renderjson_js_url = plugin_dir_url( $renderjson_js_path ) . 'renderjson.js'; |
| 253 | |
| 254 | wp_enqueue_script( 'renderjson', $renderjson_js_url, array(), '1.4', true ); |
| 255 | |
| 256 | $colresizable_js_path = realpath( __DIR__ . '/../../' ) . '/assets/vendor/colresizable/colResizable-1.6.min.js'; |
| 257 | $colresizable_js_url = plugin_dir_url( $colresizable_js_path ) . 'colResizable-1.6.min.js'; |
| 258 | |
| 259 | wp_enqueue_script( 'colresizable', $colresizable_js_url, array( 'jquery' ), '1.6', true ); |
| 260 | } |
| 261 | |
| 262 | /** |
| 263 | * Register the stylesheets for the logs page. |
| 264 | * (colours the rows with the severity of the log message!). |
| 265 | * |
| 266 | * @hooked admin_enqueue_scripts |
| 267 | */ |
| 268 | public function enqueue_styles(): void { |
| 269 | |
| 270 | $slug = $this->settings->get_plugin_slug(); |
| 271 | $page_suffix = "_{$slug}-logs"; |
| 272 | |
| 273 | $current_page = get_current_screen(); |
| 274 | |
| 275 | /** |
| 276 | * `$current_page->id` will begin with `admin_page` or `$parent_slug` determined in `add_page()`. |
| 277 | * |
| 278 | * @see Logs_Page::add_page() |
| 279 | */ |
| 280 | if ( is_null( $current_page ) || ! str_ends_with( $current_page->id, $page_suffix ) ) { |
| 281 | return; |
| 282 | } |
| 283 | |
| 284 | $handle = "{$this->settings->get_plugin_slug()}-logs"; |
| 285 | |
| 286 | $version = '1.0.0'; |
| 287 | |
| 288 | $css_path = realpath( __DIR__ . '/../../' ) . '/assets/bh-wp-logger.css'; |
| 289 | $css_url = plugin_dir_url( $css_path ) . 'bh-wp-logger.css'; |
| 290 | |
| 291 | wp_enqueue_style( $handle, $css_url, array(), $version ); |
| 292 | } |
| 293 | } |