PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.9.4
WooCommerce v10.9.4
10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / includes / admin / class-wc-admin-log-table-list.php
woocommerce / includes / admin Last commit date
helper 2 months ago importers 1 year ago list-tables 1 month ago marketplace-suggestions 11 months ago meta-boxes 1 month ago notes 2 months ago plugin-updates 2 years ago reports 3 months ago settings 6 days ago views 1 month ago class-wc-admin-addons.php 9 months ago class-wc-admin-api-keys-table-list.php 2 years ago class-wc-admin-api-keys.php 11 months ago class-wc-admin-assets.php 1 month ago class-wc-admin-attributes.php 1 month ago class-wc-admin-brands.php 4 months ago class-wc-admin-customize.php 5 years ago class-wc-admin-dashboard-setup.php 1 month ago class-wc-admin-dashboard.php 4 months ago class-wc-admin-duplicate-product.php 5 months ago class-wc-admin-exporters.php 1 year ago class-wc-admin-help.php 2 years ago class-wc-admin-importers.php 11 months ago class-wc-admin-log-table-list.php 4 months ago class-wc-admin-marketplace-promotions.php 4 months ago class-wc-admin-menus.php 1 month ago class-wc-admin-meta-boxes.php 1 month ago class-wc-admin-notices.php 1 month ago class-wc-admin-permalink-settings.php 5 years ago class-wc-admin-pointers.php 3 years ago class-wc-admin-post-types.php 1 year ago class-wc-admin-profile.php 1 year ago class-wc-admin-reports.php 4 months ago class-wc-admin-settings.php 3 months ago class-wc-admin-setup-wizard.php 1 month ago class-wc-admin-status.php 1 year ago class-wc-admin-taxonomies.php 1 month ago class-wc-admin-upload-downloadable-product.php 2 years ago class-wc-admin-webhooks-table-list.php 1 month ago class-wc-admin-webhooks.php 1 month ago class-wc-admin.php 3 months ago wc-admin-functions.php 7 months ago wc-meta-box-functions.php 1 year ago woocommerce-legacy-reports.php 1 year ago
class-wc-admin-log-table-list.php
546 lines
1 <?php
2 /**
3 * WooCommerce Log Table List
4 *
5 * @author WooThemes
6 * @category Admin
7 * @package WooCommerce\Admin
8 * @version 1.0.0
9 */
10
11 if ( ! defined( 'ABSPATH' ) ) {
12 exit;
13 }
14
15 if ( ! class_exists( 'WP_List_Table' ) ) {
16 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
17 }
18
19 class WC_Admin_Log_Table_List extends WP_List_Table {
20 /**
21 * The key for the user option of how many list table items to display per page.
22 *
23 * @const string
24 */
25 public const PER_PAGE_USER_OPTION_KEY = 'woocommerce_status_log_items_per_page';
26
27 /**
28 * The key for the option that stores the list of unique sources that exist in the log table.
29 *
30 * @const string
31 */
32 public const SOURCE_CACHE_OPTION_KEY = 'woocommerce_status_log_db_sources';
33
34 /**
35 * If the number of log entries is over this number, cache the query that gets the total count.
36 */
37 private const ITEM_COUNT_CACHE_THRESHOLD = 100000;
38
39 /**
40 * Initialize the log table list.
41 */
42 public function __construct() {
43 parent::__construct(
44 array(
45 'singular' => 'log',
46 'plural' => 'logs',
47 'ajax' => false,
48 )
49 );
50 }
51
52 /**
53 * Display level dropdown
54 *
55 * @global wpdb $wpdb
56 */
57 public function level_dropdown() {
58 $labels = WC_Log_Levels::get_all_level_labels();
59
60 $levels = array_reduce(
61 array_keys( $labels ),
62 function( $carry, $item ) use ( $labels ) {
63 $carry[] = array(
64 'value' => $item,
65 'label' => $labels[ $item ],
66 );
67
68 return $carry;
69 },
70 array()
71 );
72
73 $selected_level = isset( $_REQUEST['level'] ) ? $_REQUEST['level'] : '';
74 ?>
75 <label for="filter-by-level" class="screen-reader-text"><?php esc_html_e( 'Filter by level', 'woocommerce' ); ?></label>
76 <select name="level" id="filter-by-level">
77 <option<?php selected( $selected_level, '' ); ?> value=""><?php esc_html_e( 'All levels', 'woocommerce' ); ?></option>
78 <?php
79 foreach ( $levels as $l ) {
80 printf(
81 '<option%1$s value="%2$s">%3$s</option>',
82 selected( $selected_level, $l['value'], false ),
83 esc_attr( $l['value'] ),
84 esc_html( $l['label'] )
85 );
86 }
87 ?>
88 </select>
89 <?php
90 }
91
92 /**
93 * Generates the table rows.
94 *
95 * @return void
96 */
97 public function display_rows() {
98 foreach ( $this->items as $log ) {
99 $this->single_row( $log );
100 if ( ! empty( $log['context'] ) ) {
101 $this->context_row( $log );
102 }
103 }
104 }
105
106 /**
107 * Render the additional table row that contains extra log context data.
108 *
109 * @param array $log Log entry data.
110 *
111 * @return void
112 */
113 protected function context_row( $log ) {
114 // Maintains alternating row background colors.
115 ?>
116 <tr style="display: none"><td></td></tr>
117 <tr id="log-context-<?php echo esc_attr( $log['log_id'] ); ?>" class="log-context">
118 <td colspan="<?php echo esc_attr( $this->get_column_count() ); ?>">
119 <p><strong><?php esc_html_e( 'Additional context', 'woocommerce' ); ?></strong></p>
120 <pre><?php echo esc_html( $log['context'] ); ?></pre>
121 </td>
122 </tr>
123 <?php
124 }
125
126 /**
127 * Get list columns.
128 *
129 * @return array
130 */
131 public function get_columns() {
132 return array(
133 'cb' => '<input type="checkbox" />',
134 'timestamp' => __( 'Timestamp', 'woocommerce' ),
135 'level' => __( 'Level', 'woocommerce' ),
136 'message' => __( 'Message', 'woocommerce' ),
137 'source' => __( 'Source', 'woocommerce' ),
138 'context' => __( 'Context', 'woocommerce' ),
139 );
140 }
141
142 /**
143 * Column cb.
144 *
145 * @param array $log
146 * @return string
147 */
148 public function column_cb( $log ) {
149 return sprintf( '<input type="checkbox" name="log[]" value="%1$s" />', esc_attr( $log['log_id'] ) );
150 }
151
152 /**
153 * Timestamp column.
154 *
155 * @param array $log
156 * @return string
157 */
158 public function column_timestamp( $log ) {
159 return esc_html(
160 mysql2date(
161 'Y-m-d H:i:s',
162 $log['timestamp']
163 )
164 );
165 }
166
167 /**
168 * Level column.
169 *
170 * @param array $log
171 * @return string
172 */
173 public function column_level( $log ) {
174 $level_key = WC_Log_Levels::get_severity_level( $log['level'] );
175 $levels = WC_Log_Levels::get_all_level_labels();
176
177 if ( ! isset( $levels[ $level_key ] ) ) {
178 return '';
179 }
180
181 $level = $levels[ $level_key ];
182 $level_class = sanitize_html_class( 'log-level--' . $level_key );
183 return '<span class="log-level ' . $level_class . '">' . esc_html( $level ) . '</span>';
184 }
185
186 /**
187 * Message column.
188 *
189 * @param array $log
190 * @return string
191 */
192 public function column_message( $log ) {
193 return sprintf(
194 '<pre>%s</pre>',
195 esc_html( $log['message'] )
196 );
197 }
198
199 /**
200 * Source column.
201 *
202 * @param array $log
203 * @return string
204 */
205 public function column_source( $log ) {
206 return esc_html( $log['source'] );
207 }
208
209 /**
210 * Context column.
211 *
212 * @param array $log Log entry data.
213 *
214 * @return string
215 */
216 public function column_context( $log ) {
217 $content = '';
218
219 if ( ! empty( $log['context'] ) ) {
220 ob_start();
221 ?>
222 <button
223 class="log-toggle button button-secondary button-small"
224 data-log-id="<?php echo esc_attr( $log['log_id'] ); ?>"
225 data-toggle-status="off"
226 data-label-show="<?php esc_attr_e( 'Show context', 'woocommerce' ); ?>"
227 data-label-hide="<?php esc_attr_e( 'Hide context', 'woocommerce' ); ?>"
228 >
229 <span class="dashicons dashicons-arrow-down-alt2"></span>
230 <span class="log-toggle-label screen-reader-text"><?php esc_html_e( 'Show context', 'woocommerce' ); ?></span>
231 </button>
232 <?php
233 $content = ob_get_clean();
234 }
235
236 return $content;
237 }
238
239 /**
240 * Get bulk actions.
241 *
242 * @return array
243 */
244 protected function get_bulk_actions() {
245 return array(
246 'delete' => __( 'Delete', 'woocommerce' ),
247 );
248 }
249
250 /**
251 * Extra controls to be displayed between bulk actions and pagination.
252 *
253 * @param string $which
254 */
255 protected function extra_tablenav( $which ) {
256 if ( 'top' === $which ) {
257 echo '<div class="alignleft actions">';
258 $this->level_dropdown();
259 $this->source_dropdown();
260 submit_button( __( 'Filter', 'woocommerce' ), '', 'filter-action', false );
261 echo '</div>';
262 }
263 }
264
265 /**
266 * Get a list of sortable columns.
267 *
268 * @return array
269 */
270 protected function get_sortable_columns() {
271 return array(
272 'timestamp' => array( 'timestamp', true ),
273 'level' => array( 'level', true ),
274 'source' => array( 'source', true ),
275 );
276 }
277
278 /**
279 * Display source dropdown
280 *
281 * @global wpdb $wpdb
282 */
283 protected function source_dropdown() {
284 $sources = $this->get_sources();
285
286 if ( ! empty( $sources ) ) {
287 $selected_source = isset( $_REQUEST['source'] ) ? $_REQUEST['source'] : '';
288 ?>
289 <label for="filter-by-source" class="screen-reader-text"><?php esc_html_e( 'Filter by source', 'woocommerce' ); ?></label>
290 <select name="source" id="filter-by-source">
291 <option<?php selected( $selected_source, '' ); ?> value=""><?php esc_html_e( 'All sources', 'woocommerce' ); ?></option>
292 <?php
293 foreach ( $sources as $s ) {
294 printf(
295 '<option%1$s value="%2$s">%3$s</option>',
296 selected( $selected_source, $s, false ),
297 esc_attr( $s ),
298 esc_html( $s )
299 );
300 }
301 ?>
302 </select>
303 <?php
304 }
305 }
306
307 /**
308 * Get the list of unique sources in the log table.
309 *
310 * The query in this method can be slow when there are a high number of log entries. The list of sources also
311 * most likely doesn't change that often. So this indefinitely caches the list into the WP options table. The
312 * cache will get cleared by the log handler if a new source is being added. See WC_Log_Handler_DB::handle().
313 *
314 * @return array
315 */
316 protected function get_sources() {
317 global $wpdb;
318
319 $sources = get_option( self::SOURCE_CACHE_OPTION_KEY, null );
320 if ( is_array( $sources ) ) {
321 return $sources;
322 }
323
324 $sql = "
325 SELECT DISTINCT source
326 FROM {$wpdb->prefix}woocommerce_log
327 WHERE source != ''
328 ORDER BY source ASC
329 ";
330
331 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- Not necessary.
332 $sources = $wpdb->get_col( $sql );
333
334 update_option( self::SOURCE_CACHE_OPTION_KEY, $sources );
335
336 return $sources;
337 }
338
339 /**
340 * Prepare table list items.
341 *
342 * @global wpdb $wpdb
343 */
344 public function prepare_items() {
345 global $wpdb;
346
347 $this->prepare_column_headers();
348
349 $per_page = $this->get_items_per_page(
350 self::PER_PAGE_USER_OPTION_KEY,
351 $this->get_per_page_default()
352 );
353
354 $where = $this->get_items_query_where();
355 $order = $this->get_items_query_order();
356 $limit = $this->get_items_query_limit();
357 $offset = $this->get_items_query_offset();
358
359 $query_items = "
360 SELECT log_id, timestamp, level, message, source, context
361 FROM {$wpdb->prefix}woocommerce_log
362 {$where} {$order} {$limit} {$offset}
363 ";
364
365 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- The query parts are prepared in their respective methods.
366 $this->items = $wpdb->get_results( $query_items, ARRAY_A );
367 $total_items = $this->get_total_items_count();
368
369 $this->set_pagination_args(
370 array(
371 'total_items' => $total_items,
372 'per_page' => $per_page,
373 'total_pages' => ceil( $total_items / $per_page ),
374 )
375 );
376 }
377
378 /**
379 * Get the total count of log entries in the database.
380 *
381 * The query in this method can be slow if there are a large (100k+) rows in the database table, so this
382 * uses a transient to cache the count for 10 minutes if the count is over that threshold.
383 *
384 * @return int
385 */
386 protected function get_total_items_count() {
387 global $wpdb;
388
389 $where = $this->get_items_query_where();
390 $version = \WC_Cache_Helper::get_transient_version( 'logs-db' );
391 $transient_key = 'wc-log-total-items-count-' . md5( $where );
392 $transient = get_transient( $transient_key );
393 if (
394 false !== $transient
395 && isset( $transient['value'], $transient['version'] )
396 && $transient['version'] === $version
397 ) {
398 return $transient['value'];
399 }
400
401 $count_query = "
402 SELECT COUNT(*)
403 FROM {$wpdb->prefix}woocommerce_log
404 {$where}
405 ";
406
407 // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- The where clause is prepared in a separate method.
408 $count = intval( $wpdb->get_var( $count_query ) );
409
410 if ( $count > self::ITEM_COUNT_CACHE_THRESHOLD ) {
411 $transient = array(
412 'value' => $count,
413 'version' => \WC_Cache_Helper::get_transient_version( 'logs-db', true ),
414 );
415
416 set_transient( $transient_key, $transient, 10 * MINUTE_IN_SECONDS );
417 } else {
418 delete_transient( $transient_key );
419 }
420
421 return $count;
422 }
423
424 /**
425 * Get prepared LIMIT clause for items query
426 *
427 * @global wpdb $wpdb
428 *
429 * @return string Prepared LIMIT clause for items query.
430 */
431 protected function get_items_query_limit() {
432 global $wpdb;
433
434 $per_page = $this->get_items_per_page(
435 self::PER_PAGE_USER_OPTION_KEY,
436 $this->get_per_page_default()
437 );
438
439 return $wpdb->prepare( 'LIMIT %d', $per_page );
440 }
441
442 /**
443 * Get prepared OFFSET clause for items query
444 *
445 * @global wpdb $wpdb
446 *
447 * @return string Prepared OFFSET clause for items query.
448 */
449 protected function get_items_query_offset() {
450 global $wpdb;
451
452 $per_page = $this->get_items_per_page(
453 self::PER_PAGE_USER_OPTION_KEY,
454 $this->get_per_page_default()
455 );
456 $current_page = $this->get_pagenum();
457 if ( 1 < $current_page ) {
458 $offset = $per_page * ( $current_page - 1 );
459 } else {
460 $offset = 0;
461 }
462
463 return $wpdb->prepare( 'OFFSET %d', $offset );
464 }
465
466 /**
467 * Get prepared ORDER BY clause for items query
468 *
469 * @return string Prepared ORDER BY clause for items query.
470 */
471 protected function get_items_query_order() {
472 $valid_orders = array( 'level', 'source', 'timestamp' );
473 if ( ! empty( $_REQUEST['orderby'] ) && in_array( $_REQUEST['orderby'], $valid_orders ) ) {
474 $by = wc_clean( $_REQUEST['orderby'] );
475 } else {
476 $by = 'log_id';
477 }
478 $by = esc_sql( $by );
479
480 if ( ! empty( $_REQUEST['order'] ) && 'asc' === strtolower( $_REQUEST['order'] ) ) {
481 $order = 'ASC';
482 } else {
483 $order = 'DESC';
484 }
485
486 $orderby = "ORDER BY {$by} {$order}";
487 if ( 'log_id' !== $by ) {
488 $orderby .= ", log_id {$order}";
489 }
490
491 return $orderby;
492 }
493
494 /**
495 * Get prepared WHERE clause for items query
496 *
497 * @global wpdb $wpdb
498 *
499 * @return string Prepared WHERE clause for items query.
500 */
501 protected function get_items_query_where() {
502 global $wpdb;
503
504 $where_conditions = array();
505 $where_values = array();
506 if ( ! empty( $_REQUEST['level'] ) && WC_Log_Levels::is_valid_level( $_REQUEST['level'] ) ) {
507 $where_conditions[] = 'level >= %d';
508 $where_values[] = WC_Log_Levels::get_level_severity( $_REQUEST['level'] );
509 }
510 if ( ! empty( $_REQUEST['source'] ) ) {
511 $where_conditions[] = 'source = %s';
512 $where_values[] = wc_clean( $_REQUEST['source'] );
513 }
514 if ( ! empty( $_REQUEST['s'] ) ) {
515 $where_conditions[] = 'message like %s';
516 $where_values[] = '%' . $wpdb->esc_like( wc_clean( wp_unslash( $_REQUEST['s'] ) ) ) . '%';
517 }
518
519 if ( empty( $where_conditions ) ) {
520 return '';
521 }
522
523 return $wpdb->prepare( 'WHERE 1 = 1 AND ' . implode( ' AND ', $where_conditions ), $where_values );
524 }
525
526 /**
527 * Set _column_headers property for table list
528 */
529 protected function prepare_column_headers() {
530 $this->_column_headers = array(
531 $this->get_columns(),
532 array(),
533 $this->get_sortable_columns(),
534 );
535 }
536
537 /**
538 * Helper to get the default value for the per_page arg.
539 *
540 * @return int
541 */
542 public function get_per_page_default(): int {
543 return 20;
544 }
545 }
546