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

971 lines 26.8 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 * Fires before submenu items are added to the Stream menu
304 * allowing plugins to add menu items before Settings
305 *
306 * @return void
307 */
308 do_action( 'wp_stream_admin_menu' );
309
310 /**
311 * Filter the Settings admin page title
312 *
313 * @return string
314 */
315 $settings_page_title = apply_filters( 'wp_stream_settings_form_title', esc_html__( 'Stream Settings', 'stream' ) );
316
317 $this->screen_id['settings'] = add_submenu_page(
318 $this->records_page_slug,
319 $settings_page_title,
320 esc_html__( 'Settings', 'stream' ),
321 $this->settings_cap,
322 $this->settings_page_slug,
323 array( $this, 'render_settings_page' )
324 );
325
326 if ( isset( $this->screen_id['main'] ) ) {
327 /**
328 * Fires just before the Stream list table is registered.
329 *
330 * @return void
331 */
332 do_action( 'wp_stream_admin_menu_screens' );
333
334 // Register the list table early, so it associates the column headers with 'Screen settings'.
335 add_action( 'load-' . $this->screen_id['main'], array( $this, 'register_list_table' ) );
336 }
337 }
338
339 /**
340 * Enqueue scripts/styles for admin screen
341 *
342 * @action admin_enqueue_scripts
343 *
344 * @param string $hook
345 *
346 * @return void
347 */
348 public function admin_enqueue_scripts( $hook ) {
349 wp_register_script( 'wp-stream-select2', $this->plugin->locations['url'] . 'ui/lib/select2/js/select2.js', array( 'jquery' ), '3.5.2', true );
350 wp_register_style( 'wp-stream-select2', $this->plugin->locations['url'] . 'ui/lib/select2/css/select2.css', array(), '3.5.2' );
351 wp_register_script( 'wp-stream-timeago', $this->plugin->locations['url'] . 'ui/lib/timeago/jquery.timeago.js', array(), '1.4.1', true );
352
353 $locale = strtolower( substr( get_locale(), 0, 2 ) );
354 $file_tmpl = 'ui/lib/timeago/locales/jquery.timeago.%s.js';
355
356 if ( file_exists( $this->plugin->locations['dir'] . sprintf( $file_tmpl, $locale ) ) ) {
357 wp_register_script( 'wp-stream-timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, $locale ), array( 'wp-stream-timeago' ), '1' );
358 } else {
359 wp_register_script( 'wp-stream-timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, 'en' ), array( 'wp-stream-timeago' ), '1' );
360 }
361
362 wp_enqueue_style( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/css/admin.css', array(), $this->plugin->get_version() );
363
364 $script_screens = array( 'plugins.php' );
365
366 if ( in_array( $hook, $this->screen_id, true ) || in_array( $hook, $script_screens, true ) ) {
367 wp_enqueue_script( 'wp-stream-select2' );
368 wp_enqueue_style( 'wp-stream-select2' );
369
370 wp_enqueue_script( 'wp-stream-timeago' );
371 wp_enqueue_script( 'wp-stream-timeago-locale' );
372
373 wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() );
374 wp_enqueue_script( 'wp-stream-admin-exclude', $this->plugin->locations['url'] . 'ui/js/exclude.js', array( 'jquery', 'wp-stream-select2' ), $this->plugin->get_version() );
375 wp_enqueue_script( 'wp-stream-live-updates', $this->plugin->locations['url'] . 'ui/js/live-updates.js', array( 'jquery', 'heartbeat' ), $this->plugin->get_version() );
376
377 wp_localize_script(
378 'wp-stream-admin',
379 'wp_stream',
380 array(
381 'i18n' => array(
382 'confirm_purge' => esc_html__( 'Are you sure you want to delete all Stream activity records from the database? This cannot be undone.', 'stream' ),
383 'confirm_defaults' => esc_html__( 'Are you sure you want to reset all site settings to default? This cannot be undone.', 'stream' ),
384 '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' ),
385 ),
386 'locale' => esc_js( $locale ),
387 'gmt_offset' => get_option( 'gmt_offset' ),
388 )
389 );
390
391 wp_localize_script(
392 'wp-stream-live-updates',
393 'wp_stream_live_updates',
394 array(
395 'current_screen' => $hook,
396 'current_page' => isset( $_GET['paged'] ) ? esc_js( $_GET['paged'] ) : '1', // input var okay
397 'current_order' => isset( $_GET['order'] ) ? esc_js( $_GET['order'] ) : 'desc', // input var okay
398 'current_query' => wp_stream_json_encode( $_GET ), // input var okay
399 'current_query_count' => count( $_GET ), // input var okay
400 )
401 );
402 }
403
404 /**
405 * The maximum number of items that can be updated in bulk without receiving a warning.
406 *
407 * Stream watches for bulk actions performed in the WordPress Admin (such as updating
408 * many posts at once) and warns the user before proceeding if the number of items they
409 * are attempting to update exceeds this threshold value. Since Stream will try to save
410 * a log for each item, it will take longer than usual to complete the operation.
411 *
412 * The default threshold is 100 items.
413 *
414 * @return int
415 */
416 $bulk_actions_threshold = apply_filters( 'wp_stream_bulk_actions_threshold', 100 );
417
418 wp_enqueue_script( 'wp-stream-global', $this->plugin->locations['url'] . 'ui/js/global.js', array( 'jquery' ), $this->plugin->get_version() );
419 wp_localize_script(
420 'wp-stream-global',
421 'wp_stream_global',
422 array(
423 'bulk_actions' => array(
424 'i18n' => array(
425 '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 ) ) ),
426 ),
427 'threshold' => absint( $bulk_actions_threshold ),
428 ),
429 'plugins_screen_url' => self_admin_url( 'plugins.php#stream' ),
430 )
431 );
432 }
433
434 /**
435 * Check whether or not the current admin screen belongs to Stream
436 *
437 * @return bool
438 */
439 public function is_stream_screen() {
440 if ( is_admin() && false !== strpos( wp_stream_filter_input( INPUT_GET, 'page' ), $this->records_page_slug ) ) {
441 return true;
442 }
443
444 $screen = get_current_screen();
445 if ( is_admin() && 'post' === $screen->base && Alerts::POST_TYPE === $screen->post_type ) {
446 return true;
447 }
448
449 return false;
450 }
451
452 /**
453 * Add a specific body class to all Stream admin screens
454 *
455 * @param string $classes CSS classes to output to body
456 *
457 * @filter admin_body_class
458 *
459 * @return string
460 */
461 public function admin_body_class( $classes ) {
462 $stream_classes = array();
463
464 if ( $this->is_stream_screen() ) {
465 $stream_classes[] = $this->admin_body_class;
466
467 if ( isset( $_GET['page'] ) ) {
468 $stream_classes[] = sanitize_key( $_GET['page'] ); // input var okay
469 }
470 }
471
472 /**
473 * Filter the Stream admin body classes
474 *
475 * @return array
476 */
477 $stream_classes = apply_filters( 'wp_stream_admin_body_classes', $stream_classes );
478 $stream_classes = implode( ' ', array_map( 'trim', $stream_classes ) );
479
480 return sprintf( '%s %s ', $classes, $stream_classes );
481 }
482
483 /**
484 * Add menu styles for various WP Admin skins
485 *
486 * @uses \wp_add_inline_style()
487 *
488 * @action admin_enqueue_scripts
489 */
490 public function admin_menu_css() {
491 wp_register_style( 'wp-stream-datepicker', $this->plugin->locations['url'] . 'ui/css/datepicker.css', array(), $this->plugin->get_version() );
492 wp_register_style( 'wp-stream-icons', $this->plugin->locations['url'] . 'ui/stream-icons/style.css', array(), $this->plugin->get_version() );
493
494 // Make sure we're working off a clean version
495 if ( ! file_exists( ABSPATH . WPINC . '/version.php' ) ) {
496 return;
497 }
498 include( ABSPATH . WPINC . '/version.php' );
499
500 if ( ! isset( $wp_version ) ) {
501 return;
502 }
503
504 $body_class = $this->admin_body_class;
505 $records_page = $this->records_page_slug;
506 $stream_url = $this->plugin->locations['url'];
507
508 if ( version_compare( $wp_version, '3.8-alpha', '>=' ) ) {
509 wp_enqueue_style( 'wp-stream-icons' );
510
511 $css = "
512 #toplevel_page_{$records_page} .wp-menu-image:before {
513 font-family: 'WP Stream' !important;
514 content: '\\73' !important;
515 }
516 #toplevel_page_{$records_page} .wp-menu-image {
517 background-repeat: no-repeat;
518 }
519 #menu-posts-feedback .wp-menu-image:before {
520 font-family: dashicons !important;
521 content: '\\f175';
522 }
523 #adminmenu #menu-posts-feedback div.wp-menu-image {
524 background: none !important;
525 background-repeat: no-repeat;
526 }
527 body.{$body_class} #wpbody-content .wrap h1:nth-child(1):before {
528 font-family: 'WP Stream' !important;
529 content: '\\73';
530 padding: 0 8px 0 0;
531 }
532 ";
533 } else {
534 $css = "
535 #toplevel_page_{$records_page} .wp-menu-image {
536 background: url( {$stream_url}ui/stream-icons/menuicon-sprite.png ) 0 90% no-repeat;
537 }
538 /* Retina Stream Menu Icon */
539 @media only screen and (-moz-min-device-pixel-ratio: 1.5),
540 only screen and (-o-min-device-pixel-ratio: 3/2),
541 only screen and (-webkit-min-device-pixel-ratio: 1.5),
542 only screen and (min-device-pixel-ratio: 1.5) {
543 #toplevel_page_{$records_page} .wp-menu-image {
544 background: url( {$stream_url}ui/stream-icons/menuicon-sprite-2x.png ) 0 90% no-repeat;
545 background-size:30px 64px;
546 }
547 }
548 #toplevel_page_{$records_page}.current .wp-menu-image,
549 #toplevel_page_{$records_page}.wp-has-current-submenu .wp-menu-image,
550 #toplevel_page_{$records_page}:hover .wp-menu-image {
551 background-position: top left;
552 }
553 ";
554 }
555
556 \wp_add_inline_style( 'wp-admin', $css );
557 }
558
559 public function wp_ajax_reset() {
560 check_ajax_referer( 'stream_nonce', 'wp_stream_nonce' );
561
562 if ( ! current_user_can( $this->settings_cap ) ) {
563 wp_die(
564 esc_html__( "You don't have sufficient privileges to do this action.", 'stream' )
565 );
566 }
567
568 $this->erase_stream_records();
569
570 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
571 return true;
572 }
573
574 wp_redirect(
575 add_query_arg(
576 array(
577 'page' => is_network_admin() ? $this->network->network_settings_page_slug : $this->settings_page_slug,
578 'message' => 'data_erased',
579 ),
580 self_admin_url( $this->admin_parent_page )
581 )
582 );
583
584 exit;
585 }
586
587 private function erase_stream_records() {
588 global $wpdb;
589
590 $where = '';
591
592 if ( is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
593 $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() );
594 }
595
596 $wpdb->query(
597 "DELETE `stream`, `meta`
598 FROM {$wpdb->stream} AS `stream`
599 LEFT JOIN {$wpdb->streammeta} AS `meta`
600 ON `meta`.`record_id` = `stream`.`ID`
601 WHERE 1=1 {$where};" // @codingStandardsIgnoreLine $where already prepared
602 );
603 }
604
605 public function purge_schedule_setup() {
606 if ( ! wp_next_scheduled( 'wp_stream_auto_purge' ) ) {
607 wp_schedule_event( time(), 'twicedaily', 'wp_stream_auto_purge' );
608 }
609 }
610
611 public function purge_scheduled_action() {
612 global $wpdb;
613
614 // Don't purge when in Network Admin unless Stream is network activated
615 if (
616 is_multisite()
617 &&
618 is_network_admin()
619 &&
620 ! is_plugin_active_for_network( $this->plugin->locations['plugin'] )
621 ) {
622 return;
623 }
624
625 if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
626 $options = (array) get_site_option( 'wp_stream_network', array() );
627 } else {
628 $options = (array) get_option( 'wp_stream', array() );
629 }
630
631 if ( ! empty( $options['general_keep_records_indefinitely'] ) || ! isset( $options['general_records_ttl'] ) ) {
632 return;
633 }
634
635 $days = $options['general_records_ttl'];
636 $date = new DateTime( 'now', $timezone = new DateTimeZone( 'UTC' ) );
637
638 $date->sub( DateInterval::createFromDateString( "$days days" ) );
639
640 $where = $wpdb->prepare( ' AND `stream`.`created` < %s', $date->format( 'Y-m-d H:i:s' ) );
641
642 // Multisite but NOT network activated, only purge the current blog
643 if ( is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
644 $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() );
645 }
646
647 $wpdb->query(
648 "DELETE `stream`, `meta`
649 FROM {$wpdb->stream} AS `stream`
650 LEFT JOIN {$wpdb->streammeta} AS `meta`
651 ON `meta`.`record_id` = `stream`.`ID`
652 WHERE 1=1 {$where};" // @codingStandardsIgnoreLine $where already prepared
653 );
654 }
655
656 /**
657 * @param array $links
658 * @param string $file
659 *
660 * @filter plugin_action_links
661 *
662 * @return array
663 */
664 public function plugin_action_links( $links, $file ) {
665 if ( plugin_basename( $this->plugin->locations['dir'] . 'stream.php' ) !== $file ) {
666 return $links;
667 }
668
669 // Also don't show links in Network Admin if Stream isn't network enabled
670 if ( is_network_admin() && is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
671 return $links;
672 }
673
674 if ( is_network_admin() ) {
675 $admin_page_url = add_query_arg( array( 'page' => $this->network->network_settings_page_slug ), network_admin_url( $this->admin_parent_page ) );
676 } else {
677 $admin_page_url = add_query_arg( array( 'page' => $this->settings_page_slug ), admin_url( $this->admin_parent_page ) );
678 }
679
680 $links[] = sprintf( '<a href="%s">%s</a>', esc_url( $admin_page_url ), esc_html__( 'Settings', 'default' ) );
681
682 $url = add_query_arg(
683 array(
684 'action' => 'wp_stream_uninstall',
685 'wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ),
686 ),
687 admin_url( 'admin-ajax.php' )
688 );
689
690 $links[] = sprintf( '<span id="wp_stream_uninstall" class="delete"><a href="%s">%s</a></span>', esc_url( $url ), esc_html__( 'Uninstall', 'stream' ) );
691
692 return $links;
693 }
694
695 /**
696 * Render main page
697 */
698 public function render_list_table() {
699 $this->list_table->prepare_items();
700 ?>
701 <div class="wrap">
702 <h1><?php echo esc_html( get_admin_page_title() ) ?></h1>
703 <?php $this->list_table->display() ?>
704 </div>
705 <?php
706 }
707
708 /**
709 * Render settings page
710 */
711 public function render_settings_page() {
712 $option_key = $this->plugin->settings->option_key;
713 $form_action = apply_filters( 'wp_stream_settings_form_action', admin_url( 'options.php' ) );
714
715 $page_description = apply_filters( 'wp_stream_settings_form_description', '' );
716
717 $sections = $this->plugin->settings->get_fields();
718 $active_tab = wp_stream_filter_input( INPUT_GET, 'tab' );
719
720 wp_enqueue_script( 'wp-stream-settings', $this->plugin->locations['url'] . 'ui/js/settings.js', array( 'jquery' ), $this->plugin->get_version(), true );
721 ?>
722 <div class="wrap">
723 <h1><?php echo esc_html( get_admin_page_title() ) ?></h1>
724
725 <?php if ( ! empty( $page_description ) ) : ?>
726 <p><?php echo esc_html( $page_description ) ?></p>
727 <?php endif; ?>
728
729 <?php settings_errors() ?>
730
731 <?php if ( count( $sections ) > 1 ) : ?>
732 <h2 class="nav-tab-wrapper">
733 <?php $i = 0 ?>
734 <?php foreach ( $sections as $section => $data ) : ?>
735 <?php $i ++ ?>
736 <?php $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section ) ?>
737 <a href="<?php echo esc_url( add_query_arg( 'tab', $section ) ) ?>" class="nav-tab<?php if ( $is_active ) { echo esc_attr( ' nav-tab-active' ); } ?>">
738 <?php echo esc_html( $data['title'] ) ?>
739 </a>
740 <?php endforeach; ?>
741 </h2>
742 <?php endif; ?>
743
744 <div class="nav-tab-content" id="tab-content-settings">
745 <form method="post" action="<?php echo esc_attr( $form_action ) ?>" enctype="multipart/form-data">
746 <div class="settings-sections">
747 <?php
748 $i = 0;
749 foreach ( $sections as $section => $data ) {
750 $i++;
751
752 $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section );
753
754 if ( $is_active ) {
755 settings_fields( $option_key );
756 do_settings_sections( $option_key );
757 }
758 }
759 ?>
760 </div>
761 <?php submit_button() ?>
762 </form>
763 </div>
764 </div>
765 <?php
766 }
767
768 /**
769 * Instantiate the list table
770 */
771 public function register_list_table() {
772 $this->list_table = new List_Table( $this->plugin, array( 'screen' => $this->screen_id['main'] ) );
773 }
774
775 /**
776 * Check if a particular role has access
777 *
778 * @param string $role
779 *
780 * @return bool
781 */
782 private function role_can_view( $role ) {
783 if ( in_array( $role, $this->plugin->settings->options['general_role_access'], true ) ) {
784 return true;
785 }
786
787 return false;
788 }
789
790 /**
791 * Filter user caps to dynamically grant our view cap based on allowed roles
792 *
793 * @param $allcaps
794 * @param $caps
795 * @param $args
796 * @param $user
797 *
798 * @filter user_has_cap
799 *
800 * @return array
801 */
802 public function filter_user_caps( $allcaps, $caps, $args, $user = null ) {
803 global $wp_roles;
804
805 $_wp_roles = isset( $wp_roles ) ? $wp_roles : new WP_Roles();
806
807 $user = is_a( $user, 'WP_User' ) ? $user : wp_get_current_user();
808
809 // @see
810 // https://github.com/WordPress/WordPress/blob/c67c9565f1495255807069fdb39dac914046b1a0/wp-includes/capabilities.php#L758
811 $roles = array_unique(
812 array_merge(
813 $user->roles,
814 array_filter(
815 array_keys( $user->caps ),
816 array( $_wp_roles, 'is_role' )
817 )
818 )
819 );
820
821 $stream_view_caps = array( $this->view_cap );
822
823 foreach ( $caps as $cap ) {
824 if ( in_array( $cap, $stream_view_caps, true ) ) {
825 foreach ( $roles as $role ) {
826 if ( $this->role_can_view( $role ) ) {
827 $allcaps[ $cap ] = true;
828
829 break 2;
830 }
831 }
832 }
833 }
834
835 return $allcaps;
836 }
837
838 /**
839 * Filter role caps to dynamically grant our view cap based on allowed roles
840 *
841 * @filter role_has_cap
842 *
843 * @param $allcaps
844 * @param $cap
845 * @param $role
846 *
847 * @return array
848 */
849 public function filter_role_caps( $allcaps, $cap, $role ) {
850 $stream_view_caps = array( $this->view_cap );
851
852 if ( in_array( $cap, $stream_view_caps, true ) && $this->role_can_view( $role ) ) {
853 $allcaps[ $cap ] = true;
854 }
855
856 return $allcaps;
857 }
858
859 /**
860 * @action wp_ajax_wp_stream_filters
861 */
862 public function ajax_filters() {
863 if ( ! defined( 'DOING_AJAX' ) || ! current_user_can( $this->plugin->admin->settings_cap ) ) {
864 wp_die( '-1' );
865 }
866
867 check_ajax_referer( 'stream_filters_user_search_nonce', 'nonce' );
868
869 switch ( wp_stream_filter_input( INPUT_GET, 'filter' ) ) {
870 case 'user_id':
871 $users = array_merge(
872 array( 0 => (object) array( 'display_name' => 'WP-CLI' ) ),
873 get_users()
874 );
875
876 $search = wp_stream_filter_input( INPUT_GET, 'q' );
877 if ( $search ) {
878 // `search` arg for get_users() is not enough
879 $users = array_filter(
880 $users,
881 function( $user ) use ( $search ) {
882 return false !== mb_strpos( mb_strtolower( $user->display_name ), mb_strtolower( $search ) );
883 }
884 );
885 }
886
887 if ( count( $users ) > $this->preload_users_max ) {
888 $users = array_slice( $users, 0, $this->preload_users_max );
889 }
890
891 // Get gravatar / roles for final result set
892 $results = $this->get_users_record_meta( $users );
893
894 break;
895 }
896
897 if ( isset( $results ) ) {
898 echo wp_stream_json_encode( $results ); // xss ok
899 }
900
901 die();
902 }
903
904 public function get_users_record_meta( $authors ) {
905 $authors_records = array();
906
907 foreach ( $authors as $user_id => $args ) {
908 $author = new Author( $args->ID );
909
910 $authors_records[ $user_id ] = array(
911 'text' => $author->get_display_name(),
912 'id' => $author->id,
913 'label' => $author->get_display_name(),
914 'icon' => $author->get_avatar_src( 32 ),
915 'title' => '',
916 );
917 }
918
919 return $authors_records;
920 }
921
922 /**
923 * Get user meta in a way that is also safe for VIP
924 *
925 * @param int $user_id
926 * @param string $meta_key
927 * @param bool $single (optional)
928 *
929 * @return mixed
930 */
931 function get_user_meta( $user_id, $meta_key, $single = true ) {
932 if ( wp_stream_is_vip() && function_exists( 'get_user_attribute' ) ) {
933 return get_user_attribute( $user_id, $meta_key );
934 }
935 return get_user_meta( $user_id, $meta_key, $single );
936 }
937
938 /**
939 * Update user meta in a way that is also safe for VIP
940 *
941 * @param int $user_id
942 * @param string $meta_key
943 * @param mixed $meta_value
944 * @param mixed $prev_value (optional)
945 *
946 * @return int|bool
947 */
948 function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
949 if ( wp_stream_is_vip() && function_exists( 'update_user_attribute' ) ) {
950 return update_user_attribute( $user_id, $meta_key, $meta_value );
951 }
952 return update_user_meta( $user_id, $meta_key, $meta_value, $prev_value );
953 }
954
955 /**
956 * Delete user meta in a way that is also safe for VIP
957 *
958 * @param int $user_id
959 * @param string $meta_key
960 * @param mixed $meta_value (optional)
961 *
962 * @return bool
963 */
964 function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) {
965 if ( wp_stream_is_vip() && function_exists( 'delete_user_attribute' ) ) {
966 return delete_user_attribute( $user_id, $meta_key, $meta_value );
967 }
968 return delete_user_meta( $user_id, $meta_key, $meta_value );
969 }
970 }
971