PluginProbe
Stream – Activity Log & Audit Trail / 3.0.7
Stream – Activity Log & Audit Trail v3.0.7
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.7, at classes/class-admin.php

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