PluginProbe
Stream – Activity Log & Audit Trail / 3.0.3
Stream – Activity Log & Audit Trail v3.0.3
4.4.0 4.3.0 4.2.2 4.2.1 trunk 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.0.7 3.1 3.1.1 3.10.0 3.2.0 3.2.1 3.2.2 3.2.3 All 50 releases
stream / classes / class-admin.php

class-admin.php in Stream – Activity Log & Audit Trail 3.0.3, at classes/class-admin.php

989 lines 26.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 namespace WP_Stream;
3
4 use DateTime;
5 use DateTimeZone;
6 use DateInterval;
7 use \WP_CLI;
8 use \WP_Roles;
9
10 class Admin {
11 /**
12 * Hold Plugin class
13 * @var Plugin
14 */
15 public $plugin;
16
17 /**
18 * @var Network
19 */
20 public $network;
21
22 /**
23 * @var Live_Update
24 */
25 public $live_update;
26
27 /**
28 * Menu page screen id
29 *
30 * @var string
31 */
32 public $screen_id = array();
33
34 /**
35 * List table object
36 *
37 * @var List_Table
38 */
39 public $list_table = null;
40
41 /**
42 * Option to disable access to Stream
43 *
44 * @var bool
45 */
46 public $disable_access = false;
47
48 /**
49 * Class applied to the body of the admin screen
50 *
51 * @var string
52 */
53 public $admin_body_class = 'wp_stream_screen';
54
55 /**
56 * Slug of the records page
57 *
58 * @var string
59 */
60 public $records_page_slug = 'wp_stream';
61
62 /**
63 * Slug of the settings page
64 *
65 * @var string
66 */
67 public $settings_page_slug = 'wp_stream_settings';
68
69 /**
70 * Parent page of the records and settings pages
71 *
72 * @var string
73 */
74 public $admin_parent_page = 'admin.php';
75
76 /**
77 * Capability name for viewing records
78 *
79 * @var string
80 */
81 public $view_cap = 'view_stream';
82
83 /**
84 * Capability name for viewing settings
85 *
86 * @var string
87 */
88 public $settings_cap = 'manage_options';
89
90 /**
91 * Total amount of authors to pre-load
92 *
93 * @var int
94 */
95 public $preload_users_max = 50;
96
97 /**
98 * Admin notices, collected and displayed on proper action
99 *
100 * @var array
101 */
102 public $notices = array();
103
104 /**
105 * Class constructor.
106 *
107 * @param Plugin $plugin The main Plugin class.
108 */
109 public function __construct( $plugin ) {
110 $this->plugin = $plugin;
111
112 add_action( 'init', array( $this, 'init' ) );
113
114 // Ensure function used in various methods is pre-loaded
115 if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
116 require_once( ABSPATH . '/wp-admin/includes/plugin.php' );
117 }
118
119 // User and role caps
120 add_filter( 'user_has_cap', array( $this, 'filter_user_caps' ), 10, 4 );
121 add_filter( 'role_has_cap', array( $this, 'filter_role_caps' ), 10, 3 );
122
123 if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) && ! is_network_admin() ) {
124 $options = (array) get_site_option( 'wp_stream_network', array() );
125 $option = isset( $options['general_site_access'] ) ? absint( $options['general_site_access'] ) : 1;
126
127 $this->disable_access = ( $option ) ? false : true;
128 }
129
130 // Register settings page
131 if ( ! $this->disable_access ) {
132 add_action( 'admin_menu', array( $this, 'register_menu' ) );
133 }
134
135 // Admin notices
136 add_action( 'admin_notices', array( $this, 'prepare_admin_notices' ) );
137 add_action( 'shutdown', array( $this, 'admin_notices' ) );
138
139 // Add admin body class
140 add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
141
142 // Plugin action links
143 add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
144
145 // Load admin scripts and styles
146 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) );
147 add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
148
149 // Reset Streams database
150 add_action( 'wp_ajax_wp_stream_reset', array( $this, 'wp_ajax_reset' ) );
151
152 // Uninstall Streams and Deactivate plugin
153 $uninstall = new Uninstall( $this->plugin );
154 add_action( 'wp_ajax_wp_stream_uninstall', array( $uninstall, 'uninstall' ) );
155
156 // Auto purge setup
157 add_action( 'wp_loaded', array( $this, 'purge_schedule_setup' ) );
158 add_action( 'wp_stream_auto_purge', array( $this, 'purge_scheduled_action' ) );
159
160 // Ajax users list
161 add_action( 'wp_ajax_wp_stream_filters', array( $this, 'ajax_filters' ) );
162
163 // Ajax user's name by ID
164 add_action( 'wp_ajax_wp_stream_get_filter_value_by_id', array( $this, 'get_filter_value_by_id' ) );
165
166 // Ajax users list
167 add_action( 'wp_ajax_wp_stream_filters', array( $this, 'ajax_filters' ) );
168
169 // Ajax user's name by ID
170 add_action( 'wp_ajax_wp_stream_get_filter_value_by_id', array( $this, 'get_filter_value_by_id' ) );
171 }
172
173 /**
174 * Load admin classes
175 *
176 * @action init
177 */
178 public function init() {
179 $this->network = new Network( $this->plugin );
180 $this->live_update = new Live_Update( $this->plugin );
181 }
182
183 /**
184 * Output specific updates passed as URL parameters
185 *
186 * @action admin_notices
187 *
188 * @return string
189 */
190 public function prepare_admin_notices() {
191 $message = wp_stream_filter_input( INPUT_GET, 'message' );
192
193 switch ( $message ) {
194 case 'settings_reset':
195 $this->notice( esc_html__( 'All site settings have been successfully reset.', 'stream' ) );
196 break;
197 }
198 }
199
200 /**
201 * Handle notice messages according to the appropriate context (WP-CLI or the WP Admin)
202 *
203 * @param string $message
204 * @param bool $is_error
205 */
206 public function notice( $message, $is_error = true ) {
207 if ( defined( 'WP_CLI' ) && WP_CLI ) {
208 $message = strip_tags( $message );
209
210 if ( $is_error ) {
211 WP_CLI::warning( $message );
212 } else {
213 WP_CLI::success( $message );
214 }
215 } else {
216 // Trigger admin notices late, so that any notices which occur during page load are displayed
217 add_action( 'shutdown', array( $this, 'admin_notices' ) );
218
219 $notice = compact( 'message', 'is_error' );
220
221 if ( ! in_array( $notice, $this->notices ) ) {
222 $this->notices[] = $notice;
223 }
224 }
225 }
226
227 /**
228 * Show an error or other message in the WP Admin
229 *
230 * @action shutdown
231 */
232 public function admin_notices() {
233 global $allowedposttags;
234
235 $custom = array(
236 'progress' => array(
237 'class' => true,
238 'id' => true,
239 'max' => true,
240 'style' => true,
241 'value' => true,
242 ),
243 );
244
245 $allowed_html = array_merge( $allowedposttags, $custom );
246
247 ksort( $allowed_html );
248
249 foreach ( $this->notices as $notice ) {
250 $class_name = empty( $notice['is_error'] ) ? 'updated' : 'error';
251 $html_message = sprintf( '<div class="%s">%s</div>', esc_attr( $class_name ), wpautop( $notice['message'] ) );
252
253 echo wp_kses( $html_message, $allowed_html );
254 }
255 }
256
257 /**
258 * Register menu page
259 *
260 * @action admin_menu
261 *
262 * @return bool|void
263 */
264 public function register_menu() {
265 /**
266 * Filter the main admin menu title
267 *
268 * @return string
269 */
270 $main_menu_title = apply_filters( 'wp_stream_admin_menu_title', esc_html__( 'Stream', 'stream' ) );
271
272 /**
273 * Filter the main admin menu position
274 *
275 * Note: Using longtail decimal string to reduce the chance of position conflicts, see Codex
276 *
277 * @return string
278 */
279 $main_menu_position = apply_filters( 'wp_stream_menu_position', '2.999999' );
280
281 /**
282 * Filter the main admin page title
283 *
284 * @return string
285 */
286 $main_page_title = apply_filters( 'wp_stream_admin_page_title', esc_html__( 'Stream Records', 'stream' ) );
287
288 $this->screen_id['main'] = add_menu_page(
289 $main_page_title,
290 $main_menu_title,
291 $this->view_cap,
292 $this->records_page_slug,
293 array( $this, 'render_list_table' ),
294 'div',
295 $main_menu_position
296 );
297
298 /**
299 * Filter the Settings admin page title
300 *
301 * @return string
302 */
303 $settings_page_title = apply_filters( 'wp_stream_settings_form_title', esc_html__( 'Stream Settings', 'stream' ) );
304
305 $this->screen_id['settings'] = add_submenu_page(
306 $this->records_page_slug,
307 $settings_page_title,
308 esc_html__( 'Settings', 'stream' ),
309 $this->settings_cap,
310 $this->settings_page_slug,
311 array( $this, 'render_settings_page' )
312 );
313
314 if ( isset( $this->screen_id['main'] ) ) {
315 /**
316 * Fires just before the Stream list table is registered.
317 *
318 * @return void
319 */
320 do_action( 'wp_stream_admin_menu_screens' );
321
322 // Register the list table early, so it associates the column headers with 'Screen settings'
323 add_action( 'load-' . $this->screen_id['main'], array( $this, 'register_list_table' ) );
324 }
325 }
326
327 /**
328 * Enqueue scripts/styles for admin screen
329 *
330 * @action admin_enqueue_scripts
331 *
332 * @param string $hook
333 *
334 * @return void
335 */
336 public function admin_enqueue_scripts( $hook ) {
337 wp_register_script( 'select2', $this->plugin->locations['url'] . 'ui/lib/select2/select2.js', array( 'jquery' ), '3.5.2', true );
338 wp_register_style( 'select2', $this->plugin->locations['url'] . 'ui/lib/select2/select2.css', array(), '3.5.2' );
339 wp_register_script( 'timeago', $this->plugin->locations['url'] . 'ui/lib/timeago/jquery.timeago.js', array(), '1.4.1', true );
340
341 $locale = strtolower( substr( get_locale(), 0, 2 ) );
342 $file_tmpl = 'ui/lib/timeago/locales/jquery.timeago.%s.js';
343
344 if ( file_exists( $this->plugin->locations['dir'] . sprintf( $file_tmpl, $locale ) ) ) {
345 wp_register_script( 'timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, $locale ), array( 'timeago' ), '1' );
346 } else {
347 wp_register_script( 'timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, 'en' ), array( 'timeago' ), '1' );
348 }
349
350 wp_enqueue_style( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/css/admin.css', array(), $this->plugin->get_version() );
351
352 $script_screens = array( 'plugins.php' );
353
354 if ( in_array( $hook, $this->screen_id ) || in_array( $hook, $script_screens ) ) {
355 wp_enqueue_script( 'select2' );
356 wp_enqueue_style( 'select2' );
357
358 wp_enqueue_script( 'timeago' );
359 wp_enqueue_script( 'timeago-locale' );
360
361 wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.js', array( 'jquery', 'select2' ), $this->plugin->get_version() );
362 wp_enqueue_script( 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.js', array( 'jquery', 'heartbeat' ), $this->plugin->get_version() );
363
364 wp_localize_script(
365 'wp-stream-admin',
366 'wp_stream',
367 array(
368 'i18n' => array(
369 'confirm_purge' => esc_html__( 'Are you sure you want to delete all Stream activity records from the database? This cannot be undone.', 'stream' ),
370 'confirm_defaults' => esc_html__( 'Are you sure you want to reset all site settings to default? This cannot be undone.', 'stream' ),
371 'confirm_uninstall' => esc_html__( 'Are you sure you want to uninstall and deactivate Stream? This will delete all Stream tables from the database and cannot be undone.', 'stream' ),
372 ),
373 'locale' => esc_js( $locale ),
374 'gmt_offset' => get_option( 'gmt_offset' ),
375 )
376 );
377
378 wp_localize_script(
379 'wp-stream-live-updates',
380 'wp_stream_live_updates',
381 array(
382 'current_screen' => $hook,
383 'current_page' => isset( $_GET['paged'] ) ? esc_js( $_GET['paged'] ) : '1', // input var okay
384 'current_order' => isset( $_GET['order'] ) ? esc_js( $_GET['order'] ) : 'desc', // input var okay
385 'current_query' => wp_stream_json_encode( $_GET ), // input var okay
386 'current_query_count' => count( $_GET ), // input var okay
387 )
388 );
389 }
390
391 /**
392 * The maximum number of items that can be updated in bulk without receiving a warning.
393 *
394 * Stream watches for bulk actions performed in the WordPress Admin (such as updating
395 * many posts at once) and warns the user before proceeding if the number of items they
396 * are attempting to update exceeds this threshold value. Since Stream will try to save
397 * a log for each item, it will take longer than usual to complete the operation.
398 *
399 * The default threshold is 100 items.
400 *
401 * @return int
402 */
403 $bulk_actions_threshold = apply_filters( 'wp_stream_bulk_actions_threshold', 100 );
404
405 wp_enqueue_script( 'wp-stream-global', $this->plugin->locations['url'] . 'ui/js/global.js', array( 'jquery' ), $this->plugin->get_version() );
406 wp_localize_script(
407 'wp-stream-global',
408 'wp_stream_global',
409 array(
410 'bulk_actions' => array(
411 'i18n' => array(
412 'confirm_action' => sprintf( esc_html__( 'Are you sure you want to perform bulk actions on over %s items? This process could take a while to complete.', 'stream' ), number_format( absint( $bulk_actions_threshold ) ) ),
413 ),
414 'threshold' => absint( $bulk_actions_threshold ),
415 ),
416 'plugins_screen_url' => self_admin_url( 'plugins.php#stream' ),
417 )
418 );
419 }
420
421 /**
422 * Check whether or not the current admin screen belongs to Stream
423 *
424 * @return bool
425 */
426 public function is_stream_screen() {
427 if ( is_admin() && false !== strpos( wp_stream_filter_input( INPUT_GET, 'page' ), $this->records_page_slug ) ) {
428 return true;
429 }
430
431 return false;
432 }
433
434 /**
435 * Add a specific body class to all Stream admin screens
436 *
437 * @param string $classes
438 *
439 * @filter admin_body_class
440 *
441 * @return string
442 */
443 public function admin_body_class( $classes ) {
444 $stream_classes = array();
445
446 if ( $this->is_stream_screen() ) {
447 $stream_classes[] = $this->admin_body_class;
448
449 if ( isset( $_GET['page'] ) ) {
450 $stream_classes[] = sanitize_key( $_GET['page'] ); // input var okay
451 }
452 }
453
454 /**
455 * Filter the Stream admin body classes
456 *
457 * @return array
458 */
459 $stream_classes = apply_filters( 'wp_stream_admin_body_classes', $stream_classes );
460 $stream_classes = implode( ' ', array_map( 'trim', $stream_classes ) );
461
462 return sprintf( '%s %s ', $classes, $stream_classes );
463 }
464
465 /**
466 * Add menu styles for various WP Admin skins
467 *
468 * @uses \wp_add_inline_style()
469 *
470 * @action admin_enqueue_scripts
471 */
472 public function admin_menu_css() {
473 wp_register_style( 'wp-stream-datepicker', $this->plugin->locations['url'] . 'ui/css/datepicker.css', array(), $this->plugin->get_version() );
474 wp_register_style( 'wp-stream-icons', $this->plugin->locations['url'] . 'ui/stream-icons/style.css', array(), $this->plugin->get_version() );
475
476 // Make sure we're working off a clean version
477 if ( ! file_exists( ABSPATH . WPINC . '/version.php' ) ) {
478 return;
479 }
480 include( ABSPATH . WPINC . '/version.php' );
481
482 if ( ! isset( $wp_version ) ) {
483 return;
484 }
485
486 $body_class = $this->admin_body_class;
487 $records_page = $this->records_page_slug;
488 $stream_url = $this->plugin->locations['url'];
489
490 if ( version_compare( $wp_version, '3.8-alpha', '>=' ) ) {
491 wp_enqueue_style( 'wp-stream-icons' );
492
493 $css = "
494 #toplevel_page_{$records_page} .wp-menu-image:before {
495 font-family: 'WP Stream' !important;
496 content: '\\73' !important;
497 }
498 #toplevel_page_{$records_page} .wp-menu-image {
499 background-repeat: no-repeat;
500 }
501 #menu-posts-feedback .wp-menu-image:before {
502 font-family: dashicons !important;
503 content: '\\f175';
504 }
505 #adminmenu #menu-posts-feedback div.wp-menu-image {
506 background: none !important;
507 background-repeat: no-repeat;
508 }
509 body.{$body_class} #wpbody-content .wrap h1:nth-child(1):before {
510 font-family: 'WP Stream' !important;
511 content: '\\73';
512 padding: 0 8px 0 0;
513 }
514 ";
515 } else {
516 $css = "
517 #toplevel_page_{$records_page} .wp-menu-image {
518 background: url( {$stream_url}ui/stream-icons/menuicon-sprite.png ) 0 90% no-repeat;
519 }
520 /* Retina Stream Menu Icon */
521 @media only screen and (-moz-min-device-pixel-ratio: 1.5),
522 only screen and (-o-min-device-pixel-ratio: 3/2),
523 only screen and (-webkit-min-device-pixel-ratio: 1.5),
524 only screen and (min-device-pixel-ratio: 1.5) {
525 #toplevel_page_{$records_page} .wp-menu-image {
526 background: url( {$stream_url}ui/stream-icons/menuicon-sprite-2x.png ) 0 90% no-repeat;
527 background-size:30px 64px;
528 }
529 }
530 #toplevel_page_{$records_page}.current .wp-menu-image,
531 #toplevel_page_{$records_page}.wp-has-current-submenu .wp-menu-image,
532 #toplevel_page_{$records_page}:hover .wp-menu-image {
533 background-position: top left;
534 }
535 ";
536 }
537
538 \wp_add_inline_style( 'wp-admin', $css );
539 }
540
541 public function wp_ajax_reset() {
542 check_ajax_referer( 'stream_nonce', 'wp_stream_nonce' );
543
544 if ( ! current_user_can( $this->settings_cap ) ) {
545 wp_die(
546 esc_html__( "You don't have sufficient privileges to do this action.", 'stream' )
547 );
548 }
549
550 $this->erase_stream_records();
551
552 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
553 return true;
554 }
555
556 wp_redirect(
557 add_query_arg(
558 array(
559 'page' => is_network_admin() ? $this->network->network_settings_page_slug : $this->settings_page_slug,
560 'message' => 'data_erased',
561 ),
562 self_admin_url( $this->admin_parent_page )
563 )
564 );
565
566 exit;
567 }
568
569 private function erase_stream_records() {
570 global $wpdb;
571
572 $where = '';
573
574 if ( is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
575 $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() );
576 }
577
578 $wpdb->query(
579 "DELETE `stream`, `meta`
580 FROM {$wpdb->stream} AS `stream`
581 LEFT JOIN {$wpdb->streammeta} AS `meta`
582 ON `meta`.`record_id` = `stream`.`ID`
583 WHERE 1=1 {$where};" // @codingStandardsIgnoreLine $where already prepared
584 );
585 }
586
587 public function purge_schedule_setup() {
588 if ( ! wp_next_scheduled( 'wp_stream_auto_purge' ) ) {
589 wp_schedule_event( time(), 'twicedaily', 'wp_stream_auto_purge' );
590 }
591 }
592
593 public function purge_scheduled_action() {
594 global $wpdb;
595
596 // Don't purge when in Network Admin unless Stream is network activated
597 if (
598 is_multisite()
599 &&
600 is_network_admin()
601 &&
602 ! is_plugin_active_for_network( $this->plugin->locations['plugin'] )
603 ) {
604 return;
605 }
606
607 if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
608 $options = (array) get_site_option( 'wp_stream_network', array() );
609 } else {
610 $options = (array) get_option( 'wp_stream', array() );
611 }
612
613 if ( isset( $options['general_keep_records_indefinitely'] ) || ! isset( $options['general_records_ttl'] ) ) {
614 return;
615 }
616
617 $days = $options['general_records_ttl'];
618 $date = new DateTime( 'now', $timezone = new DateTimeZone( 'UTC' ) );
619
620 $date->sub( DateInterval::createFromDateString( "$days days" ) );
621
622 $where = $wpdb->prepare( ' AND `stream`.`created` < %s', $date->format( 'Y-m-d H:i:s' ) );
623
624 // Multisite but NOT network activated, only purge the current blog
625 if ( is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
626 $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() );
627 }
628
629 $wpdb->query(
630 "DELETE `stream`, `meta`
631 FROM {$wpdb->stream} AS `stream`
632 LEFT JOIN {$wpdb->streammeta} AS `meta`
633 ON `meta`.`record_id` = `stream`.`ID`
634 WHERE 1=1 {$where};" // @codingStandardsIgnoreLine $where already prepared
635 );
636 }
637
638 /**
639 * @param array $links
640 * @param string $file
641 *
642 * @filter plugin_action_links
643 *
644 * @return array
645 */
646 public function plugin_action_links( $links, $file ) {
647 if ( plugin_basename( $this->plugin->locations['dir'] . 'stream.php' ) !== $file ) {
648 return $links;
649 }
650
651 // Also don't show links in Network Admin if Stream isn't network enabled
652 if ( is_network_admin() && is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
653 return $links;
654 }
655
656 if ( is_network_admin() ) {
657 $admin_page_url = add_query_arg( array( 'page' => $this->network->network_settings_page_slug ), network_admin_url( $this->admin_parent_page ) );
658 } else {
659 $admin_page_url = add_query_arg( array( 'page' => $this->settings_page_slug ), admin_url( $this->admin_parent_page ) );
660 }
661
662 $links[] = sprintf( '<a href="%s">%s</a>', esc_url( $admin_page_url ), esc_html__( 'Settings', 'default' ) );
663
664 $url = add_query_arg(
665 array(
666 'action' => 'wp_stream_uninstall',
667 'wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ),
668 ),
669 admin_url( 'admin-ajax.php' )
670 );
671
672 $links[] = sprintf( '<span id="wp_stream_uninstall" class="delete"><a href="%s">%s</a></span>', esc_url( $url ), esc_html__( 'Uninstall', 'stream' ) );
673
674 return $links;
675 }
676
677 /**
678 * Render main page
679 */
680 public function render_list_table() {
681 $this->list_table->prepare_items();
682 ?>
683 <div class="wrap">
684 <h1><?php echo esc_html( get_admin_page_title() ) ?></h1>
685 <?php $this->list_table->display() ?>
686 </div>
687 <?php
688 }
689
690 /**
691 * Render settings page
692 */
693 public function render_settings_page() {
694 $option_key = $this->plugin->settings->option_key;
695 $form_action = apply_filters( 'wp_stream_settings_form_action', admin_url( 'options.php' ) );
696
697 $page_description = apply_filters( 'wp_stream_settings_form_description', '' );
698
699 $sections = $this->plugin->settings->get_fields();
700 $active_tab = wp_stream_filter_input( INPUT_GET, 'tab' );
701
702 wp_enqueue_script( 'wp-stream-settings', $this->plugin->locations['url'] . 'ui/js/settings.js', array( 'jquery' ), $this->plugin->get_version(), true );
703 ?>
704 <div class="wrap">
705 <h1><?php echo esc_html( get_admin_page_title() ) ?></h1>
706
707 <?php if ( ! empty( $page_description ) ) : ?>
708 <p><?php echo esc_html( $page_description ) ?></p>
709 <?php endif; ?>
710
711 <?php settings_errors() ?>
712
713 <?php if ( count( $sections ) > 1 ) : ?>
714 <h2 class="nav-tab-wrapper">
715 <?php $i = 0 ?>
716 <?php foreach ( $sections as $section => $data ) : ?>
717 <?php $i ++ ?>
718 <?php $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section ) ?>
719 <a href="<?php echo esc_url( add_query_arg( 'tab', $section ) ) ?>" class="nav-tab<?php if ( $is_active ) { echo esc_attr( ' nav-tab-active' ); } ?>">
720 <?php echo esc_html( $data['title'] ) ?>
721 </a>
722 <?php endforeach; ?>
723 </h2>
724 <?php endif; ?>
725
726 <div class="nav-tab-content" id="tab-content-settings">
727 <form method="post" action="<?php echo esc_attr( $form_action ) ?>" enctype="multipart/form-data">
728 <div class="settings-sections">
729 <?php
730 $i = 0;
731 foreach ( $sections as $section => $data ) {
732 $i++;
733
734 $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section );
735
736 if ( $is_active ) {
737 settings_fields( $option_key );
738 do_settings_sections( $option_key );
739 }
740 }
741 ?>
742 </div>
743 <?php submit_button() ?>
744 </form>
745 </div>
746 </div>
747 <?php
748 }
749
750 /**
751 * Instantiate the list table
752 */
753 public function register_list_table() {
754 $this->list_table = new List_Table( $this->plugin, array( 'screen' => $this->screen_id['main'] ) );
755 }
756
757 /**
758 * Check if a particular role has access
759 *
760 * @param string $role
761 *
762 * @return bool
763 */
764 private function role_can_view( $role ) {
765 if ( in_array( $role, $this->plugin->settings->options['general_role_access'] ) ) {
766 return true;
767 }
768
769 return false;
770 }
771
772 /**
773 * Filter user caps to dynamically grant our view cap based on allowed roles
774 *
775 * @param $allcaps
776 * @param $caps
777 * @param $args
778 * @param $user
779 *
780 * @filter user_has_cap
781 *
782 * @return array
783 */
784 public function filter_user_caps( $allcaps, $caps, $args, $user = null ) {
785 global $wp_roles;
786
787 $_wp_roles = isset( $wp_roles ) ? $wp_roles : new WP_Roles();
788
789 $user = is_a( $user, 'WP_User' ) ? $user : wp_get_current_user();
790
791 // @see
792 // https://github.com/WordPress/WordPress/blob/c67c9565f1495255807069fdb39dac914046b1a0/wp-includes/capabilities.php#L758
793 $roles = array_unique(
794 array_merge(
795 $user->roles,
796 array_filter(
797 array_keys( $user->caps ),
798 array( $_wp_roles, 'is_role' )
799 )
800 )
801 );
802
803 $stream_view_caps = array( $this->view_cap );
804
805 foreach ( $caps as $cap ) {
806 if ( in_array( $cap, $stream_view_caps ) ) {
807 foreach ( $roles as $role ) {
808 if ( $this->role_can_view( $role ) ) {
809 $allcaps[ $cap ] = true;
810
811 break 2;
812 }
813 }
814 }
815 }
816
817 return $allcaps;
818 }
819
820 /**
821 * Filter role caps to dynamically grant our view cap based on allowed roles
822 *
823 * @filter role_has_cap
824 *
825 * @param $allcaps
826 * @param $cap
827 * @param $role
828 *
829 * @return array
830 */
831 public function filter_role_caps( $allcaps, $cap, $role ) {
832 $stream_view_caps = array( $this->view_cap );
833
834 if ( in_array( $cap, $stream_view_caps ) && $this->role_can_view( $role ) ) {
835 $allcaps[ $cap ] = true;
836 }
837
838 return $allcaps;
839 }
840
841 /**
842 * @action wp_ajax_wp_stream_filters
843 */
844 public function ajax_filters() {
845 switch ( wp_stream_filter_input( INPUT_GET, 'filter' ) ) {
846 case 'user_id':
847 $users = array_merge(
848 array( 0 => (object) array( 'display_name' => 'WP-CLI' ) ),
849 get_users()
850 );
851
852 $search = wp_stream_filter_input( INPUT_GET, 'q' );
853 if ( $search ) {
854 // `search` arg for get_users() is not enough
855 $users = array_filter(
856 $users,
857 function( $user ) use ( $search ) {
858 return false !== mb_strpos( mb_strtolower( $user->display_name ), mb_strtolower( $search ) );
859 }
860 );
861 }
862
863 if ( count( $users ) > $this->preload_users_max ) {
864 $users = array_slice( $users, 0, $this->preload_users_max );
865 }
866
867 // Get gravatar / roles for final result set
868 $results = $this->get_users_record_meta( $users );
869
870 break;
871 }
872
873 if ( isset( $results ) ) {
874 echo wp_stream_json_encode( array_values( $results ) ); // xss ok
875 }
876
877 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
878 return;
879 }
880
881 die();
882 }
883
884 /**
885 * @action wp_ajax_wp_stream_get_filter_value_by_id
886 */
887 public function get_filter_value_by_id() {
888 $filter = wp_stream_filter_input( INPUT_POST, 'filter' );
889
890 switch ( $filter ) {
891 case 'user_id':
892 $id = wp_stream_filter_input( INPUT_POST, 'id' );
893
894 if ( '0' === $id ) {
895 $value = 'WP-CLI';
896
897 break;
898 }
899
900 $user = get_userdata( $id );
901
902 if ( ! $user || is_wp_error( $user ) ) {
903 $value = '';
904 } else {
905 $value = $user->display_name;
906 }
907
908 break;
909 default:
910 $value = '';
911 }
912
913 echo wp_stream_json_encode( $value ); // xss ok
914
915 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
916 return;
917 }
918
919 die();
920 }
921
922 public function get_users_record_meta( $authors ) {
923 $authors_records = array();
924
925 foreach ( $authors as $user_id => $args ) {
926 $author = new Author( $user_id );
927
928 $authors_records[ $user_id ] = array(
929 'text' => $author->get_display_name(),
930 'id' => $user_id,
931 'label' => $author->get_display_name(),
932 'icon' => $author->get_avatar_src( 32 ),
933 'title' => '',
934 );
935 }
936
937 return $authors_records;
938 }
939
940 /**
941 * Get user meta in a way that is also safe for VIP
942 *
943 * @param int $user_id
944 * @param string $meta_key
945 * @param bool $single (optional)
946 *
947 * @return mixed
948 */
949 function get_user_meta( $user_id, $meta_key, $single = true ) {
950 if ( wp_stream_is_vip() && function_exists( 'get_user_attribute' ) ) {
951 return get_user_attribute( $user_id, $meta_key );
952 }
953 return get_user_meta( $user_id, $meta_key, $single );
954 }
955
956 /**
957 * Update user meta in a way that is also safe for VIP
958 *
959 * @param int $user_id
960 * @param string $meta_key
961 * @param mixed $meta_value
962 * @param mixed $prev_value (optional)
963 *
964 * @return int|bool
965 */
966 function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
967 if ( wp_stream_is_vip() && function_exists( 'update_user_attribute' ) ) {
968 return update_user_attribute( $user_id, $meta_key, $meta_value );
969 }
970 return update_user_meta( $user_id, $meta_key, $meta_value, $prev_value );
971 }
972
973 /**
974 * Delete user meta in a way that is also safe for VIP
975 *
976 * @param int $user_id
977 * @param string $meta_key
978 * @param mixed $meta_value (optional)
979 *
980 * @return bool
981 */
982 function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) {
983 if ( wp_stream_is_vip() && function_exists( 'delete_user_attribute' ) ) {
984 return delete_user_attribute( $user_id, $meta_key, $meta_value );
985 }
986 return delete_user_meta( $user_id, $meta_key, $meta_value );
987 }
988 }
989