| @@ -1,572 +1,634 @@ | ||
| 1 | -<?php | |
| 2 | -/** | |
| 3 | - * This class is implemented page: snippet table | |
| 4 | - * | |
| 5 | - * @author Webcraftic <wordpress.webraftic@gmail.com> | |
| 6 | - * @since 1.0.0 | |
| 7 | - * @package core | |
| 8 | - * @copyright (c) 2019, OnePress Ltd | |
| 9 | - * s | |
| 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 = []; | |
| 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 = [ | |
| 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 | - add_thickbox(); | |
| 90 | - $this->modal = $modal; | |
| 91 | - $this->common = true; | |
| 92 | - | |
| 93 | - // Set parent defaults | |
| 94 | - parent::__construct( [ | |
| 95 | - 'singular' => 'snippet', // singular name of the listed records | |
| 96 | - 'plural' => 'snippets', // plural name of the listed records | |
| 97 | - 'ajax' => true, // does this table support ajax? | |
| 98 | - ] ); | |
| 99 | - } | |
| 100 | - | |
| 101 | - /** ************************************************************************ | |
| 102 | - * Recommended. This method is called when the parent class can't find a method | |
| 103 | - * specifically build for a given column. Generally, it's recommended to include | |
| 104 | - * one method for each column you want to render, keeping your package class | |
| 105 | - * neat and organized. For example, if the class needs to process a column | |
| 106 | - * named 'title', it would first see if a method named $this->column_title() | |
| 107 | - * exists - if it does, that method will be used. If it doesn't, this one will | |
| 108 | - * be used. Generally, you should try to use custom column methods as much as | |
| 109 | - * possible. | |
| 110 | - * | |
| 111 | - * Since we have defined a column_title() method later on, this method doesn't | |
| 112 | - * need to concern itself with any column with a name of 'title'. Instead, it | |
| 113 | - * needs to handle everything else. | |
| 114 | - * | |
| 115 | - * For more detailed insight into how columns are handled, take a look at | |
| 116 | - * WP_List_Table::single_row_columns() | |
| 117 | - * | |
| 118 | - * @param array $item A singular item (one full row's worth of data) | |
| 119 | - * @param string $column_name The name/slug of the column to be processed | |
| 120 | - * | |
| 121 | - * @return string Text or HTML to be placed inside the column <td> | |
| 122 | - **************************************************************************/ | |
| 123 | - public function column_default( $item, $column_name ) { | |
| 124 | - switch ( $column_name ) { | |
| 125 | - case 'type': | |
| 126 | - $class = 'wbcr-inp-type-' . esc_attr( $item[ $column_name ] ); | |
| 127 | - $type = 'universal' == $item[ $column_name ] ? 'uni' : esc_attr( $item[ $column_name ] ); | |
| 128 | - | |
| 129 | - return '<div class="wbcr-inp-snippet-type-label ' . $class . '">' . esc_html( $type ) . '</div>'; | |
| 130 | - case 'desc': | |
| 131 | - $desc = strlen( $item[ $column_name ] ) > 500 ? substr( $item[ $column_name ], 0, 500 ) : $item[ $column_name ]; | |
| 132 | - | |
| 133 | - return '<div class="wbcr-inp-snippet-description" title="' . esc_attr( $desc ) . '">' . esc_html( $desc ) . '</div>'; | |
| 134 | - case 'preview': | |
| 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 | - * @param array $item A singular item (one full row's worth of data) | |
| 157 | - * | |
| 158 | - * @return string Text to be placed inside the column <td> (movie title only) | |
| 159 | - **************************************************************************@see WP_List_Table::::single_row_columns() | |
| 160 | - */ | |
| 161 | - public function column_title( $item ) { | |
| 162 | - //Build row actions | |
| 163 | - $actions = [/*'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 | - | |
| 169 | - //Return the title contents | |
| 170 | - return sprintf( '<a href="%1$s"><b>%2$s</b></a>%3$s', /*$1%s*/ esc_url( $url ), /*$2%s*/ esc_html( $item['title'] ), /*$3%s*/ $this->row_actions( $actions ) ); | |
| 171 | - } | |
| 172 | - | |
| 173 | - /** ************************************************************************ | |
| 174 | - * REQUIRED if displaying checkboxes or using bulk actions! The 'cb' column | |
| 175 | - * is given special treatment when columns are processed. It ALWAYS needs to | |
| 176 | - * have it's own method. | |
| 177 | - * | |
| 178 | - * @param array $item A singular item (one full row's worth of data) | |
| 179 | - * | |
| 180 | - * @return string Text to be placed inside the column <td> (movie title only) | |
| 181 | - **************************************************************************@see WP_List_Table::::single_row_columns() | |
| 182 | - */ | |
| 183 | - public function column_cb( $item ) { | |
| 184 | - return sprintf( '<input type="checkbox" name="%1$s[]" value="%2$s" />', /*$1%s*/ esc_attr( $this->_args['singular'] ), //Let's simply repurpose the table's singular label ("movie") | |
| 185 | - /*$2%s*/ esc_attr( $item['ID'] ) //The value of the checkbox should be the record's id | |
| 186 | - ); | |
| 187 | - } | |
| 188 | - | |
| 189 | - /** ************************************************************************ | |
| 190 | - * REQUIRED! This method dictates the table's columns and titles. This should | |
| 191 | - * return an array where the key is the column slug (and class) and the value | |
| 192 | - * is the column's title text. If you need a checkbox for bulk actions, refer | |
| 193 | - * to the $columns array below. | |
| 194 | - * | |
| 195 | - * The 'cb' column is treated differently than the rest. If including a checkbox | |
| 196 | - * column in your table you must create a column_cb() method. If you don't need | |
| 197 | - * bulk actions or checkboxes, simply leave the 'cb' entry out of your array. | |
| 198 | - * | |
| 199 | - * @return array An associative array containing column information: 'slugs'=>'Visible Titles' | |
| 200 | - **************************************************************************@see WP_List_Table::::single_row_columns() | |
| 201 | - */ | |
| 202 | - public function get_columns() { | |
| 203 | - $columns = [// 'cb' => '<input type="checkbox" />', //Render a checkbox instead of text | |
| 204 | - ]; | |
| 205 | - $columns['type'] = __( 'Type', 'insert-php' ); | |
| 206 | - $columns['title'] = __( 'Title', 'insert-php' ); | |
| 207 | - | |
| 208 | - if ( ! $this->modal && $this->common ) { | |
| 209 | - $columns['preview'] = __( 'Preview', 'insert-php' ); | |
| 210 | - } | |
| 211 | - | |
| 212 | - $columns['desc'] = __( 'Description', 'insert-php' ); | |
| 213 | - $columns['datetime'] = __( 'Date', 'insert-php' ); | |
| 214 | - $columns['insert'] = __( 'Insert', 'insert-php' ); | |
| 215 | - | |
| 216 | - if ( ! $this->modal && ! $this->common ) { | |
| 217 | - $columns['delete'] = __( 'Delete', 'insert-php' ); | |
| 218 | - } | |
| 219 | - | |
| 220 | - return $columns; | |
| 221 | - } | |
| 222 | - | |
| 223 | - /** ************************************************************************ | |
| 224 | - * Optional. If you want one or more columns to be sortable (ASC/DESC toggle), | |
| 225 | - * you will need to register it here. This should return an array where the | |
| 226 | - * key is the column that needs to be sortable, and the value is db column to | |
| 227 | - * sort by. Often, the key and value will be the same, but this is not always | |
| 228 | - * the case (as the value is a column name from the database, not the list table). | |
| 229 | - * | |
| 230 | - * This method merely defines which columns should be sortable and makes them | |
| 231 | - * clickable - it does not handle the actual sorting. You still need to detect | |
| 232 | - * the ORDERBY and ORDER querystring variables within prepare_items() and sort | |
| 233 | - * your data accordingly (usually by modifying your query). | |
| 234 | - * | |
| 235 | - * @return array An associative array containing all the columns that should be sortable: 'slugs'=>array('data_values',bool) | |
| 236 | - **************************************************************************/ | |
| 237 | - public function get_sortable_columns() { | |
| 238 | - $sortable_columns = [ | |
| 239 | - 'title' => [ 'title', false ], //true means it's already sorted | |
| 240 | - 'type' => [ 'type', false ], | |
| 241 | - 'datetime' => [ 'datetime', false ], | |
| 242 | - ]; | |
| 243 | - | |
| 244 | - return $sortable_columns; | |
| 245 | - } | |
| 246 | - | |
| 247 | - /** ************************************************************************ | |
| 248 | - * Optional. If you need to include bulk actions in your list table, this is | |
| 249 | - * the place to define them. Bulk actions are an associative array in the format | |
| 250 | - * 'slug'=>'Visible Title' | |
| 251 | - * | |
| 252 | - * If this method returns an empty value, no bulk action will be rendered. If | |
| 253 | - * you specify any bulk actions, the bulk actions box will be rendered with | |
| 254 | - * the table automatically on display(). | |
| 255 | - * | |
| 256 | - * Also note that list tables are not automatically wrapped in <form> elements, | |
| 257 | - * so you will need to create those manually in order for bulk actions to function. | |
| 258 | - * | |
| 259 | - * @return array An associative array containing all the bulk actions: 'slugs'=>'Visible Titles' | |
| 260 | - **************************************************************************/ | |
| 261 | - public function get_bulk_actions() { | |
| 262 | - $actions = [//'sync' => __( 'Synchronization', 'insert-php' ), | |
| 263 | - ]; | |
| 264 | - | |
| 265 | - return $actions; | |
| 266 | - } | |
| 267 | - | |
| 268 | - /** ************************************************************************ | |
| 269 | - * Optional. You can handle your bulk actions anywhere or anyhow you prefer. | |
| 270 | - * For this example package, we will handle it in the class to keep things | |
| 271 | - * clean and organized. | |
| 272 | - * | |
| 273 | - * @see $this->prepare_items() | |
| 274 | - **************************************************************************/ | |
| 275 | - public function process_bulk_action() { | |
| 276 | - | |
| 277 | - //Detect when a bulk action is being triggered... | |
| 278 | - /*if ( 'sync' === $this->current_action() ) { | |
| 279 | - wp_die( 'Synchronization' ); | |
| 280 | - }*/ | |
| 281 | - } | |
| 282 | - | |
| 283 | - /** | |
| 284 | - * Возвращает id youtube видео | |
| 285 | - * | |
| 286 | - * @param $video_link - ссылка на видео | |
| 287 | - * | |
| 288 | - * @return bool|string - если id сниппета не найден, то вернёт false | |
| 289 | - */ | |
| 290 | - private function get_video_id( $video_link ) { | |
| 291 | - // youtube regex | |
| 292 | - preg_match( "#([\/|\?|&]vi?[\/|=]|youtu\.be\/|embed\/)([a-zA-Z0-9_-]+)#", $video_link, $matches ); | |
| 293 | - | |
| 294 | - return ! empty( $matches ) ? end( $matches ) : false; | |
| 295 | - } | |
| 296 | - | |
| 297 | - /** | |
| 298 | - * Get snippets data | |
| 299 | - * | |
| 300 | - * @return array | |
| 301 | - */ | |
| 302 | - public function get_data() { | |
| 303 | - $data = []; | |
| 304 | - $saved_data = []; | |
| 305 | - | |
| 306 | - $orderby = WINP_Plugin::app()->request->request( 'orderby', 'datetime', true ); | |
| 307 | - $order = WINP_Plugin::app()->request->request( 'order', 'desc', true ); | |
| 308 | - $paged = WINP_Plugin::app()->request->request( 'paged', 1, 'intval' ); | |
| 309 | - | |
| 310 | - $order_tags = [ | |
| 311 | - 'title' => 'title', | |
| 312 | - 'type' => 'type_id', | |
| 313 | - 'datetime' => 'updated_at', | |
| 314 | - ]; | |
| 315 | - | |
| 316 | - $args = [ | |
| 317 | - 'per-page=' . $this->per_page, | |
| 318 | - 'page=' . $paged, | |
| 319 | - 'sort=' . ( 'asc' == $order ? '' : '-' ) . ( $order_tags[ $orderby ] ), | |
| 320 | - ]; | |
| 321 | - | |
| 322 | - $snippets = WINP_Plugin::app()->get_api_object()->get_all_snippets( $this->common, $args ); | |
| 323 | - | |
| 324 | - if ( ! empty( $snippets ) ) { | |
| 325 | - foreach ( (array) $snippets as $snippet ) { | |
| 326 | - $_data = [ | |
| 327 | - 'ID' => (int) $snippet->id, | |
| 328 | - 'title' => esc_html( $snippet->title ), | |
| 329 | - 'desc' => esc_html( $snippet->description ), | |
| 330 | - 'type' => $snippet->type->title, | |
| 331 | - 'datetime' => date( get_option( 'date_format' ) . ' ' . get_option( 'time_format' ), $snippet->updated_at ), | |
| 332 | - 'insert' => '<a class="wbcr-inp-enable-snippet-button button" data-snippet="' . esc_attr( $snippet->id ) . '" data-common="' . ( $this->common ? 1 : 0 ) . '" href="javascript: void(0)"><span class="dashicons dashicons-plus"></span></a>', | |
| 333 | - '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>', | |
| 334 | - ]; | |
| 335 | - | |
| 336 | - if ( $this->common ) { | |
| 337 | - $_data['preview'] = ''; | |
| 338 | - } | |
| 339 | - | |
| 340 | - $video_id = $this->get_video_id( $snippet->video_link ); | |
| 341 | - | |
| 342 | - if ( $video_id ) { | |
| 343 | - $_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="' . __( 'View the video', 'insert-php' ) . '">' . '</a>'; | |
| 344 | - } | |
| 345 | - | |
| 346 | - $data[] = $_data; | |
| 347 | - | |
| 348 | - $saved_data[ $snippet->id ] = [ | |
| 349 | - 'title' => esc_html( $snippet->title ), | |
| 350 | - 'desc' => esc_html( $snippet->description ), | |
| 351 | - 'type' => $snippet->type->slug, | |
| 352 | - 'content' => $snippet->content, | |
| 353 | - 'type_id' => (int) $snippet->type_id, | |
| 354 | - 'scope' => $snippet->execute_everywhere ? 'evrywhere' : 'shortcode', | |
| 355 | - ]; | |
| 356 | - } | |
| 357 | - | |
| 358 | - update_user_meta( get_current_user_id(), WINP_Plugin::app()->getPrefix() . 'current_snippets', $saved_data ); | |
| 359 | - } | |
| 360 | - | |
| 361 | - return $data; | |
| 362 | - } | |
| 363 | - | |
| 364 | - /** | |
| 365 | - * Get total items for last query | |
| 366 | - * | |
| 367 | - * @return int | |
| 368 | - */ | |
| 369 | - public function get_total_items() { | |
| 370 | - return WINP_Plugin::app()->get_api_object()->get_total_items(); | |
| 371 | - } | |
| 372 | - | |
| 373 | - /** ************************************************************************ | |
| 374 | - * REQUIRED! This is where you prepare your data for display. This method will | |
| 375 | - * usually be used to query the database, sort and filter the data, and generally | |
| 376 | - * get it ready to be displayed. At a minimum, we should set $this->items and | |
| 377 | - * $this->set_pagination_args(), although the following properties and methods | |
| 378 | - * are frequently interacted with here... | |
| 379 | - * | |
| 380 | - * @param bool $common - если true, то выводить общие сниппеты без привязки к пользователю | |
| 381 | - * | |
| 382 | - * @global WPDB $wpdb | |
| 383 | - * @uses $this->_column_headers | |
| 384 | - * @uses $this->items | |
| 385 | - * @uses $this->get_columns() | |
| 386 | - * @uses $this->get_sortable_columns() | |
| 387 | - * @uses $this->get_pagenum() | |
| 388 | - * @uses $this->set_pagination_args() | |
| 389 | - * *************************************************************************/ | |
| 390 | - public function prepare_items( $common = false ) { | |
| 391 | - /** | |
| 392 | - * First, lets decide how many records per page to show | |
| 393 | - */ | |
| 394 | - $this->per_page = 10; | |
| 395 | - | |
| 396 | - /** | |
| 397 | - * @param bool $common - если true, то выводить общие сниппеты без привязки к пользователю | |
| 398 | - */ | |
| 399 | - $this->common = $common; | |
| 400 | - | |
| 401 | - /** | |
| 402 | - * REQUIRED. Now we need to define our column headers. This includes a complete | |
| 403 | - * array of columns to be displayed (slugs & titles), a list of columns | |
| 404 | - * to keep hidden, and a list of columns that are sortable. Each of these | |
| 405 | - * can be defined in another method (as we've done here) before being | |
| 406 | - * used to build the value for our _column_headers property. | |
| 407 | - */ | |
| 408 | - $columns = $this->get_columns(); | |
| 409 | - $hidden = $this->hidden_columns; | |
| 410 | - $sortable = $this->get_sortable_columns(); | |
| 411 | - | |
| 412 | - /** | |
| 413 | - * REQUIRED. Finally, we build an array to be used by the class for column | |
| 414 | - * headers. The $this->_column_headers property takes an array which contains | |
| 415 | - * 3 other arrays. One for all columns, one for hidden columns, and one | |
| 416 | - * for sortable columns. | |
| 417 | - */ | |
| 418 | - $this->_column_headers = [ $columns, $hidden, $sortable ]; | |
| 419 | - | |
| 420 | - /** | |
| 421 | - * Optional. You can handle your bulk actions however you see fit. In this | |
| 422 | - * case, we'll handle them within our package just to keep things clean. | |
| 423 | - */ | |
| 424 | - $this->process_bulk_action(); | |
| 425 | - | |
| 426 | - /** | |
| 427 | - * Instead of querying a database, we're going to fetch the example data | |
| 428 | - * property we created for use in this plugin. This makes this example | |
| 429 | - * package slightly different than one you might build on your own. In | |
| 430 | - * this example, we'll be using array manipulation to sort and paginate | |
| 431 | - * our data. In a real-world implementation, you will probably want to | |
| 432 | - * use sort and pagination data to build a custom query instead, as you'll | |
| 433 | - * be able to use your precisely-queried data immediately. | |
| 434 | - */ | |
| 435 | - $data = $this->get_data(); | |
| 436 | - | |
| 437 | - /** | |
| 438 | - * This checks for sorting input and sorts the data in our array accordingly. | |
| 439 | - * | |
| 440 | - * In a real-world situation involving a database, you would probably want | |
| 441 | - * to handle sorting by passing the 'orderby' and 'order' values directly | |
| 442 | - * to a custom query. The returned data will be pre-sorted, and this array | |
| 443 | - * sorting technique would be unnecessary. | |
| 444 | - * | |
| 445 | - * @param $a | |
| 446 | - * @param $b | |
| 447 | - * | |
| 448 | - * @return int | |
| 449 | - */ /*function usort_reorder( $a, $b ) { | |
| 450 | - $orderby = ( ! empty( $_REQUEST['orderby'] ) ) ? $_REQUEST['orderby'] : 'title'; // If no sort, default to title | |
| 451 | - $order = ( ! empty( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : 'asc'; // If no order, default to asc | |
| 452 | - $result = strcmp( $a[ $orderby ], $b[ $orderby ] ); // Determine sort order | |
| 453 | - | |
| 454 | - return ( 'asc' === $order ) ? $result : - $result; // Send final sort direction to usort | |
| 455 | - } | |
| 456 | - | |
| 457 | - usort( $data, 'usort_reorder' );*/ | |
| 458 | - | |
| 459 | - /*********************************************************************** | |
| 460 | - * --------------------------------------------------------------------- | |
| 461 | - * vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv | |
| 462 | - * | |
| 463 | - * In a real-world situation, this is where you would place your query. | |
| 464 | - * | |
| 465 | - * For information on making queries in WordPress, see this Codex entry: | |
| 466 | - * http://codex.wordpress.org/Class_Reference/wpdb | |
| 467 | - * | |
| 468 | - * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | |
| 469 | - * --------------------------------------------------------------------- | |
| 470 | - **********************************************************************/ | |
| 471 | - | |
| 472 | - /** | |
| 473 | - * REQUIRED for pagination. Let's figure out what page the user is currently | |
| 474 | - * looking at. We'll need this later, so you should always include it in | |
| 475 | - * your own package classes. | |
| 476 | - */ // $current_page = $this->get_current_page(); | |
| 477 | - | |
| 478 | - /** | |
| 479 | - * REQUIRED for pagination. Let's check how many items are in our data array. | |
| 480 | - * In real-world use, this would be the total number of items in your database, | |
| 481 | - * without filtering. We'll need this later, so you should always include it | |
| 482 | - * in your own package classes. | |
| 483 | - */ | |
| 484 | - $total_items = $this->get_total_items(); | |
| 485 | - | |
| 486 | - /** | |
| 487 | - * The WP_List_Table class does not handle pagination for us, so we need | |
| 488 | - * to ensure that the data is trimmed to only the current page. We can use | |
| 489 | - * array_slice() to | |
| 490 | - */ // $data = array_slice( $data, ( ( $current_page - 1 ) * $this->per_page ), $this->per_page ); | |
| 491 | - | |
| 492 | - /** | |
| 493 | - * REQUIRED. Now we can add our *sorted* data to the items property, where | |
| 494 | - * it can be used by the rest of the class. | |
| 495 | - */ | |
| 496 | - $this->items = $data; | |
| 497 | - | |
| 498 | - /** | |
| 499 | - * REQUIRED. We also have to register our pagination options & calculations. | |
| 500 | - */ | |
| 501 | - $this->set_pagination_args( [ | |
| 502 | - 'total_items' => $total_items, | |
| 503 | - 'per_page' => $this->per_page, | |
| 504 | - 'total_pages' => ceil( $total_items / $this->per_page ), | |
| 505 | - 'orderby' => WINP_Plugin::app()->request->request( 'orderby', 'title', true ), | |
| 506 | - 'order' => WINP_Plugin::app()->request->request( 'order', 'asc', true ), | |
| 507 | - ] ); | |
| 508 | - } | |
| 509 | - | |
| 510 | - /** | |
| 511 | - * @Override of display method | |
| 512 | - */ | |
| 513 | - public function display() { | |
| 514 | - /** | |
| 515 | - * Adds a nonce field | |
| 516 | - */ | |
| 517 | - wp_nonce_field( 'winp-ajax-custom-list-nonce', 'winp_ajax_custom_list_nonce' ); | |
| 518 | - | |
| 519 | - if ( ! empty( $this->items ) && ! $this->common ) { | |
| 520 | - foreach ( $this->items as $item ) { | |
| 521 | - wp_nonce_field( 'winp-ajax-snippet-delete-' . $item['ID'], 'winp_ajax_snippet_delete_' . $item['ID'] ); | |
| 522 | - } | |
| 523 | - } | |
| 524 | - | |
| 525 | - /** | |
| 526 | - * Adds field order and orderby | |
| 527 | - */ | |
| 528 | - echo '<input type="hidden" id="order" name="order" value="' . $this->_pagination_args['order'] . '" />'; | |
| 529 | - echo '<input type="hidden" id="orderby" name="orderby" value="' . $this->_pagination_args['orderby'] . '" />'; | |
| 530 | - parent::display(); | |
| 531 | - } | |
| 532 | - | |
| 533 | - /** | |
| 534 | - * @Override ajax_response method | |
| 535 | - */ | |
| 536 | - public function ajax_response() { | |
| 537 | - | |
| 538 | - $this->prepare_items(); | |
| 539 | - extract( $this->_args ); | |
| 540 | - extract( $this->_pagination_args, EXTR_SKIP ); | |
| 541 | - ob_start(); | |
| 542 | - $no_placeholder = WINP_Plugin::app()->request->request( 'no_placeholder', '' ); | |
| 543 | - if ( ! empty( $no_placeholder ) ) { | |
| 544 | - $this->display_rows(); | |
| 545 | - } else { | |
| 546 | - $this->display_rows_or_placeholder(); | |
| 547 | - } | |
| 548 | - $rows = ob_get_clean(); | |
| 549 | - ob_start(); | |
| 550 | - $this->print_column_headers(); | |
| 551 | - $headers = ob_get_clean(); | |
| 552 | - ob_start(); | |
| 553 | - $this->pagination( 'top' ); | |
| 554 | - $pagination_top = ob_get_clean(); | |
| 555 | - ob_start(); | |
| 556 | - $this->pagination( 'bottom' ); | |
| 557 | - $pagination_bottom = ob_get_clean(); | |
| 558 | - $response = [ 'rows' => $rows ]; | |
| 559 | - $response['pagination']['top'] = $pagination_top; | |
| 560 | - $response['pagination']['bottom'] = $pagination_bottom; | |
| 561 | - $response['column_headers'] = $headers; | |
| 562 | - if ( isset( $total_items ) ) { | |
| 563 | - $response['total_items_i18n'] = sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ); | |
| 564 | - } | |
| 565 | - if ( isset( $total_pages ) ) { | |
| 566 | - $response['total_pages'] = $total_pages; | |
| 567 | - $response['total_pages_i18n'] = number_format_i18n( $total_pages ); | |
| 568 | - } | |
| 569 | - die( json_encode( $response ) ); | |
| 570 | - } | |
| 571 | - | |
| 572 | -} | |
| 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;">×</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 | +} | |