PluginProbe
Code Snippets / 2.10.0
Code Snippets v2.10.0
4.0.0-beta.2 3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 All 65 releases
code-snippets / php / class-list-table.php

class-list-table.php in Code Snippets 2.10.0, at php/class-list-table.php

1,072 lines 30.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Contains the class for handling the snippets table
5 *
6 * @package Code_Snippets
7 */
8
9 /* The WP_List_Table base class is not included by default, so we need to load it */
10 if ( ! class_exists( 'WP_List_Table' ) ) {
11 require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
12 }
13
14 /**
15 * This class handles the table for the manage snippets menu
16 *
17 * @since 1.5
18 * @package Code_Snippets
19 */
20 class Code_Snippets_List_Table extends WP_List_Table {
21
22 /**
23 * true if the current screen is in the network admin
24 * @var bool
25 */
26 public $is_network;
27
28 /**
29 * A list of statuses (views)
30 * @var array
31 */
32 public $statuses = array( 'all', 'active', 'inactive', 'recently_activated' );
33
34 /**
35 * The constructor function for our class.
36 * Adds hooks, initializes variables, setups class.
37 */
38 public function __construct() {
39 global $status, $page;
40 $screen = get_current_screen();
41 $this->is_network = is_network_admin();
42
43 /* Determine the status */
44 $status = 'all';
45 if ( isset( $_REQUEST['status'] ) && in_array( $_REQUEST['status'], $this->statuses ) ) {
46 $status = $_REQUEST['status'];
47 }
48
49 /* Add the search query to the URL */
50 if ( isset( $_REQUEST['s'] ) ) {
51 $_SERVER['REQUEST_URI'] = add_query_arg( 's', stripslashes( $_REQUEST['s'] ) );
52 }
53
54 /* Add a snippets per page screen option */
55 $page = $this->get_pagenum();
56
57 add_screen_option( 'per_page', array(
58 'label' => __( 'Snippets per page', 'code-snippets' ),
59 'default' => 999,
60 'option' => 'snippets_per_page',
61 ) );
62
63 /* Set the table columns hidden in Screen Options by default */
64 add_filter( "get_user_option_manage{$screen->id}columnshidden", array( $this, 'get_default_hidden_columns' ), 15 );
65
66 /* Strip the result query arg from the URL */
67 $_SERVER['REQUEST_URI'] = remove_query_arg( 'result' );
68
69 /* Add filters to format the snippet description in the same way the post content is formatted */
70 $filters = array( 'wptexturize', 'convert_smilies', 'convert_chars', 'wpautop', 'shortcode_unautop', 'capital_P_dangit' );
71
72 foreach ( $filters as $filter ) {
73 add_filter( 'code_snippets/list_table/print_snippet_description', $filter );
74 }
75
76 /* Setup the class */
77 parent::__construct( array(
78 'ajax' => true,
79 'plural' => 'snippets',
80 'singular' => 'snippet',
81 ) );
82 }
83
84 /**
85 * Define the output of all columns that have no callback function
86 *
87 * @param Code_Snippet $snippet The snippet used for the current row
88 * @param string $column_name The name of the column being printed
89 *
90 * @return string The content of the column to output
91 */
92 protected function column_default( $snippet, $column_name ) {
93
94 switch ( $column_name ) {
95 case 'id':
96 return $snippet->id;
97 case 'description':
98 return empty( $snippet->desc ) ? '&#8212;' :
99 apply_filters( 'code_snippets/list_table/column_description', $snippet->desc );
100 default:
101 return apply_filters( "code_snippets/list_table/column_{$column_name}", $snippet );
102 }
103 }
104
105 /**
106 * Build a list of action links for individual snippets
107 *
108 * @param Code_Snippet $snippet The current snippet
109 *
110 * @return array The action links HTML
111 */
112 private function get_snippet_action_links( Code_Snippet $snippet ) {
113 $actions = array();
114 $link_format = '<a href="%2$s">%1$s</a>';
115
116 if ( $this->is_network || ! $snippet->network ) {
117
118 if ( $snippet->active ) {
119 $actions['deactivate'] = sprintf(
120 $link_format,
121 $snippet->network ? esc_html__( 'Network Deactivate', 'code-snippets' ) : esc_html__( 'Deactivate', 'code-snippets' ),
122 esc_url( add_query_arg( array(
123 'action' => 'deactivate',
124 'id' => $snippet->id,
125 ) ) )
126 );
127 } elseif ( 'single-use' === $snippet->scope ) {
128
129 $actions['run_once'] = sprintf(
130 $link_format,
131 esc_html__( 'Run Once', 'code-snippets' ),
132 esc_url( add_query_arg( array(
133 'action' => 'run-once',
134 'id' => $snippet->id,
135 ) ) )
136 );
137
138 } else {
139 $actions['activate'] = sprintf(
140 $link_format,
141 $snippet->network ? esc_html__( 'Network Activate', 'code-snippets' ) : esc_html__( 'Activate', 'code-snippets' ),
142 esc_url( add_query_arg( array(
143 'action' => 'activate',
144 'id' => $snippet->id,
145 ) ) )
146 );
147 }
148
149 $actions['edit'] = sprintf(
150 $link_format,
151 esc_html__( 'Edit', 'code-snippets' ),
152 code_snippets()->get_snippet_edit_url( $snippet->id )
153 );
154
155 $actions['export'] = sprintf(
156 $link_format,
157 esc_html__( 'Export', 'code-snippets' ),
158 esc_url( add_query_arg( array(
159 'action' => 'export',
160 'id' => $snippet->id,
161 ) ) )
162 );
163
164 $actions['delete'] = sprintf(
165 '<a href="%2$s" class="delete" onclick="%3$s">%1$s</a>',
166 esc_html__( 'Delete', 'code-snippets' ),
167 esc_url( add_query_arg( array(
168 'action' => 'delete',
169 'id' => $snippet->id,
170 ) ) ),
171 esc_js( sprintf(
172 'return confirm("%s");',
173 esc_html__( 'You are about to permanently delete the selected item.', 'code-snippets' ) . "\n" .
174 esc_html__( "'Cancel' to stop, 'OK' to delete.", 'code-snippets' )
175 ) )
176 );
177
178 } else {
179
180 if ( $snippet->active ) {
181 $actions['network_active'] = esc_html__( 'Network Active', 'code-snippets' );
182 } else {
183 $actions['network_only'] = esc_html__( 'Network Only', 'code-snippets' );
184 }
185 }
186
187 return $actions;
188 }
189
190 /**
191 * Build a list of action links for individual shared network snippets
192 *
193 * @param Code_Snippet $snippet The current snippet
194 *
195 * @return array The action links HTML
196 */
197 private function get_shared_network_snippet_action_links( Code_Snippet $snippet ) {
198 $actions = array();
199 $link_format = '<a href="%2$s">%1$s</a>';
200
201 /* Only add Activate/Deactivate for subsites */
202 if ( ! $this->is_network ) {
203
204 $action = $snippet->active ? 'deactivate' : 'activate';
205 $label = $snippet->active ? esc_html__( 'Deactivate', 'code-snippets' ) : esc_html__( 'Activate', 'code-snippets' );
206 $activate_url = add_query_arg( array(
207 'action' => $action . '-shared',
208 'id' => $snippet->id,
209 ) );
210
211 $actions[ $action ] = sprintf( $link_format, $label, esc_url( $activate_url ) );
212 }
213
214 /* Don't add Edit/Export/Delete actions for if current user can't manage network snippets */
215 if ( ! current_user_can( code_snippets()->get_network_cap_name() ) ) {
216 return $actions;
217 }
218
219 $actions['edit'] = sprintf(
220 $link_format,
221 esc_html__( 'Edit', 'code-snippets' ),
222 code_snippets()->get_snippet_edit_url( $snippet->id, 'network' )
223 );
224
225 $actions['export'] = sprintf(
226 $link_format,
227 esc_html__( 'Export', 'code-snippets' ),
228 add_query_arg(
229 array(
230 'action' => 'export',
231 'id' => $snippet->id,
232 ),
233 code_snippets()->get_menu_url( 'manage', 'network' )
234 )
235 );
236
237 $actions['delete'] = sprintf(
238 '<a href="%2$s" class="delete" onclick="%3$s">%1$s</a>',
239 esc_html__( 'Delete', 'code-snippets' ),
240 add_query_arg(
241 array(
242 'action' => 'delete',
243 'id' => $snippet->id,
244 ),
245 code_snippets()->get_menu_url( 'manage', 'network' )
246 ),
247 esc_js( sprintf(
248 'return confirm("%s");',
249 esc_html__( 'You are about to permanently delete the selected item.', 'code-snippets' ) . "\n" .
250 esc_html__( "'Cancel' to stop, 'OK' to delete.", 'code-snippets' )
251 ) )
252 );
253
254 return $actions;
255 }
256
257 /**
258 * Build the content of the snippet name column
259 *
260 * @param Code_Snippet $snippet The snippet being used for the current row
261 *
262 * @return string The content of the column to output
263 */
264 protected function column_name( $snippet ) {
265
266 $action_links = $snippet->shared_network ?
267 $this->get_shared_network_snippet_action_links( $snippet ) :
268 $this->get_snippet_action_links( $snippet );
269
270 $title = empty( $snippet->name ) ? sprintf( __( 'Untitled #%d', 'code-snippets' ), $snippet->id ) : $snippet->name;
271
272 $row_actions = $this->row_actions( $action_links,
273 apply_filters( 'code_snippets/list_table/row_actions_always_visible', true )
274 );
275
276 $out = esc_html( $title );
277
278 if ( 'global' !== $snippet->scope ) {
279 $out .= ' <span class="dashicons dashicons-' . $snippet->scope_icon . '"></span>';
280 }
281
282 /* Only bold active snippets */
283 if ( $snippet->active ) {
284 $out = sprintf( '<strong>%s</strong>', $out );
285 }
286
287 /* Add a link to the snippet if it isn't an unreadable network-only snippet */
288 if ( $this->is_network || ! $snippet->network || current_user_can( code_snippets()->get_network_cap_name() ) ) {
289
290 $out = sprintf(
291 '<a href="%s">%s</a>',
292 code_snippets()->get_snippet_edit_url( $snippet->id, $snippet->network ? 'network' : 'admin' ),
293 $out
294 );
295 }
296
297 if ( $snippet->shared_network ) {
298 $out .= ' <span class="badge">' . esc_html__( 'Shared on Network', 'code-snippets' ) . '</span>';
299 }
300
301 /* Return the name contents */
302 return apply_filters( 'code_snippets/list_table/column_name', $out, $snippet ) . $row_actions;
303 }
304
305 /**
306 * Builds the checkbox column content
307 *
308 * @param Code_Snippet $snippet The snippet being used for the current row
309 *
310 * @return string The column content to be printed
311 */
312 protected function column_cb( $snippet ) {
313
314 $out = sprintf(
315 '<input type="checkbox" name="%s[]" value="%s" />',
316 $snippet->shared_network ? 'shared_ids' : 'ids',
317 $snippet->id
318 );
319
320 return apply_filters( 'code_snippets/list_table/column_cb', $out, $snippet );
321 }
322
323 /**
324 * Output the content of the tags column
325 * This function is used once for each row
326 *
327 * @since 2.0
328 *
329 * @param Code_Snippet $snippet The snippet being used for the current row
330 *
331 * @return string The column output
332 */
333 protected function column_tags( $snippet ) {
334
335 /* Return a placeholder if there are no tags */
336 if ( empty( $snippet->tags ) ) {
337 return '&#8212;';
338 }
339
340 $out = array();
341
342 /* Loop through the tags and create a link for each one */
343 foreach ( $snippet->tags as $tag ) {
344 $out[] = sprintf( '<a href="%s">%s</a>',
345 esc_url( add_query_arg( 'tag', esc_attr( $tag ) ) ),
346 esc_html( $tag )
347 );
348 }
349
350 return join( ', ', $out );
351 }
352
353 /**
354 * Define the column headers for the table
355 *
356 * @return array The column headers, ID paired with label
357 */
358 public function get_columns() {
359 $columns = array(
360 'cb' => '<input type="checkbox">',
361 'name' => __( 'Name', 'code-snippets' ),
362 'id' => __( 'ID', 'code-snippets' ),
363 'description' => __( 'Description', 'code-snippets' ),
364 'tags' => __( 'Tags', 'code-snippets' ),
365 );
366
367 if ( ! code_snippets_get_setting( 'general', 'enable_description' ) ) {
368 unset( $columns['description'] );
369 }
370
371 if ( ! code_snippets_get_setting( 'general', 'enable_tags' ) ) {
372 unset( $columns['tags'] );
373 }
374
375 return apply_filters( 'code_snippets/list_table/columns', $columns );
376 }
377
378 /**
379 * Define the columns that can be sorted
380 *
381 * @return array The IDs of the columns that can be sorted
382 */
383 public function get_sortable_columns() {
384 $sortable_columns = array(
385 'id' => array( 'id', true ),
386 'name' => array( 'name', false ),
387 );
388 return apply_filters( 'code_snippets/list_table/sortable_columns', $sortable_columns );
389 }
390
391 /**
392 * Define the columns that are hidden by default
393 *
394 * @param mixed $result
395 * @return mixed|array
396 */
397 public function get_default_hidden_columns( $result ) {
398 return $result ? $result : array( 'id' );
399 }
400
401 /**
402 * Define the bulk actions to include in the drop-down menus
403 *
404 * @return array An array of menu items with the ID paired to the label
405 */
406 public function get_bulk_actions() {
407 $actions = array(
408 'activate-selected' => $this->is_network ? __( 'Network Activate', 'code-snippets' ) : __( 'Activate', 'code-snippets' ),
409 'deactivate-selected' => $this->is_network ? __( 'Network Deactivate', 'code-snippets' ) : __( 'Deactivate', 'code-snippets' ),
410 'download-selected' => __( 'Download', 'code-snippets' ),
411 'export-selected' => __( 'Export', 'code-snippets' ),
412 'delete-selected' => __( 'Delete', 'code-snippets' ),
413 );
414 return apply_filters( 'code_snippets/list_table/bulk_actions', $actions );
415 }
416
417 /**
418 * Retrieve the classes for the table
419 *
420 * We override this in order to add 'snippets' as a class for custom styling
421 *
422 * @return array The classes to include on the table element
423 */
424 public function get_table_classes() {
425 $classes = array( 'widefat', $this->_args['plural'] );
426 return apply_filters( 'code_snippets/list_table/table_classes', $classes );
427 }
428
429 /**
430 * Retrieve the 'views' of the table
431 *
432 * Example: active, inactive, recently active
433 *
434 * @return array A list of the view labels linked to the view
435 */
436 public function get_views() {
437 global $totals, $status;
438 $status_links = array();
439
440 /* Loop through the view counts */
441 foreach ( $totals as $type => $count ) {
442
443 /* Don't show the view if there is no count */
444 if ( ! $count ) {
445 continue;
446 }
447
448 /* Define the labels for each view */
449 $labels = array(
450 'all' => _n( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'code-snippets' ),
451 'active' => _n( 'Active <span class="count">(%s)</span>', 'Active <span class="count">(%s)</span>', $count, 'code-snippets' ),
452 'inactive' => _n( 'Inactive <span class="count">(%s)</span>', 'Inactive <span class="count">(%s)</span>', $count, 'code-snippets' ),
453 'recently_activated' => _n( 'Recently Active <span class="count">(%s)</span>', 'Recently Active <span class="count">(%s)</span>', $count, 'code-snippets' ),
454 );
455
456 /* The page URL with the status parameter */
457 $url = esc_url( add_query_arg( 'status', $type ) );
458
459 /* Add a class if this view is currently being viewed */
460 $class = $type === $status ? ' class="current"' : '';
461
462 /* Add the view count to the label */
463 $text = sprintf( $labels[ $type ], number_format_i18n( $count ) );
464
465 /* Construct the link */
466 $status_links[ $type ] = sprintf( '<a href="%s"%s>%s</a>', $url, $class, $text );
467 }
468
469 /* Filter and return the list of views */
470 return apply_filters( 'code_snippets/list_table/views', $status_links );
471 }
472
473 /**
474 * Gets the tags of the snippets currently being viewed in the table
475 * @since 2.0
476 */
477 public function get_current_tags() {
478 global $snippets, $status;
479
480 /* If we're not viewing a snippets table, get all used tags instead */
481 if ( ! isset( $snippets, $status ) ) {
482 return get_all_snippet_tags();
483 }
484
485 $tags = array();
486
487 /* Merge all tags into a single array */
488 foreach ( $snippets[ $status ] as $snippet ) {
489 $tags = array_merge( $snippet->tags, $tags );
490 }
491
492 /* Remove duplicate tags */
493 return array_values( array_unique( $tags, SORT_REGULAR ) );
494 }
495
496 /**
497 * Add filters and extra actions above and below the table
498 * @param string $which Are the actions displayed on the table top or bottom
499 */
500 public function extra_tablenav( $which ) {
501 global $status, $wpdb;
502
503 if ( 'top' === $which ) {
504
505 /* Tags dropdown filter */
506 $tags = $this->get_current_tags();
507
508 if ( count( $tags ) ) {
509 $query = isset( $_GET['tag'] ) ? $_GET['tag'] : '';
510
511 echo '<div class="alignleft actions">';
512 echo '<select name="tag">';
513
514 printf( "<option %s value=''>%s</option>\n",
515 selected( $query, '', false ),
516 __( 'Show all tags', 'code-snippets' )
517 );
518
519 foreach ( $tags as $tag ) {
520
521 printf( "<option %s value='%s'>%s</option>\n",
522 selected( $query, $tag, false ),
523 esc_attr( $tag ),
524 $tag
525 );
526 }
527
528 echo '</select>';
529
530 submit_button( __( 'Filter', 'code-snippets' ), 'button', 'filter_action', false );
531 echo '</div>';
532 }
533 }
534
535 echo '<div class="alignleft actions">';
536
537 if ( 'recently_activated' === $status ) {
538 submit_button( __( 'Clear List', 'code-snippets' ), 'secondary', 'clear-recent-list', false );
539 }
540
541 do_action( 'code_snippets/list_table/actions', $which );
542
543 echo '</div>';
544 }
545
546 /**
547 * Output form fields needed to preserve important
548 * query vars over form submissions
549 *
550 * @param string $context In what context are the fields being outputted?
551 */
552 public function required_form_fields( $context = 'main' ) {
553
554 $vars = apply_filters(
555 'code_snippets/list_table/required_form_fields',
556 array( 'page', 's', 'status', 'paged', 'tag' ),
557 $context
558 );
559
560 if ( 'search_box' === $context ) {
561 /* Remove the 's' var if we're doing this for the search box */
562 $vars = array_diff( $vars, array( 's' ) );
563 }
564
565 foreach ( $vars as $var ) {
566 if ( ! empty( $_REQUEST[ $var ] ) ) {
567 printf( '<input type="hidden" name="%s" value="%s" />', esc_attr( $var ), esc_attr( $_REQUEST[ $var ] ) );
568 print "\n";
569 }
570 }
571
572 do_action( 'code_snippets/list_table/print_required_form_fields', $context );
573 }
574
575
576 /**
577 * Clear the recently activated snippets list if we've clicked the button
578 * @return string The action to execute
579 */
580 public function current_action() {
581 if ( isset( $_POST['clear-recent-list'] ) ) {
582 $action = 'clear-recent-list';
583 } else {
584 $action = parent::current_action();
585 }
586 return apply_filters( 'code_snippets/list_table/current_action', $action );
587 }
588
589 /**
590 * Processes a bulk action
591 *
592 * @uses activate_snippet() to activate snippets
593 * @uses deactivate_snippet() to deactivate snippets
594 * @uses delete_snippet() to delete snippets
595 * @uses export_snippets() to export selected snippets
596 * @uses wp_redirect() to pass the results to the current page
597 * @uses add_query_arg() to append the results to the current URI
598 */
599 public function process_bulk_actions() {
600
601 if ( isset( $_GET['action'], $_GET['id'] ) ) {
602
603 $id = absint( $_GET['id'] );
604 $action = sanitize_key( $_GET['action'] );
605 $_SERVER['REQUEST_URI'] = remove_query_arg( array( 'action', 'id' ) );
606
607 if ( 'activate' === $action ) {
608 activate_snippet( $id, $this->is_network );
609 $result = 'activated';
610 }
611 elseif ( 'run-once' === $action ) {
612 activate_snippet( $id, $this->is_network );
613 $result = 'executed';
614 }
615 elseif ( 'deactivate' === $action ) {
616 deactivate_snippet( $id, $this->is_network );
617 $result = 'deactivated';
618 }
619 elseif ( 'activate-shared' === $action ) {
620 $active_shared_snippets = get_option( 'active_shared_network_snippets', array() );
621
622 if ( ! in_array( $id, $active_shared_snippets ) ) {
623 $active_shared_snippets[] = $id;
624 update_option( 'active_shared_network_snippets', $active_shared_snippets );
625 }
626
627 $result = 'activated';
628 }
629 elseif ( 'deactivate-shared' === $action ) {
630 $active_shared_snippets = get_option( 'active_shared_network_snippets', array() );
631 update_option( 'active_shared_network_snippets', array_diff( $active_shared_snippets, array( $id ) ) );
632
633 $result = 'deactivated';
634 }
635 elseif ( 'delete' === $action ) {
636 delete_snippet( $id, $this->is_network );
637 $result = 'deleted';
638 }
639 elseif ( 'export' === $action ) {
640 export_snippets( array( $id ), $this->is_network );
641 }
642 elseif ( 'download' === $action ) {
643 export_snippets( array( $id ), $this->is_network, 'php' );
644 }
645
646 if ( isset( $result ) ) {
647 wp_redirect( esc_url_raw( add_query_arg( 'result', $result ) ) );
648 exit;
649 }
650 }
651
652 if ( ! isset( $_POST['ids'] ) && ! isset( $_POST['shared_ids'] ) ) {
653 return;
654 }
655
656 $ids = isset( $_POST['ids'] ) ? $_POST['ids'] : array();
657 $_SERVER['REQUEST_URI'] = remove_query_arg( 'action' );
658
659 switch ( $this->current_action() ) {
660
661 case 'activate-selected':
662
663 foreach ( $ids as $id ) {
664 activate_snippet( $id, $this->is_network );
665 }
666
667 /* Process the shared network snippets */
668 if ( isset( $_POST['shared_ids'] ) && is_multisite() && ! $this->is_network ) {
669 $active_shared_snippets = get_option( 'active_shared_network_snippets', array() );
670
671 foreach ( $_POST['shared_ids'] as $id ) {
672 if ( ! in_array( $id, $active_shared_snippets ) ) {
673 $active_shared_snippets[] = $id;
674 }
675 }
676
677 update_option( 'active_shared_network_snippets', $active_shared_snippets );
678 }
679
680 wp_redirect( esc_url_raw( add_query_arg( 'result', 'activated-multi' ) ) );
681 exit;
682
683 case 'deactivate-selected':
684
685 foreach ( $ids as $id ) {
686 deactivate_snippet( $id, $this->is_network );
687 }
688
689 /* Process the shared network snippets */
690 if ( isset( $_POST['shared_ids'] ) && is_multisite() && ! $this->is_network ) {
691 $active_shared_snippets = get_option( 'active_shared_network_snippets', array() );
692 $active_shared_snippets = ( '' === $active_shared_snippets ) ? array() : $active_shared_snippets;
693 $active_shared_snippets = array_diff( $active_shared_snippets, $_POST['shared_ids'] );
694 update_option( 'active_shared_network_snippets', $active_shared_snippets );
695 }
696
697 wp_redirect( esc_url_raw( add_query_arg( 'result', 'deactivated-multi' ) ) );
698 exit;
699
700 case 'export-selected':
701 export_snippets( $ids, $this->is_network );
702 break;
703
704 case 'download-selected':
705 export_snippets( $ids, $this->is_network, 'php' );
706 break;
707
708 case 'delete-selected':
709 foreach ( $ids as $id ) {
710 delete_snippet( $id, $this->is_network );
711 }
712 wp_redirect( esc_url_raw( add_query_arg( 'result', 'deleted-multi' ) ) );
713 exit;
714
715 case 'clear-recent-list':
716 if ( $this->is_network ) {
717 update_site_option( 'recently_activated_snippets', array() );
718 } else {
719 update_option( 'recently_activated_snippets', array() );
720 }
721 break;
722 }
723 }
724
725 /**
726 * Message to display if no snippets are found
727 */
728 public function no_items() {
729 esc_html_e( 'Whoops, it looks like no snippets could be found.', 'code-snippets' );
730 printf(
731 ' <a href="%s">%s</a>',
732 esc_url( code_snippets()->get_menu_url( 'add' ) ),
733 esc_html__( 'Perhaps you would like to add a new one?', 'code-snippets' )
734 );
735 }
736
737 /**
738 *
739 */
740 private function fetch_shared_network_snippets() {
741 /** @var wpdb $wpdb */
742 global $snippets, $wpdb;
743
744 if ( ! is_multisite() || ! $ids = get_site_option( 'shared_network_snippets', false ) ) {
745 return;
746 }
747
748 if ( $this->is_network ) {
749 $limit = count( $snippets['all'] );
750
751 /** @var Code_Snippet $snippet */
752 for ( $i = 0; $i < $limit; $i++ ) {
753 $snippet = &$snippets['all'][ $i ];
754
755 if ( in_array( $snippet->id, $ids ) ) {
756 $snippet->shared_network = true;
757 $snippet->tags = array_merge( $snippet->tags, array( 'shared on network' ) );
758 $snippet->active = false;
759 }
760 }
761 } else {
762
763 $active_shared_snippets = get_option( 'active_shared_network_snippets', array() );
764
765 $sql = sprintf( "SELECT * FROM {$wpdb->ms_snippets} WHERE id IN (%s)",
766 implode( ',', array_fill( 0, count( $ids ), '%d' ) )
767 );
768
769 $shared_snippets = $wpdb->get_results( $wpdb->prepare( $sql, $ids ), ARRAY_A );
770
771 foreach ( $shared_snippets as $index => $snippet ) {
772 $snippet = new Code_Snippet( $snippet );
773 $snippet->network = true;
774 $snippet->shared_network = true;
775 $snippet->tags = array_merge( $snippet->tags, array( 'shared on network' ) );
776 $snippet->active = in_array( $snippet->id, $active_shared_snippets );
777
778 $shared_snippets[ $index ] = $snippet;
779 }
780
781 $snippets['all'] = array_merge( $snippets['all'], $shared_snippets );
782 }
783 }
784
785 /**
786 * Prepares the items to later display in the table.
787 * Should run before any headers are sent.
788 */
789 public function prepare_items() {
790 global $status, $snippets, $totals, $s;
791
792 wp_reset_vars( array( 'orderby', 'order', 's' ) );
793
794 $screen = get_current_screen();
795 $user = get_current_user_id();
796
797 /* First, lets process the bulk actions */
798 $this->process_bulk_actions();
799
800 /* Initialize the $snippets array */
801 $snippets = array_fill_keys( $this->statuses, array() );
802
803 /* Fetch all snippets */
804 if ( is_multisite() && ! $this->is_network && current_user_can( code_snippets()->get_network_cap_name() ) &&
805 code_snippets_get_setting( 'general', 'show_network_snippets' ) ) {
806 $network_snippets = get_snippets( array(), true );
807 $network_snippets = array_filter( $network_snippets, array( $this, 'exclude_shared_network_snippets' ) );
808
809 $local_snippets = get_snippets( array(), false );
810 $snippets['all'] = array_merge( $local_snippets, $network_snippets );
811 } else {
812 $snippets['all'] = get_snippets( array() );
813 }
814
815 $snippets['all'] = apply_filters( 'code_snippets/list_table/get_snippets', $snippets['all'] );
816
817 /* Fetch shared network snippets */
818 $this->fetch_shared_network_snippets();
819
820 /* Redirect POST'ed tag filter to GET */
821 if ( isset( $_POST['tag'] ) ) {
822 $location = empty( $_POST['tag'] ) ? remove_query_arg( 'tag' ) : add_query_arg( 'tag', $_POST['tag'] );
823 wp_redirect( esc_url_raw( $location ) );
824 exit;
825 }
826
827 /* Add scope tags */
828 if ( code_snippets_get_setting( 'general', 'snippet_scope_enabled' ) ) {
829 foreach ( $snippets['all'] as $snippet ) {
830
831 if ( 'global' !== $snippet->scope ) {
832 $snippet->tags = array_merge( $snippet->tags, array( $snippet->scope ) );
833 }
834 }
835 }
836
837 /* Filter snippets by tag */
838 if ( ! empty( $_GET['tag'] ) ) {
839 $snippets['all'] = array_filter( $snippets['all'], array( $this, 'tags_filter_callback' ) );
840 }
841
842 /* Filter snippets based on search query */
843 if ( $s ) {
844 $snippets['all'] = array_filter( $snippets['all'], array( $this, 'search_callback' ) );
845 }
846
847 /* Clear recently activated snippets older than a week */
848 $recently_activated = $this->is_network ?
849 get_site_option( 'recently_activated_snippets', array() ) :
850 get_option( 'recently_activated_snippets', array() );
851
852 foreach ( $recently_activated as $key => $time ) {
853
854 if ( $time + WEEK_IN_SECONDS < time() ) {
855 unset( $recently_activated[ $key ] );
856 }
857 }
858
859 $this->is_network ?
860 update_site_option( 'recently_activated_snippets', $recently_activated ) :
861 update_option( 'recently_activated_snippets', $recently_activated );
862
863 /* Filter snippets into individual sections */
864 foreach ( $snippets['all'] as $snippet ) {
865
866 if ( $snippet->active ) {
867 $snippets['active'][] = $snippet;
868 } else {
869 $snippets['inactive'][] = $snippet;
870
871 /* Was the snippet recently activated? */
872 if ( isset( $recently_activated[ $snippet->id ] ) ) {
873 $snippets['recently_activated'][] = $snippet;
874 }
875 }
876 }
877
878 /* Count the totals for each section */
879 $totals = array();
880 foreach ( $snippets as $type => $list ) {
881 $totals[ $type ] = count( $list );
882 }
883
884 /* If the current status is empty, default tp all */
885 if ( empty( $snippets[ $status ] ) ) {
886 $status = 'all';
887 }
888
889 /* Get the current data */
890 $data = $snippets[ $status ];
891
892 /* Decide how many records per page to show by
893 getting the user's setting in the Screen Options panel */
894 $sort_by = $screen->get_option( 'per_page', 'option' );
895 $per_page = get_user_meta( $user, $sort_by, true );
896
897 if ( empty( $per_page ) || $per_page < 1 ) {
898 $per_page = $screen->get_option( 'per_page', 'default' );
899 }
900
901 $per_page = (int) $per_page;
902
903 $this->_column_headers = $this->get_column_info();
904
905 usort( $data, array( $this, 'usort_reorder_callback' ) );
906
907 /* Determine what page the user is currently looking at */
908 $current_page = $this->get_pagenum();
909
910 /* Check how many items are in the data array */
911 $total_items = count( $data );
912
913 /* The WP_List_Table class does not handle pagination for us, so we need
914 to ensure that the data is trimmed to only the current page. */
915 $data = array_slice( $data, ( ( $current_page - 1 ) * $per_page ), $per_page );
916
917 /* Now we can add our *sorted* data to the items property,
918 where it can be used by the rest of the class. */
919 $this->items = $data;
920
921 /* We register our pagination options and calculations */
922 $this->set_pagination_args( array(
923 'total_items' => $total_items, // Calculate the total number of items
924 'per_page' => $per_page, // Determine how many items to show on a page
925 'total_pages' => ceil( $total_items / $per_page ), // Calculate the total number of pages
926 ) );
927 }
928
929 /**
930 * Callback for array_filter() to exclude shared network snippets from the
931 *
932 * @ignore
933 *
934 * @param Code_Snippet $snippet The current snippet item being filtered
935 *
936 * @return bool false if the snippet is a shared network snippet
937 */
938 private function exclude_shared_network_snippets( $snippet ) {
939 return ! $snippet->shared_network;
940 }
941
942 /**
943 * Callback for usort() used to sort snippets
944 *
945 * @ignore
946 *
947 * @param Code_Snippet $a The first snippet to compare
948 * @param Code_Snippet $b The second snippet to compare
949 *
950 * @return int The sort order
951 */
952 private function usort_reorder_callback( $a, $b ) {
953
954 /* If no sort, default to ID */
955 $orderby = (
956 ! empty( $_REQUEST['orderby'] )
957 ? $_REQUEST['orderby']
958 : apply_filters( 'code_snippets/list_table/default_orderby', 'id' )
959 );
960
961 /* If no order, default to ascending */
962 $order = (
963 ! empty( $_REQUEST['order'] )
964 ? $_REQUEST['order']
965 : apply_filters( 'code_snippets/list_table/default_order', 'asc' )
966 );
967
968 /* Determine sort order */
969 if ( 'id' === $orderby ) {
970 $result = $a->$orderby - $b->$orderby; // get the result for numerical data
971 } else {
972 $result = strcmp( $a->$orderby, $b->$orderby ); // get the result for string data
973 }
974
975 /* Send final sort direction to usort */
976 return ( 'asc' === $order ) ? $result : -$result;
977 }
978
979 /**
980 * Callback for search function
981 * @ignore
982 *
983 * @param Code_Snippet $snippet The snippet being filtered
984 *
985 * @return bool The result of the filter
986 */
987 private function search_callback( $snippet ) {
988 static $term;
989 if ( is_null( $term ) ) {
990 $term = stripslashes( $_REQUEST['s'] );
991 }
992
993 $fields = array( 'name', 'desc', 'code', 'tags_list' );
994
995 foreach ( $fields as $field ) {
996 if ( false !== stripos( $snippet->$field, $term ) ) {
997 return true;
998 }
999 }
1000
1001 return false;
1002 }
1003
1004 /**
1005 * Callback for filtering snippets by tag
1006 *
1007 * @ignore
1008 *
1009 * @param Code_Snippet $snippet The snippet being filtered
1010 *
1011 * @return bool The result of the filter
1012 */
1013 private function tags_filter_callback( $snippet ) {
1014 $tags = explode( ',', $_GET['tag'] );
1015
1016 foreach ( $tags as $tag ) {
1017 if ( in_array( $tag, $snippet->tags ) ) {
1018 return true;
1019 }
1020 }
1021 }
1022
1023 /**
1024 * Display a notice showing the current search terms
1025 *
1026 * @since 1.7
1027 */
1028 public function search_notice() {
1029 if ( ! empty( $_REQUEST['s'] ) || ! empty( $_GET['tag'] ) ) {
1030
1031 echo '<span class="subtitle">' . __( 'Search results', 'code-snippets' );
1032
1033 if ( ! empty( $_REQUEST['s'] ) ) {
1034 echo sprintf( __( ' for &#8220;%s&#8221;', 'code-snippets' ), esc_html( $_REQUEST['s'] ) );
1035 }
1036
1037 if ( ! empty( $_GET['tag'] ) ) {
1038 echo sprintf( __( ' in tag &#8220;%s&#8221;', 'code-snippets' ), esc_html( $_GET['tag'] ) );
1039 }
1040
1041 echo '</span>';
1042
1043 printf(
1044 '&nbsp;<a class="button clear-filters" href="%s">%s</a>',
1045 esc_url( remove_query_arg( array( 's', 'tag' ) ) ),
1046 __( 'Clear Filters', 'code-snippets' )
1047 );
1048 }
1049 }
1050
1051 /**
1052 * Outputs content for a single row of the table
1053 *
1054 * @param Code_Snippet $snippet The snippet being used for the current row
1055 */
1056 public function single_row( $snippet ) {
1057 $row_class = ( $snippet->active ? 'active' : 'inactive' );
1058
1059 if ( code_snippets_get_setting( 'general', 'snippet_scope_enabled' ) ) {
1060 $row_class .= sprintf( ' %s-scope', $snippet->scope );
1061 }
1062
1063 if ( $snippet->shared_network ) {
1064 $row_class .= ' shared-network';
1065 }
1066
1067 printf( '<tr class="%s">', $row_class );
1068 $this->single_row_columns( $snippet );
1069 echo '</tr>';
1070 }
1071 } // end of class
1072