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