PluginProbe
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts / trunk
Woody Code Snippets – Insert PHP, CSS, JS, and Header/Footer Scripts vtrunk
2.7.7 2.7.6 2.7.5 2.7.4 trunk 1.3 2.0.4 2.0.6 2.1.91 2.2.4 2.2.7 2.2.9 2.3.1 2.3.10 2.4.10 2.4.2 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.6.0 2.6.1 2.7.0 All 28 releases
← All changes | admin/includes/class.snippets.table.php +634 -557 2.2.4trunk View file →
@@ -1,557 +1,634 @@
1 -<?php
2 -/**
3 - * This class is implemented page: snippet table
4 - *
5 - * @author Webcraftic <wordpress.webraftic@gmail.com>
6 - * @copyright (c) 2019, OnePress Ltd
7 - *s
8 - * @package core
9 - * @since 1.0.0
10 - */
11 -
12 -// Exit if accessed directly
13 -if ( ! defined( 'ABSPATH' ) ) {
14 - exit;
15 -}
16 -
17 -if ( ! class_exists( 'WP_List_Table' ) ) {
18 - require_once( ABSPATH . 'wp-admin/includes/class-wp-list-table.php' );
19 -}
20 -
21 -/************************** CREATE A PACKAGE CLASS *****************************
22 - *******************************************************************************
23 - * Create a new list table package that extends the core WP_List_Table class.
24 - * WP_List_Table contains most of the framework for generating the table, but we
25 - * need to define and override some methods so that our data can be displayed
26 - * exactly the way we need it to be.
27 - *
28 - * To display this example on a page, you will first need to instantiate the class,
29 - * then call $yourInstance->prepare_items() to handle any data manipulation, then
30 - * finally call $yourInstance->display() to render the table to the page.
31 - *
32 - * Our theme for this list table is going to be movies.
33 - */
34 -class WINP_Snippet_Library_Table extends WP_List_Table {
35 -
36 - /** ************************************************************************
37 - * Normally we would be querying data from a database and manipulating that
38 - * for use in your list table. For this example, we're going to simplify it
39 - * slightly and create a pre-built array. Think of this as the data that might
40 - * be returned by $wpdb->query()
41 - *
42 - * In a real-world scenario, you would make your own custom query inside
43 - * this class' prepare_items() method.
44 - *
45 - * @var array
46 - **************************************************************************/
47 - var $example_data = array();
48 -
49 - /**
50 - * Is modal window
51 - *
52 - * @var bool
53 - */
54 - private $modal;
55 -
56 - /**
57 - * Если true, то выводить общие сниппеты без привязки к пользователю
58 - *
59 - * @var bool
60 - */
61 - private $common;
62 -
63 - /**
64 - * @var array
65 - *
66 - * Array contains slug columns that you want hidden
67 - *
68 - */
69 - private $hidden_columns = array(
70 - 'id',
71 - );
72 -
73 - /**
74 - * @var integer
75 - */
76 - private $per_page = 10;
77 -
78 - /** ************************************************************************
79 - * REQUIRED. Set up a constructor that references the parent constructor. We
80 - * use the parent reference to set some default configs.
81 - ***************************************************************************/
82 - /**
83 - * WINP_Snippet_Library_Table constructor.
84 - *
85 - * @param bool $modal
86 - */
87 - function __construct( $modal = false ) {
88 - global $status, $page;
89 -
90 - $this->modal = $modal;
91 - $this->common = true;
92 -
93 - // Set parent defaults
94 - parent::__construct(
95 - array(
96 - 'singular' => 'snippet', // singular name of the listed records
97 - 'plural' => 'snippets', // plural name of the listed records
98 - 'ajax' => true, // does this table support ajax?
99 - )
100 - );
101 -
102 - }
103 -
104 - /** ************************************************************************
105 - * Recommended. This method is called when the parent class can't find a method
106 - * specifically build for a given column. Generally, it's recommended to include
107 - * one method for each column you want to render, keeping your package class
108 - * neat and organized. For example, if the class needs to process a column
109 - * named 'title', it would first see if a method named $this->column_title()
110 - * exists - if it does, that method will be used. If it doesn't, this one will
111 - * be used. Generally, you should try to use custom column methods as much as
112 - * possible.
113 - *
114 - * Since we have defined a column_title() method later on, this method doesn't
115 - * need to concern itself with any column with a name of 'title'. Instead, it
116 - * needs to handle everything else.
117 - *
118 - * For more detailed insight into how columns are handled, take a look at
119 - * WP_List_Table::single_row_columns()
120 - *
121 - * @param array $item A singular item (one full row's worth of data)
122 - * @param string $column_name The name/slug of the column to be processed
123 - * @return string Text or HTML to be placed inside the column <td>
124 - **************************************************************************/
125 - public function column_default( $item, $column_name ) {
126 - switch ( $column_name ) {
127 - case 'type':
128 - $class = 'wbcr-inp-type-' . esc_attr( $item[ $column_name ] );
129 - $type = 'universal' == $item[ $column_name ] ? 'uni' : esc_attr( $item[ $column_name ] );
130 -
131 - return '<div class="wbcr-inp-snippet-type-label ' . $class . '">' . esc_html( $type ) . '</div>';
132 - case 'desc':
133 - $desc = strlen( $item[ $column_name ] ) > 500 ? substr( $item[ $column_name ], 0, 500 ) : $item[ $column_name ];
134 - return '<div class="wbcr-inp-snippet-description" title="' . esc_attr( $desc ) . '">' . esc_html( $desc ) . '</div>';
135 - case 'datetime':
136 - case 'insert':
137 - case 'delete':
138 - return $item[ $column_name ];
139 - default:
140 - return print_r( $item, true ); // Show the whole array for troubleshooting purposes
141 - }
142 - }
143 -
144 - /** ************************************************************************
145 - * Recommended. This is a custom column method and is responsible for what
146 - * is rendered in any column with a name/slug of 'title'. Every time the class
147 - * needs to render a column, it first looks for a method named
148 - * column_{$column_title} - if it exists, that method is run. If it doesn't
149 - * exist, column_default() is called instead.
150 - *
151 - * This example also illustrates how to implement rollover actions. Actions
152 - * should be an associative array formatted as 'slug'=>'link html' - and you
153 - * will need to generate the URLs yourself. You could even ensure the links
154 - *
155 - *
156 - * @see WP_List_Table::::single_row_columns()
157 - * @param array $item A singular item (one full row's worth of data)
158 - * @return string Text to be placed inside the column <td> (movie title only)
159 - **************************************************************************/
160 - public function column_title( $item ) {
161 - //Build row actions
162 - $actions = array(
163 - /*'edit' => sprintf( '<a href="?page=%s&action=%s&movie=%s">Edit</a>', $_REQUEST['page'], 'edit', $item['ID'] ),
164 - 'delete' => sprintf( '<a href="?page=%s&action=%s&movie=%s">Delete</a>', $_REQUEST['page'], 'delete', $item['ID'] ),*/
165 - );
166 -
167 - $url = admin_url() . 'post-new.php?post_type=' . WINP_SNIPPETS_POST_TYPE . '&winp_item=' . $item['type'] . '&snippet_id=' . $item['ID'] . ( $this->common ? '&common=1' : '' );
168 - //Return the title contents
169 - return sprintf(
170 - '<a href="%1$s"><b>%2$s</b></a>%3$s',
171 - /*$1%s*/
172 - esc_url( $url ),
173 - /*$2%s*/
174 - esc_html( $item['title'] ),
175 - /*$3%s*/
176 - $this->row_actions( $actions )
177 - );
178 - }
179 -
180 - /** ************************************************************************
181 - * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
182 - * is given special treatment when columns are processed. It ALWAYS needs to
183 - * have it's own method.
184 - *
185 - * @see WP_List_Table::::single_row_columns()
186 - * @param array $item A singular item (one full row's worth of data)
187 - * @return string Text to be placed inside the column <td> (movie title only)
188 - **************************************************************************/
189 - public function column_cb( $item ) {
190 - return sprintf(
191 - '<input type="checkbox" name="%1$s[]" value="%2$s" />',
192 - /*$1%s*/
193 - esc_attr( $this->_args['singular'] ), //Let's simply repurpose the table's singular label ("movie")
194 - /*$2%s*/
195 - esc_attr( $item['ID'] ) //The value of the checkbox should be the record's id
196 - );
197 - }
198 -
199 - /** ************************************************************************
200 - * REQUIRED! This method dictates the table's columns and titles. This should
201 - * return an array where the key is the column slug (and class) and the value
202 - * is the column's title text. If you need a checkbox for bulk actions, refer
203 - * to the $columns array below.
204 - *
205 - * The 'cb' column is treated differently than the rest. If including a checkbox
206 - * column in your table you must create a column_cb() method. If you don't need
207 - * bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
208 - *
209 - * @see WP_List_Table::::single_row_columns()
210 - * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
211 - **************************************************************************/
212 - public function get_columns() {
213 - $columns = array(
214 - // 'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
215 - 'type' => __( 'Type', 'insert-php' ),
216 - 'title' => __( 'Title', 'insert-php' ),
217 - 'desc' => __( 'Description', 'insert-php' ),
218 - 'datetime' => __( 'Date', 'insert-php' ),
219 - 'insert' => __( 'Insert', 'insert-php' ),
220 - );
221 -
222 - if ( ! $this->modal && ! $this->common ) {
223 - $columns['delete'] = __( 'Delete', 'insert-php' );
224 - }
225 -
226 - return $columns;
227 - }
228 -
229 - /** ************************************************************************
230 - * Optional. If you want one or more columns to be sortable (ASC/DESC toggle),
231 - * you will need to register it here. This should return an array where the
232 - * key is the column that needs to be sortable, and the value is db column to
233 - * sort by. Often, the key and value will be the same, but this is not always
234 - * the case (as the value is a column name from the database, not the list table).
235 - *
236 - * This method merely defines which columns should be sortable and makes them
237 - * clickable - it does not handle the actual sorting. You still need to detect
238 - * the ORDERBY and ORDER querystring variables within prepare_items() and sort
239 - * your data accordingly (usually by modifying your query).
240 - *
241 - * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
242 - **************************************************************************/
243 - public function get_sortable_columns() {
244 - $sortable_columns = array(
245 - 'title' => array( 'title', false ), //true means it's already sorted
246 - 'type' => array( 'type', false ),
247 - 'datetime' => array( 'datetime', false ),
248 - );
249 -
250 - return $sortable_columns;
251 - }
252 -
253 - /** ************************************************************************
254 - * Optional. If you need to include bulk actions in your list table, this is
255 - * the place to define them. Bulk actions are an associative array in the format
256 - * 'slug'=>'Visible Title'
257 - *
258 - * If this method returns an empty value, no bulk action will be rendered. If
259 - * you specify any bulk actions, the bulk actions box will be rendered with
260 - * the table automatically on display().
261 - *
262 - * Also note that list tables are not automatically wrapped in <form> elements,
263 - * so you will need to create those manually in order for bulk actions to function.
264 - *
265 - * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
266 - **************************************************************************/
267 - public function get_bulk_actions() {
268 - $actions = array(
269 - //'sync' => __( 'Synchronization', 'insert-php' ),
270 - );
271 -
272 - return $actions;
273 - }
274 -
275 - /** ************************************************************************
276 - * Optional. You can handle your bulk actions anywhere or anyhow you prefer.
277 - * For this example package, we will handle it in the class to keep things
278 - * clean and organized.
279 - *
280 - * @see $this->prepare_items()
281 - **************************************************************************/
282 - public function process_bulk_action() {
283 -
284 - //Detect when a bulk action is being triggered...
285 - /*if ( 'sync' === $this->current_action() ) {
286 - wp_die( 'Synchronization' );
287 - }*/
288 -
289 - }
290 -
291 - /**
292 - * Get snippets data
293 - *
294 - * @return array
295 - */
296 - public function get_data() {
297 - $data = array();
298 - $saved_data = array();
299 -
300 - $orderby = WINP_Plugin::app()->request->request( 'orderby', 'title', true );
301 - $order = WINP_Plugin::app()->request->request( 'order', 'asc', true );
302 - $paged = WINP_Plugin::app()->request->request( 'paged', 1 );
303 -
304 - $order_tags = array(
305 - 'title' => 'title',
306 - 'type' => 'type_id',
307 - 'datetime' => 'updated_at',
308 - );
309 -
310 - $args = array(
311 - 'per-page=' . $this->per_page,
312 - 'page=' . $paged,
313 - 'sort=' . ( 'asc' == $order ? '' : '-' ) . ( $order_tags[ $orderby ] ),
314 - );
315 -
316 - $snippets = WINP_Plugin::app()->get_api_object()->get_all_snippets( $this->common, $args );
317 - if ( $snippets ) {
318 - foreach ( $snippets as $snippet ) {
319 - $data[] = array(
320 - 'ID' => $snippet->id,
321 - 'title' => esc_html( $snippet->title ),
322 - 'desc' => esc_html( $snippet->description ),
323 - 'type' => $snippet->type->title,
324 - 'datetime' => date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $snippet->updated_at ),
325 - 'insert' => '<a class="wbcr-inp-enable-snippet-button button" data-snippet="' . $snippet->id . '" data-common="' . ( $this->common ? 1 : 0 ) . '" href="javascript: void(0)"><span class="dashicons dashicons-plus"></span></a>',
326 - 'delete' => '<a class="wbcr-inp-delete-snippet-button button" data-snippet="' . $snippet->id . '" href="javascript: void(0)"><span class="dashicons dashicons-no"></span></a>',
327 - );
328 -
329 - $saved_data[ $snippet->id ] = array(
330 - 'title' => esc_html( $snippet->title ),
331 - 'desc' => esc_html( $snippet->description ),
332 - 'type' => $snippet->type->slug,
333 - 'content' => $snippet->content,
334 - 'type_id' => $snippet->type_id,
335 - );
336 - }
337 -
338 - update_user_meta( get_current_user_id(), WINP_Plugin::app()->getPrefix() . 'current_snippets', $saved_data );
339 - }
340 -
341 - return $data;
342 - }
343 -
344 - /**
345 - * Get total items for last query
346 - *
347 - * @return int
348 - */
349 - public function get_total_items() {
350 - return WINP_Plugin::app()->get_api_object()->get_total_items();
351 - }
352 -
353 - /** ************************************************************************
354 - * REQUIRED! This is where you prepare your data for display. This method will
355 - * usually be used to query the database, sort and filter the data, and generally
356 - * get it ready to be displayed. At a minimum, we should set $this->items and
357 - * $this->set_pagination_args(), although the following properties and methods
358 - * are frequently interacted with here...
359 - *
360 - * @param bool $common - если true, то выводить общие сниппеты без привязки к пользователю
361 - *
362 - * @global WPDB $wpdb
363 - * @uses $this->_column_headers
364 - * @uses $this->items
365 - * @uses $this->get_columns()
366 - * @uses $this->get_sortable_columns()
367 - * @uses $this->get_pagenum()
368 - * @uses $this->set_pagination_args()
369 - **************************************************************************/
370 - public function prepare_items( $common = false ) {
371 - /**
372 - * First, lets decide how many records per page to show
373 - */
374 - $this->per_page = 10;
375 -
376 - /**
377 - * @param bool $common - если true, то выводить общие сниппеты без привязки к пользователю
378 - */
379 - $this->common = $common;
380 -
381 - /**
382 - * REQUIRED. Now we need to define our column headers. This includes a complete
383 - * array of columns to be displayed (slugs & titles), a list of columns
384 - * to keep hidden, and a list of columns that are sortable. Each of these
385 - * can be defined in another method (as we've done here) before being
386 - * used to build the value for our _column_headers property.
387 - */
388 - $columns = $this->get_columns();
389 - $hidden = $this->hidden_columns;
390 - $sortable = $this->get_sortable_columns();
391 -
392 - /**
393 - * REQUIRED. Finally, we build an array to be used by the class for column
394 - * headers. The $this->_column_headers property takes an array which contains
395 - * 3 other arrays. One for all columns, one for hidden columns, and one
396 - * for sortable columns.
397 - */
398 - $this->_column_headers = array( $columns, $hidden, $sortable );
399 -
400 - /**
401 - * Optional. You can handle your bulk actions however you see fit. In this
402 - * case, we'll handle them within our package just to keep things clean.
403 - */
404 - $this->process_bulk_action();
405 -
406 - /**
407 - * Instead of querying a database, we're going to fetch the example data
408 - * property we created for use in this plugin. This makes this example
409 - * package slightly different than one you might build on your own. In
410 - * this example, we'll be using array manipulation to sort and paginate
411 - * our data. In a real-world implementation, you will probably want to
412 - * use sort and pagination data to build a custom query instead, as you'll
413 - * be able to use your precisely-queried data immediately.
414 - */
415 - $data = $this->get_data();
416 -
417 - /**
418 - * This checks for sorting input and sorts the data in our array accordingly.
419 - *
420 - * In a real-world situation involving a database, you would probably want
421 - * to handle sorting by passing the 'orderby' and 'order' values directly
422 - * to a custom query. The returned data will be pre-sorted, and this array
423 - * sorting technique would be unnecessary.
424 - *
425 - * @param $a
426 - * @param $b
427 - *
428 - * @return int
429 - */
430 - /*function usort_reorder( $a, $b ) {
431 - $orderby = ( ! empty( $_REQUEST['orderby'] ) ) ? $_REQUEST['orderby'] : 'title'; // If no sort, default to title
432 - $order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc'; // If no order, default to asc
433 - $result = strcmp( $a[ $orderby ], $b[ $orderby ] ); // Determine sort order
434 -
435 - return ( 'asc' === $order ) ? $result : - $result; // Send final sort direction to usort
436 - }
437 -
438 - usort( $data, 'usort_reorder' );*/
439 -
440 - /***********************************************************************
441 - * ---------------------------------------------------------------------
442 - * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
443 - *
444 - * In a real-world situation, this is where you would place your query.
445 - *
446 - * For information on making queries in WordPress, see this Codex entry:
447 - * http://codex.wordpress.org/Class_Reference/wpdb
448 - *
449 - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
450 - * ---------------------------------------------------------------------
451 - **********************************************************************/
452 -
453 - /**
454 - * REQUIRED for pagination. Let's figure out what page the user is currently
455 - * looking at. We'll need this later, so you should always include it in
456 - * your own package classes.
457 - */
458 - // $current_page = $this->get_current_page();
459 -
460 - /**
461 - * REQUIRED for pagination. Let's check how many items are in our data array.
462 - * In real-world use, this would be the total number of items in your database,
463 - * without filtering. We'll need this later, so you should always include it
464 - * in your own package classes.
465 - */
466 - $total_items = $this->get_total_items();
467 -
468 - /**
469 - * The WP_List_Table class does not handle pagination for us, so we need
470 - * to ensure that the data is trimmed to only the current page. We can use
471 - * array_slice() to
472 - */
473 - // $data = array_slice( $data, ( ( $current_page - 1 ) * $this->per_page ), $this->per_page );
474 -
475 - /**
476 - * REQUIRED. Now we can add our *sorted* data to the items property, where
477 - * it can be used by the rest of the class.
478 - */
479 - $this->items = $data;
480 -
481 - /**
482 - * REQUIRED. We also have to register our pagination options & calculations.
483 - */
484 - $this->set_pagination_args(
485 - array(
486 - 'total_items' => $total_items,
487 - 'per_page' => $this->per_page,
488 - 'total_pages' => ceil( $total_items / $this->per_page ),
489 - 'orderby' => WINP_Plugin::app()->request->request( 'orderby', 'title', true ),
490 - 'order' => WINP_Plugin::app()->request->request( 'order', 'asc', true ),
491 - )
492 - );
493 - }
494 -
495 - /**
496 - * @Override of display method
497 - */
498 - public function display() {
499 - /**
500 - * Adds a nonce field
501 - */
502 - wp_nonce_field( 'winp-ajax-custom-list-nonce', 'winp_ajax_custom_list_nonce' );
503 -
504 - if ( ! empty( $this->items ) && ! $this->common ) {
505 - foreach ( $this->items as $item ) {
506 - wp_nonce_field( 'winp-ajax-snippet-delete-' . $item['ID'], 'winp_ajax_snippet_delete_' . $item['ID'] );
507 - }
508 - }
509 -
510 - /**
511 - * Adds field order and orderby
512 - */
513 - echo '<input type="hidden" id="order" name="order" value="' . $this->_pagination_args['order'] . '" />';
514 - echo '<input type="hidden" id="orderby" name="orderby" value="' . $this->_pagination_args['orderby'] . '" />';
515 - parent::display();
516 - }
517 -
518 - /**
519 - * @Override ajax_response method
520 - */
521 - public function ajax_response() {
522 - check_ajax_referer( 'winp-ajax-custom-list-nonce', 'winp_ajax_custom_list_nonce' );
523 - $this->prepare_items();
524 - extract( $this->_args );
525 - extract( $this->_pagination_args, EXTR_SKIP );
526 - ob_start();
527 - $no_placeholder = WINP_Plugin::app()->request->request( 'no_placeholder', '' );
528 - if ( ! empty( $no_placeholder ) ) {
529 - $this->display_rows();
530 - } else {
531 - $this->display_rows_or_placeholder();
532 - }
533 - $rows = ob_get_clean();
534 - ob_start();
535 - $this->print_column_headers();
536 - $headers = ob_get_clean();
537 - ob_start();
538 - $this->pagination( 'top' );
539 - $pagination_top = ob_get_clean();
540 - ob_start();
541 - $this->pagination( 'bottom' );
542 - $pagination_bottom = ob_get_clean();
543 - $response = array( 'rows' => $rows );
544 - $response['pagination']['top'] = $pagination_top;
545 - $response['pagination']['bottom'] = $pagination_bottom;
546 - $response['column_headers'] = $headers;
547 - if ( isset( $total_items ) ) {
548 - $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) );
549 - }
550 - if ( isset( $total_pages ) ) {
551 - $response['total_pages'] = $total_pages;
552 - $response['total_pages_i18n'] = number_format_i18n( $total_pages );
553 - }
554 - die( json_encode( $response ) );
555 - }
556 -
557 -}
1 +<?php
2 +/**
3 + * This class is implemented page: snippet table
4 + *
5 + * @since 1.0.0
6 + * @package core
7 + */
8 +
9 +// Exit if accessed directly
10 +if ( ! defined( 'ABSPATH' ) ) {
11 + exit;
12 +}
13 +
14 +if ( ! class_exists( 'WP_List_Table' ) ) {
15 + require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
16 +}
17 +
18 +/************************** CREATE A PACKAGE CLASS *****************************
19 + * ******************************************************************************
20 + * Create a new list table package that extends the core WP_List_Table class.
21 + * WP_List_Table contains most of the framework for generating the table, but we
22 + * need to define and override some methods so that our data can be displayed
23 + * exactly the way we need it to be.
24 + *
25 + * To display this example on a page, you will first need to instantiate the class,
26 + * then call $yourInstance->prepare_items() to handle any data manipulation, then
27 + * finally call $yourInstance->display() to render the table to the page.
28 + *
29 + * Our theme for this list table is going to be movies.
30 + */
31 +class WINP_Snippet_Library_Table extends WP_List_Table {
32 +
33 + /** ************************************************************************
34 + * Normally we would be querying data from a database and manipulating that
35 + * for use in your list table. For this example, we're going to simplify it
36 + * slightly and create a pre-built array. Think of this as the data that might
37 + * be returned by $wpdb->query()
38 + *
39 + * In a real-world scenario, you would make your own custom query inside
40 + * this class' prepare_items() method.
41 + *
42 + * @var array
43 + **************************************************************************/
44 + var $example_data = [];
45 +
46 + /**
47 + * Is modal window
48 + *
49 + * @var bool
50 + */
51 + private $modal;
52 +
53 + /**
54 + * Если true, то выводить общие сниппеты без привязки к пользователю
55 + *
56 + * @var bool
57 + */
58 + private $common;
59 +
60 + /**
61 + * @var array
62 + *
63 + * Array contains slug columns that you want hidden
64 + */
65 + private $hidden_columns = [
66 + 'id',
67 + ];
68 +
69 + /**
70 + * @var integer
71 + */
72 + private $per_page = 10;
73 +
74 + /** ************************************************************************
75 + * REQUIRED. Set up a constructor that references the parent constructor. We
76 + * use the parent reference to set some default configs.
77 + ***************************************************************************/
78 + /**
79 + * WINP_Snippet_Library_Table constructor.
80 + *
81 + * @param bool $modal
82 + */
83 + function __construct( $modal = false ) {
84 + global $status, $page;
85 + add_thickbox();
86 + $this->modal = $modal;
87 + $this->common = true;
88 +
89 + // Set parent defaults
90 + parent::__construct(
91 + [
92 + 'singular' => 'snippet', // singular name of the listed records
93 + 'plural' => 'snippets', // plural name of the listed records
94 + 'ajax' => true, // does this table support ajax?
95 + ]
96 + );
97 + }
98 +
99 + /** ************************************************************************
100 + * Recommended. This method is called when the parent class can't find a method
101 + * specifically build for a given column. Generally, it's recommended to include
102 + * one method for each column you want to render, keeping your package class
103 + * neat and organized. For example, if the class needs to process a column
104 + * named 'title', it would first see if a method named $this->column_title()
105 + * exists - if it does, that method will be used. If it doesn't, this one will
106 + * be used. Generally, you should try to use custom column methods as much as
107 + * possible.
108 + *
109 + * Since we have defined a column_title() method later on, this method doesn't
110 + * need to concern itself with any column with a name of 'title'. Instead, it
111 + * needs to handle everything else.
112 + *
113 + * For more detailed insight into how columns are handled, take a look at
114 + * WP_List_Table::single_row_columns()
115 + *
116 + * @param array $item A singular item (one full row's worth of data)
117 + * @param string $column_name The name/slug of the column to be processed
118 + *
119 + * @return string Text or HTML to be placed inside the column <td>
120 + **************************************************************************/
121 + public function column_default( $item, $column_name ) {
122 + switch ( $column_name ) {
123 + case 'type':
124 + $class = 'wbcr-inp-type-' . esc_attr( $item[ $column_name ] );
125 + $type = 'universal' == $item[ $column_name ] ? 'uni' : esc_attr( $item[ $column_name ] );
126 +
127 + return '<div class="wbcr-inp-snippet-type-label ' . $class . '">' . esc_html( $type ) . '</div>';
128 + case 'desc':
129 + $desc = strlen( $item[ $column_name ] ) > 500 ? substr( $item[ $column_name ], 0, 500 ) : $item[ $column_name ];
130 +
131 + return '<div class="wbcr-inp-snippet-description" title="' . esc_attr( $desc ) . '">' . esc_html( $desc ) . '</div>';
132 + case 'preview':
133 + case 'datetime':
134 + case 'insert':
135 + case 'delete':
136 + return $item[ $column_name ];
137 + default:
138 + return print_r( $item, true ); // Show the whole array for troubleshooting purposes
139 + }
140 + }
141 +
142 + /** ************************************************************************
143 + * Recommended. This is a custom column method and is responsible for what
144 + * is rendered in any column with a name/slug of 'title'. Every time the class
145 + * needs to render a column, it first looks for a method named
146 + * column_{$column_title} - if it exists, that method is run. If it doesn't
147 + * exist, column_default() is called instead.
148 + *
149 + * This example also illustrates how to implement rollover actions. Actions
150 + * should be an associative array formatted as 'slug'=>'link html' - and you
151 + * will need to generate the URLs yourself. You could even ensure the links
152 + *
153 + * @param array $item A singular item (one full row's worth of data)
154 + *
155 + * @return string Text to be placed inside the column <td> (movie title only)
156 + * *************************************************************************@see WP_List_Table::::single_row_columns()
157 + */
158 + public function column_title( $item ) {
159 + // Build row actions
160 + $actions = [/*
161 + 'edit' => sprintf( '<a href="?page=%s&action=%s&movie=%s">Edit</a>', $_REQUEST['page'], 'edit', $item['ID'] ),
162 + 'delete' => sprintf( '<a href="?page=%s&action=%s&movie=%s">Delete</a>', $_REQUEST['page'], 'delete', $item['ID'] ),*/
163 + ];
164 +
165 + $url = admin_url() . 'post-new.php?post_type=' . WINP_SNIPPETS_POST_TYPE . '&winp_item=' . $item['type'] . '&snippet_id=' . $item['ID'] . ( $this->common ? '&common=1' : '' );
166 +
167 + // Add premium badge if snippet is locked.
168 + $premium_badge = '';
169 + $is_locked = ! empty( $item['is_premium_locked'] );
170 +
171 + if ( $is_locked ) {
172 + $premium_badge = ' <span style="display:inline-block;background:#6366f1;color:#fff;font-size:11px;padding:2px 8px;border-radius:3px;font-weight:600;margin-left:6px;">PRO</span>';
173 + }
174 +
175 + // Return the title contents - no link for locked premium snippets.
176 + if ( $is_locked ) {
177 + return sprintf( '<b>%1$s</b>%2$s%3$s', /*$1%s*/ esc_html( $item['title'] ), /*$2%s*/ $premium_badge, /*$3%s*/ $this->row_actions( $actions ) );
178 + }
179 +
180 + return sprintf( '<a href="%1$s"><b>%2$s</b></a>%3$s%4$s', /*$1%s*/ esc_url( $url ), /*$2%s*/ esc_html( $item['title'] ), /*$3%s*/ $premium_badge, /*$4%s*/ $this->row_actions( $actions ) );
181 + }
182 +
183 + /** ************************************************************************
184 + * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column
185 + * is given special treatment when columns are processed. It ALWAYS needs to
186 + * have it's own method.
187 + *
188 + * @param array $item A singular item (one full row's worth of data)
189 + *
190 + * @return string Text to be placed inside the column <td> (movie title only)
191 + * *************************************************************************@see WP_List_Table::::single_row_columns()
192 + */
193 + public function column_cb( $item ) {
194 + return sprintf(
195 + '<input type="checkbox" name="%1$s[]" value="%2$s" />', /*$1%s*/
196 + esc_attr( $this->_args['singular'] ), // Let's simply repurpose the table's singular label ("movie")
197 + /*$2%s*/ esc_attr( $item['ID'] ) // The value of the checkbox should be the record's id
198 + );
199 + }
200 +
201 + /** ************************************************************************
202 + * REQUIRED! This method dictates the table's columns and titles. This should
203 + * return an array where the key is the column slug (and class) and the value
204 + * is the column's title text. If you need a checkbox for bulk actions, refer
205 + * to the $columns array below.
206 + *
207 + * The 'cb' column is treated differently than the rest. If including a checkbox
208 + * column in your table you must create a column_cb() method. If you don't need
209 + * bulk actions or checkboxes, simply leave the 'cb' entry out of your array.
210 + *
211 + * @return array An associative array containing column information: 'slugs'=>'Visible Titles'
212 + * *************************************************************************@see WP_List_Table::::single_row_columns()
213 + */
214 + public function get_columns() {
215 + $columns = [// 'cb' => '<input type="checkbox" />', //Render a checkbox instead of text
216 + ];
217 + $columns['type'] = __( 'Type', 'insert-php' );
218 + $columns['title'] = __( 'Title', 'insert-php' );
219 +
220 + if ( ! $this->modal && $this->common ) {
221 + $columns['preview'] = __( 'Preview', 'insert-php' );
222 + }
223 +
224 + $columns['desc'] = __( 'Description', 'insert-php' );
225 + $columns['datetime'] = __( 'Date', 'insert-php' );
226 + $columns['insert'] = __( 'Insert', 'insert-php' );
227 +
228 + if ( ! $this->modal && ! $this->common ) {
229 + $columns['delete'] = __( 'Delete', 'insert-php' );
230 + }
231 +
232 + return $columns;
233 + }
234 +
235 + /** ************************************************************************
236 + * Optional. If you want one or more columns to be sortable (ASC/DESC toggle),
237 + * you will need to register it here. This should return an array where the
238 + * key is the column that needs to be sortable, and the value is db column to
239 + * sort by. Often, the key and value will be the same, but this is not always
240 + * the case (as the value is a column name from the database, not the list table).
241 + *
242 + * This method merely defines which columns should be sortable and makes them
243 + * clickable - it does not handle the actual sorting. You still need to detect
244 + * the ORDERBY and ORDER querystring variables within prepare_items() and sort
245 + * your data accordingly (usually by modifying your query).
246 + *
247 + * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool)
248 + **************************************************************************/
249 + public function get_sortable_columns() {
250 + $sortable_columns = [
251 + 'title' => [ 'title', false ], // true means it's already sorted
252 + 'type' => [ 'type', false ],
253 + 'datetime' => [ 'datetime', false ],
254 + ];
255 +
256 + return $sortable_columns;
257 + }
258 +
259 + /** ************************************************************************
260 + * Optional. If you need to include bulk actions in your list table, this is
261 + * the place to define them. Bulk actions are an associative array in the format
262 + * 'slug'=>'Visible Title'
263 + *
264 + * If this method returns an empty value, no bulk action will be rendered. If
265 + * you specify any bulk actions, the bulk actions box will be rendered with
266 + * the table automatically on display().
267 + *
268 + * Also note that list tables are not automatically wrapped in <form> elements,
269 + * so you will need to create those manually in order for bulk actions to function.
270 + *
271 + * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles'
272 + **************************************************************************/
273 + public function get_bulk_actions() {
274 + $actions = [// 'sync' => __( 'Synchronization', 'insert-php' ),
275 + ];
276 +
277 + return $actions;
278 + }
279 +
280 + /** ************************************************************************
281 + * Optional. You can handle your bulk actions anywhere or anyhow you prefer.
282 + * For this example package, we will handle it in the class to keep things
283 + * clean and organized.
284 + *
285 + * @see $this->prepare_items()
286 + **************************************************************************/
287 + public function process_bulk_action() {
288 +
289 + // Detect when a bulk action is being triggered...
290 + /*
291 + if ( 'sync' === $this->current_action() ) {
292 + wp_die( 'Synchronization' );
293 + }*/
294 + }
295 +
296 + /**
297 + * Возвращает id youtube видео
298 + *
299 + * @param $video_link - ссылка на видео
300 + *
301 + * @return bool|string - если id сниппета не найден, то вернёт false
302 + */
303 + private function get_video_id( $video_link ) {
304 + // youtube regex
305 + preg_match( '#([\/|\?|&]vi?[\/|=]|youtu\.be\/|embed\/)([a-zA-Z0-9_-]+)#', $video_link, $matches );
306 +
307 + return ! empty( $matches ) ? end( $matches ) : false;
308 + }
309 +
310 + /**
311 + * Get snippets data
312 + *
313 + * @return array
314 + */
315 + public function get_data() {
316 + $data = [];
317 + $saved_data = [];
318 +
319 + $orderby = WINP_HTTP::request( 'orderby', 'datetime', true );
320 + $order = WINP_HTTP::request( 'order', 'desc', true );
321 + $paged = WINP_HTTP::request( 'paged', 1, 'intval' );
322 +
323 + $order_tags = [
324 + 'title' => 'title',
325 + 'type' => 'type_id',
326 + 'datetime' => 'updated_at',
327 + ];
328 +
329 + $args = [
330 + 'per-page=' . $this->per_page,
331 + 'page=' . $paged,
332 + 'sort=' . ( 'asc' == $order ? '' : '-' ) . ( $order_tags[ $orderby ] ),
333 + ];
334 +
335 + $snippets = WINP_Plugin::app()->get_api_object()->get_all_snippets( $this->common, $args );
336 +
337 + if ( ! empty( $snippets ) ) {
338 + foreach ( (array) $snippets as $snippet ) {
339 + // Check if snippet is premium and user doesn't have license.
340 + $is_premium_locked = $this->common &&
341 + isset( $snippet->is_premium ) &&
342 + $snippet->is_premium &&
343 + ! WINP_Plugin::app()->get_api_object()->is_key();
344 +
345 + // Build Insert button - use different class for premium locked snippets.
346 + if ( $is_premium_locked ) {
347 + $insert_button = '<a class="wbcr-inp-premium-snippet-button button" href="javascript: void(0)" style="display:inline-flex;align-items:center;justify-content:center;"><span class="dashicons dashicons-plus"></span></a>';
348 + } else {
349 + $insert_button = '<a class="wbcr-inp-enable-snippet-button button" data-snippet="' . esc_attr( $snippet->id ) . '" data-common="' . ( $this->common ? 1 : 0 ) . '" href="javascript: void(0)" style="display:inline-flex;align-items:center;justify-content:center;"><span class="dashicons dashicons-plus"></span></a>';
350 + }
351 +
352 + $_data = [
353 + 'ID' => (int) $snippet->id,
354 + 'title' => esc_html( $snippet->title ),
355 + 'desc' => esc_html( $snippet->description ),
356 + 'type' => $snippet->type->title,
357 + 'datetime' => gmdate( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $snippet->updated_at ),
358 + 'insert' => $insert_button,
359 + 'delete' => '<a class="wbcr-inp-delete-snippet-button button" data-snippet="' . esc_attr( $snippet->id ) . '" href="javascript: void(0)"><span class="dashicons dashicons-no"></span></a>',
360 + 'is_premium_locked' => $is_premium_locked,
361 + ];
362 +
363 + if ( $this->common ) {
364 + $_data['preview'] = '';
365 + }
366 +
367 + $video_id = $this->get_video_id( $snippet->video_link );
368 +
369 + if ( $video_id ) {
370 + $_data['preview'] = '<a class="thickbox" href="https://www.youtube.com/embed/' . esc_attr( $video_id ) . '?autoplay=1&rel=0&TB_iframe=true"><img src="' . WINP_PLUGIN_URL . '/admin/assets/img/video.png" class="winp-library-image-preview" data-videoid="' . esc_attr( $video_id ) . '" alt="' . __( 'Watch Tutorial Video', 'insert-php' ) . '"></a>';
371 + }
372 +
373 + $data[] = $_data;
374 +
375 + $saved_data[ $snippet->id ] = [
376 + 'title' => esc_html( $snippet->title ),
377 + 'desc' => esc_html( $snippet->description ),
378 + 'type' => $snippet->type->slug,
379 + 'content' => $snippet->content,
380 + 'type_id' => (int) $snippet->type_id,
381 + 'scope' => $snippet->execute_everywhere ? 'evrywhere' : 'shortcode',
382 + ];
383 + }
384 +
385 + update_user_meta( get_current_user_id(), 'wbcr_inp_current_snippets', $saved_data );
386 + }
387 +
388 + return $data;
389 + }
390 +
391 + /**
392 + * Get total items for last query
393 + *
394 + * @return int
395 + */
396 + public function get_total_items() {
397 + return WINP_Plugin::app()->get_api_object()->get_total_items();
398 + }
399 +
400 + /** ************************************************************************
401 + * REQUIRED! This is where you prepare your data for display. This method will
402 + * usually be used to query the database, sort and filter the data, and generally
403 + * get it ready to be displayed. At a minimum, we should set $this->items and
404 + * $this->set_pagination_args(), although the following properties and methods
405 + * are frequently interacted with here...
406 + *
407 + * @param bool $common - если true, то выводить общие сниппеты без привязки к пользователю
408 + *
409 + * @global WPDB $wpdb
410 + * @uses $this->_column_headers
411 + * @uses $this->items
412 + * @uses $this->get_columns()
413 + * @uses $this->get_sortable_columns()
414 + * @uses $this->get_pagenum()
415 + * @uses $this->set_pagination_args()
416 + * *************************************************************************/
417 + public function prepare_items( $common = false ) {
418 + /**
419 + * First, lets decide how many records per page to show
420 + */
421 + $this->per_page = 10;
422 +
423 + /**
424 + * @param bool $common - если true, то выводить общие сниппеты без привязки к пользователю
425 + */
426 + $this->common = $common;
427 +
428 + /**
429 + * REQUIRED. Now we need to define our column headers. This includes a complete
430 + * array of columns to be displayed (slugs & titles), a list of columns
431 + * to keep hidden, and a list of columns that are sortable. Each of these
432 + * can be defined in another method (as we've done here) before being
433 + * used to build the value for our _column_headers property.
434 + */
435 + $columns = $this->get_columns();
436 + $hidden = $this->hidden_columns;
437 + $sortable = $this->get_sortable_columns();
438 +
439 + /**
440 + * REQUIRED. Finally, we build an array to be used by the class for column
441 + * headers. The $this->_column_headers property takes an array which contains
442 + * 3 other arrays. One for all columns, one for hidden columns, and one
443 + * for sortable columns.
444 + */
445 + $this->_column_headers = [ $columns, $hidden, $sortable ];
446 +
447 + /**
448 + * Optional. You can handle your bulk actions however you see fit. In this
449 + * case, we'll handle them within our package just to keep things clean.
450 + */
451 + $this->process_bulk_action();
452 +
453 + /**
454 + * Instead of querying a database, we're going to fetch the example data
455 + * property we created for use in this plugin. This makes this example
456 + * package slightly different than one you might build on your own. In
457 + * this example, we'll be using array manipulation to sort and paginate
458 + * our data. In a real-world implementation, you will probably want to
459 + * use sort and pagination data to build a custom query instead, as you'll
460 + * be able to use your precisely-queried data immediately.
461 + */
462 + $data = $this->get_data();
463 +
464 + /**
465 + * This checks for sorting input and sorts the data in our array accordingly.
466 + *
467 + * In a real-world situation involving a database, you would probably want
468 + * to handle sorting by passing the 'orderby' and 'order' values directly
469 + * to a custom query. The returned data will be pre-sorted, and this array
470 + * sorting technique would be unnecessary.
471 + *
472 + * @param $a
473 + * @param $b
474 + *
475 + * @return int
476 + */ /*
477 + function usort_reorder( $a, $b ) {
478 + $orderby = ( ! empty( $_REQUEST['orderby'] ) ) ? $_REQUEST['orderby'] : 'title'; // If no sort, default to title
479 + $order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc'; // If no order, default to asc
480 + $result = strcmp( $a[ $orderby ], $b[ $orderby ] ); // Determine sort order
481 +
482 + return ( 'asc' === $order ) ? $result : - $result; // Send final sort direction to usort
483 + }
484 +
485 + usort( $data, 'usort_reorder' );*/
486 +
487 + /***********************************************************************
488 + * ---------------------------------------------------------------------
489 + * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
490 + *
491 + * In a real-world situation, this is where you would place your query.
492 + *
493 + * For information on making queries in WordPress, see this Codex entry:
494 + * http://codex.wordpress.org/Class_Reference/wpdb
495 + *
496 + * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
497 + * ---------------------------------------------------------------------
498 + */
499 +
500 + /**
501 + * REQUIRED for pagination. Let's figure out what page the user is currently
502 + * looking at. We'll need this later, so you should always include it in
503 + * your own package classes.
504 + */ // $current_page = $this->get_current_page();
505 +
506 + /**
507 + * REQUIRED for pagination. Let's check how many items are in our data array.
508 + * In real-world use, this would be the total number of items in your database,
509 + * without filtering. We'll need this later, so you should always include it
510 + * in your own package classes.
511 + */
512 + $total_items = $this->get_total_items();
513 +
514 + /**
515 + * The WP_List_Table class does not handle pagination for us, so we need
516 + * to ensure that the data is trimmed to only the current page. We can use
517 + * array_slice() to
518 + */ // $data = array_slice( $data, ( ( $current_page - 1 ) * $this->per_page ), $this->per_page );
519 +
520 + /**
521 + * REQUIRED. Now we can add our *sorted* data to the items property, where
522 + * it can be used by the rest of the class.
523 + */
524 + $this->items = $data;
525 +
526 + /**
527 + * REQUIRED. We also have to register our pagination options & calculations.
528 + */
529 + $this->set_pagination_args(
530 + [
531 + 'total_items' => $total_items,
532 + 'per_page' => $this->per_page,
533 + 'total_pages' => ceil( $total_items / $this->per_page ),
534 + 'orderby' => WINP_HTTP::request( 'orderby', 'title', true ),
535 + 'order' => WINP_HTTP::request( 'order', 'asc', true ),
536 + ]
537 + );
538 + }
539 +
540 + /**
541 + * @Override of display method
542 + */
543 + public function display() {
544 + /**
545 + * Adds a nonce field
546 + */
547 + wp_nonce_field( 'winp-ajax-custom-list-nonce', 'winp_ajax_custom_list_nonce' );
548 +
549 + if ( ! empty( $this->items ) && ! $this->common ) {
550 + foreach ( $this->items as $item ) {
551 + wp_nonce_field( 'winp-ajax-snippet-delete-' . $item['ID'], 'winp_ajax_snippet_delete_' . $item['ID'] );
552 + }
553 + }
554 +
555 + /**
556 + * Adds field order and orderby
557 + */
558 + echo '<input type="hidden" id="order" name="order" value="' . $this->_pagination_args['order'] . '" />';
559 + echo '<input type="hidden" id="orderby" name="orderby" value="' . $this->_pagination_args['orderby'] . '" />';
560 + parent::display();
561 +
562 + // Add premium upsell modal.
563 + if ( $this->common ) {
564 + ?>
565 + <div id="winp-premium-snippet-modal" style="display:none;">
566 + <div style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.7);z-index:100000;display:flex;align-items:center;justify-content:center;">
567 + <div style="background:#fff;border-radius:8px;padding:0;max-width:500px;width:90%;position:relative;">
568 + <button id="winp-modal-close" style="position:absolute;top:15px;right:15px;background:none;border:none;font-size:24px;cursor:pointer;color:#666;line-height:1;padding:0;width:30px;height:30px;">&times;</button>
569 + <div class="winp-upsell-container" style="margin:0;padding:0;">
570 + <div class="winp-upsell-card" style="box-shadow:none;">
571 + <div class="winp-upsell-icon">
572 + <span class="dashicons dashicons-star-filled"></span>
573 + </div>
574 + <div class="winp-upsell-title">
575 + <?php esc_html_e( 'Premium Snippet', 'insert-php' ); ?>
576 + </div>
577 + <p class="winp-upsell-badge">
578 + <?php esc_html_e( 'Pro feature', 'insert-php' ); ?>
579 + </p>
580 + <p class="winp-upsell-description">
581 + <?php esc_html_e( 'This is a premium snippet available only in the Pro version. Upgrade to unlock access to all premium code snippets and advanced features.', 'insert-php' ); ?>
582 + </p>
583 + <a href="<?php echo esc_url( tsdk_utmify( WINP_UPGRADE, 'snippet_library', 'premium_snippet_upsell' ) ); ?>" class="button button-primary button-large winp-upsell-button" target="_blank">
584 + <?php esc_html_e( 'Upgrade to Pro', 'insert-php' ); ?>
585 + </a>
586 + </div>
587 + </div>
588 + </div>
589 + </div>
590 + </div>
591 + <?php
592 + }
593 + }
594 +
595 + /**
596 + * @Override ajax_response method
597 + */
598 + public function ajax_response() {
599 +
600 + $this->prepare_items();
601 + extract( $this->_args );
602 + extract( $this->_pagination_args, EXTR_SKIP );
603 + ob_start();
604 + $no_placeholder = WINP_HTTP::request( 'no_placeholder', '' );
605 + if ( ! empty( $no_placeholder ) ) {
606 + $this->display_rows();
607 + } else {
608 + $this->display_rows_or_placeholder();
609 + }
610 + $rows = ob_get_clean();
611 + ob_start();
612 + $this->print_column_headers();
613 + $headers = ob_get_clean();
614 + ob_start();
615 + $this->pagination( 'top' );
616 + $pagination_top = ob_get_clean();
617 + ob_start();
618 + $this->pagination( 'bottom' );
619 + $pagination_bottom = ob_get_clean();
620 + $response = [ 'rows' => $rows ];
621 + $response['pagination']['top'] = $pagination_top;
622 + $response['pagination']['bottom'] = $pagination_bottom;
623 + $response['column_headers'] = $headers;
624 + if ( isset( $total_items ) ) {
625 + /* translators: %s: Number of items */
626 + $response['total_items_i18n'] = sprintf( _n( '%s item', '%s items', $total_items, 'insert-php' ), number_format_i18n( $total_items ) );
627 + }
628 + if ( isset( $total_pages ) ) {
629 + $response['total_pages'] = $total_pages;
630 + $response['total_pages_i18n'] = number_format_i18n( $total_pages );
631 + }
632 + die( json_encode( $response ) );
633 + }
634 +}