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

1,002 lines 27.2 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 // Ajax user's name by ID.
176 add_action( 'wp_ajax_wp_stream_get_filter_value_by_id', array( $this, 'get_filter_value_by_id' ) );
177
178 // Ajax users list.
179 add_action( 'wp_ajax_wp_stream_filters', array( $this, 'ajax_filters' ) );
180
181 // Ajax user's name by ID.
182 add_action( 'wp_ajax_wp_stream_get_filter_value_by_id', array( $this, 'get_filter_value_by_id' ) );
183 }
184
185 /**
186 * Load admin classes
187 *
188 * @action init
189 */
190 public function init() {
191 $this->network = new Network( $this->plugin );
192 $this->live_update = new Live_Update( $this->plugin );
193 $this->export = new Export( $this->plugin );
194 }
195
196 /**
197 * Output specific updates passed as URL parameters.
198 *
199 * @action admin_notices
200 *
201 * @return void
202 */
203 public function prepare_admin_notices() {
204 $message = wp_stream_filter_input( INPUT_GET, 'message' );
205
206 switch ( $message ) {
207 case 'settings_reset':
208 $this->notice( esc_html__( 'All site settings have been successfully reset.', 'stream' ) );
209 break;
210 }
211 }
212
213 /**
214 * Handle notice messages according to the appropriate context (WP-CLI or the WP Admin)
215 *
216 * @param string $message Message to output.
217 * @param bool $is_error If the message is error_level (true) or warning (false).
218 */
219 public function notice( $message, $is_error = true ) {
220 if ( defined( 'WP_CLI' ) && WP_CLI ) {
221 $message = strip_tags( $message );
222
223 if ( $is_error ) {
224 WP_CLI::warning( $message );
225 } else {
226 WP_CLI::success( $message );
227 }
228 } else {
229 // Trigger admin notices late, so that any notices which occur during page load are displayed.
230 add_action( 'shutdown', array( $this, 'admin_notices' ) );
231
232 $notice = compact( 'message', 'is_error' );
233
234 if ( ! in_array( $notice, $this->notices, true ) ) {
235 $this->notices[] = $notice;
236 }
237 }
238 }
239
240 /**
241 * Show an error or other message in the WP Admin
242 *
243 * @action shutdown
244 */
245 public function admin_notices() {
246 global $allowedposttags;
247
248 $custom = array(
249 'progress' => array(
250 'class' => true,
251 'id' => true,
252 'max' => true,
253 'style' => true,
254 'value' => true,
255 ),
256 );
257
258 $allowed_html = array_merge( $allowedposttags, $custom );
259
260 ksort( $allowed_html );
261
262 foreach ( $this->notices as $notice ) {
263 $class_name = empty( $notice['is_error'] ) ? 'updated' : 'error';
264 $html_message = sprintf( '<div class="%s">%s</div>', esc_attr( $class_name ), wpautop( $notice['message'] ) );
265
266 echo wp_kses( $html_message, $allowed_html );
267 }
268 }
269
270 /**
271 * Register menu page
272 *
273 * @action admin_menu
274 *
275 * @return void
276 */
277 public function register_menu() {
278 /**
279 * Filter the main admin menu title
280 *
281 * @return string
282 */
283 $main_menu_title = apply_filters( 'wp_stream_admin_menu_title', esc_html__( 'Stream', 'stream' ) );
284
285 /**
286 * Filter the main admin menu position
287 *
288 * Note: Using longtail decimal string to reduce the chance of position conflicts, see Codex
289 *
290 * @return string
291 */
292 $main_menu_position = apply_filters( 'wp_stream_menu_position', '2.999999' );
293
294 /**
295 * Filter the main admin page title
296 *
297 * @return string
298 */
299 $main_page_title = apply_filters( 'wp_stream_admin_page_title', esc_html__( 'Stream Records', 'stream' ) );
300
301 $this->screen_id['main'] = add_menu_page(
302 $main_page_title,
303 $main_menu_title,
304 $this->view_cap,
305 $this->records_page_slug,
306 array( $this, 'render_list_table' ),
307 'div',
308 $main_menu_position
309 );
310
311 /**
312 * Filter the Settings admin page title
313 *
314 * @return string
315 */
316 $settings_page_title = apply_filters( 'wp_stream_settings_form_title', esc_html__( 'Stream Settings', 'stream' ) );
317
318 $this->screen_id['settings'] = add_submenu_page(
319 $this->records_page_slug,
320 $settings_page_title,
321 esc_html__( 'Settings', 'stream' ),
322 $this->settings_cap,
323 $this->settings_page_slug,
324 array( $this, 'render_settings_page' )
325 );
326
327 if ( isset( $this->screen_id['main'] ) ) {
328 /**
329 * Fires just before the Stream list table is registered.
330 *
331 * @return void
332 */
333 do_action( 'wp_stream_admin_menu_screens' );
334
335 // Register the list table early, so it associates the column headers with 'Screen settings'.
336 add_action( 'load-' . $this->screen_id['main'], array( $this, 'register_list_table' ) );
337 }
338 }
339
340 /**
341 * Enqueue scripts/styles for admin screen
342 *
343 * @action admin_enqueue_scripts
344 *
345 * @param string $hook
346 *
347 * @return void
348 */
349 public function admin_enqueue_scripts( $hook ) {
350 wp_register_script( 'wp-stream-select2', $this->plugin->locations['url'] . 'ui/lib/select2/select2.js', array( 'jquery' ), '3.5.2', true );
351 wp_register_style( 'wp-stream-select2', $this->plugin->locations['url'] . 'ui/lib/select2/select2.css', array(), '3.5.2' );
352 wp_register_script( 'wp-stream-timeago', $this->plugin->locations['url'] . 'ui/lib/timeago/jquery.timeago.js', array(), '1.4.1', true );
353
354 $locale = strtolower( substr( get_locale(), 0, 2 ) );
355 $file_tmpl = 'ui/lib/timeago/locales/jquery.timeago.%s.js';
356
357 if ( file_exists( $this->plugin->locations['dir'] . sprintf( $file_tmpl, $locale ) ) ) {
358 wp_register_script( 'wp-stream-timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, $locale ), array( 'wp-stream-timeago' ), '1' );
359 } else {
360 wp_register_script( 'wp-stream-timeago-locale', $this->plugin->locations['url'] . sprintf( $file_tmpl, 'en' ), array( 'wp-stream-timeago' ), '1' );
361 }
362
363 wp_enqueue_style( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/css/admin.css', array(), $this->plugin->get_version() );
364
365 $script_screens = array( 'plugins.php' );
366
367 if ( in_array( $hook, $this->screen_id, true ) || in_array( $hook, $script_screens, true ) ) {
368 wp_enqueue_script( 'wp-stream-select2' );
369 wp_enqueue_style( 'wp-stream-select2' );
370
371 wp_enqueue_script( 'wp-stream-timeago' );
372 wp_enqueue_script( 'wp-stream-timeago-locale' );
373
374 wp_enqueue_script( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/js/admin.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 return false;
445 }
446
447 /**
448 * Add a specific body class to all Stream admin screens
449 *
450 * @param string $classes CSS classes to output to body
451 *
452 * @filter admin_body_class
453 *
454 * @return string
455 */
456 public function admin_body_class( $classes ) {
457 $stream_classes = array();
458
459 if ( $this->is_stream_screen() ) {
460 $stream_classes[] = $this->admin_body_class;
461
462 if ( isset( $_GET['page'] ) ) {
463 $stream_classes[] = sanitize_key( $_GET['page'] ); // input var okay
464 }
465 }
466
467 /**
468 * Filter the Stream admin body classes
469 *
470 * @return array
471 */
472 $stream_classes = apply_filters( 'wp_stream_admin_body_classes', $stream_classes );
473 $stream_classes = implode( ' ', array_map( 'trim', $stream_classes ) );
474
475 return sprintf( '%s %s ', $classes, $stream_classes );
476 }
477
478 /**
479 * Add menu styles for various WP Admin skins
480 *
481 * @uses \wp_add_inline_style()
482 *
483 * @action admin_enqueue_scripts
484 */
485 public function admin_menu_css() {
486 wp_register_style( 'wp-stream-datepicker', $this->plugin->locations['url'] . 'ui/css/datepicker.css', array(), $this->plugin->get_version() );
487 wp_register_style( 'wp-stream-icons', $this->plugin->locations['url'] . 'ui/stream-icons/style.css', array(), $this->plugin->get_version() );
488
489 // Make sure we're working off a clean version
490 if ( ! file_exists( ABSPATH . WPINC . '/version.php' ) ) {
491 return;
492 }
493 include( ABSPATH . WPINC . '/version.php' );
494
495 if ( ! isset( $wp_version ) ) {
496 return;
497 }
498
499 $body_class = $this->admin_body_class;
500 $records_page = $this->records_page_slug;
501 $stream_url = $this->plugin->locations['url'];
502
503 if ( version_compare( $wp_version, '3.8-alpha', '>=' ) ) {
504 wp_enqueue_style( 'wp-stream-icons' );
505
506 $css = "
507 #toplevel_page_{$records_page} .wp-menu-image:before {
508 font-family: 'WP Stream' !important;
509 content: '\\73' !important;
510 }
511 #toplevel_page_{$records_page} .wp-menu-image {
512 background-repeat: no-repeat;
513 }
514 #menu-posts-feedback .wp-menu-image:before {
515 font-family: dashicons !important;
516 content: '\\f175';
517 }
518 #adminmenu #menu-posts-feedback div.wp-menu-image {
519 background: none !important;
520 background-repeat: no-repeat;
521 }
522 body.{$body_class} #wpbody-content .wrap h1:nth-child(1):before {
523 font-family: 'WP Stream' !important;
524 content: '\\73';
525 padding: 0 8px 0 0;
526 }
527 ";
528 } else {
529 $css = "
530 #toplevel_page_{$records_page} .wp-menu-image {
531 background: url( {$stream_url}ui/stream-icons/menuicon-sprite.png ) 0 90% no-repeat;
532 }
533 /* Retina Stream Menu Icon */
534 @media only screen and (-moz-min-device-pixel-ratio: 1.5),
535 only screen and (-o-min-device-pixel-ratio: 3/2),
536 only screen and (-webkit-min-device-pixel-ratio: 1.5),
537 only screen and (min-device-pixel-ratio: 1.5) {
538 #toplevel_page_{$records_page} .wp-menu-image {
539 background: url( {$stream_url}ui/stream-icons/menuicon-sprite-2x.png ) 0 90% no-repeat;
540 background-size:30px 64px;
541 }
542 }
543 #toplevel_page_{$records_page}.current .wp-menu-image,
544 #toplevel_page_{$records_page}.wp-has-current-submenu .wp-menu-image,
545 #toplevel_page_{$records_page}:hover .wp-menu-image {
546 background-position: top left;
547 }
548 ";
549 }
550
551 \wp_add_inline_style( 'wp-admin', $css );
552 }
553
554 public function wp_ajax_reset() {
555 check_ajax_referer( 'stream_nonce', 'wp_stream_nonce' );
556
557 if ( ! current_user_can( $this->settings_cap ) ) {
558 wp_die(
559 esc_html__( "You don't have sufficient privileges to do this action.", 'stream' )
560 );
561 }
562
563 $this->erase_stream_records();
564
565 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
566 return true;
567 }
568
569 wp_redirect(
570 add_query_arg(
571 array(
572 'page' => is_network_admin() ? $this->network->network_settings_page_slug : $this->settings_page_slug,
573 'message' => 'data_erased',
574 ),
575 self_admin_url( $this->admin_parent_page )
576 )
577 );
578
579 exit;
580 }
581
582 private function erase_stream_records() {
583 global $wpdb;
584
585 $where = '';
586
587 if ( is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
588 $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() );
589 }
590
591 $wpdb->query(
592 "DELETE `stream`, `meta`
593 FROM {$wpdb->stream} AS `stream`
594 LEFT JOIN {$wpdb->streammeta} AS `meta`
595 ON `meta`.`record_id` = `stream`.`ID`
596 WHERE 1=1 {$where};" // @codingStandardsIgnoreLine $where already prepared
597 );
598 }
599
600 public function purge_schedule_setup() {
601 if ( ! wp_next_scheduled( 'wp_stream_auto_purge' ) ) {
602 wp_schedule_event( time(), 'twicedaily', 'wp_stream_auto_purge' );
603 }
604 }
605
606 public function purge_scheduled_action() {
607 global $wpdb;
608
609 // Don't purge when in Network Admin unless Stream is network activated
610 if (
611 is_multisite()
612 &&
613 is_network_admin()
614 &&
615 ! is_plugin_active_for_network( $this->plugin->locations['plugin'] )
616 ) {
617 return;
618 }
619
620 if ( is_multisite() && is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
621 $options = (array) get_site_option( 'wp_stream_network', array() );
622 } else {
623 $options = (array) get_option( 'wp_stream', array() );
624 }
625
626 if ( isset( $options['general_keep_records_indefinitely'] ) || ! isset( $options['general_records_ttl'] ) ) {
627 return;
628 }
629
630 $days = $options['general_records_ttl'];
631 $date = new DateTime( 'now', $timezone = new DateTimeZone( 'UTC' ) );
632
633 $date->sub( DateInterval::createFromDateString( "$days days" ) );
634
635 $where = $wpdb->prepare( ' AND `stream`.`created` < %s', $date->format( 'Y-m-d H:i:s' ) );
636
637 // Multisite but NOT network activated, only purge the current blog
638 if ( is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
639 $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() );
640 }
641
642 $wpdb->query(
643 "DELETE `stream`, `meta`
644 FROM {$wpdb->stream} AS `stream`
645 LEFT JOIN {$wpdb->streammeta} AS `meta`
646 ON `meta`.`record_id` = `stream`.`ID`
647 WHERE 1=1 {$where};" // @codingStandardsIgnoreLine $where already prepared
648 );
649 }
650
651 /**
652 * @param array $links
653 * @param string $file
654 *
655 * @filter plugin_action_links
656 *
657 * @return array
658 */
659 public function plugin_action_links( $links, $file ) {
660 if ( plugin_basename( $this->plugin->locations['dir'] . 'stream.php' ) !== $file ) {
661 return $links;
662 }
663
664 // Also don't show links in Network Admin if Stream isn't network enabled
665 if ( is_network_admin() && is_multisite() && ! is_plugin_active_for_network( $this->plugin->locations['plugin'] ) ) {
666 return $links;
667 }
668
669 if ( is_network_admin() ) {
670 $admin_page_url = add_query_arg( array( 'page' => $this->network->network_settings_page_slug ), network_admin_url( $this->admin_parent_page ) );
671 } else {
672 $admin_page_url = add_query_arg( array( 'page' => $this->settings_page_slug ), admin_url( $this->admin_parent_page ) );
673 }
674
675 $links[] = sprintf( '<a href="%s">%s</a>', esc_url( $admin_page_url ), esc_html__( 'Settings', 'default' ) );
676
677 $url = add_query_arg(
678 array(
679 'action' => 'wp_stream_uninstall',
680 'wp_stream_nonce' => wp_create_nonce( 'stream_nonce' ),
681 ),
682 admin_url( 'admin-ajax.php' )
683 );
684
685 $links[] = sprintf( '<span id="wp_stream_uninstall" class="delete"><a href="%s">%s</a></span>', esc_url( $url ), esc_html__( 'Uninstall', 'stream' ) );
686
687 return $links;
688 }
689
690 /**
691 * Render main page
692 */
693 public function render_list_table() {
694 $this->list_table->prepare_items();
695 ?>
696 <div class="wrap">
697 <h1><?php echo esc_html( get_admin_page_title() ) ?></h1>
698 <?php $this->list_table->display() ?>
699 </div>
700 <?php
701 }
702
703 /**
704 * Render settings page
705 */
706 public function render_settings_page() {
707 $option_key = $this->plugin->settings->option_key;
708 $form_action = apply_filters( 'wp_stream_settings_form_action', admin_url( 'options.php' ) );
709
710 $page_description = apply_filters( 'wp_stream_settings_form_description', '' );
711
712 $sections = $this->plugin->settings->get_fields();
713 $active_tab = wp_stream_filter_input( INPUT_GET, 'tab' );
714
715 wp_enqueue_script( 'wp-stream-settings', $this->plugin->locations['url'] . 'ui/js/settings.js', array( 'jquery' ), $this->plugin->get_version(), true );
716 ?>
717 <div class="wrap">
718 <h1><?php echo esc_html( get_admin_page_title() ) ?></h1>
719
720 <?php if ( ! empty( $page_description ) ) : ?>
721 <p><?php echo esc_html( $page_description ) ?></p>
722 <?php endif; ?>
723
724 <?php settings_errors() ?>
725
726 <?php if ( count( $sections ) > 1 ) : ?>
727 <h2 class="nav-tab-wrapper">
728 <?php $i = 0 ?>
729 <?php foreach ( $sections as $section => $data ) : ?>
730 <?php $i ++ ?>
731 <?php $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section ) ?>
732 <a href="<?php echo esc_url( add_query_arg( 'tab', $section ) ) ?>" class="nav-tab<?php if ( $is_active ) { echo esc_attr( ' nav-tab-active' ); } ?>">
733 <?php echo esc_html( $data['title'] ) ?>
734 </a>
735 <?php endforeach; ?>
736 </h2>
737 <?php endif; ?>
738
739 <div class="nav-tab-content" id="tab-content-settings">
740 <form method="post" action="<?php echo esc_attr( $form_action ) ?>" enctype="multipart/form-data">
741 <div class="settings-sections">
742 <?php
743 $i = 0;
744 foreach ( $sections as $section => $data ) {
745 $i++;
746
747 $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section );
748
749 if ( $is_active ) {
750 settings_fields( $option_key );
751 do_settings_sections( $option_key );
752 }
753 }
754 ?>
755 </div>
756 <?php submit_button() ?>
757 </form>
758 </div>
759 </div>
760 <?php
761 }
762
763 /**
764 * Instantiate the list table
765 */
766 public function register_list_table() {
767 $this->list_table = new List_Table( $this->plugin, array( 'screen' => $this->screen_id['main'] ) );
768 }
769
770 /**
771 * Check if a particular role has access
772 *
773 * @param string $role
774 *
775 * @return bool
776 */
777 private function role_can_view( $role ) {
778 if ( in_array( $role, $this->plugin->settings->options['general_role_access'], true ) ) {
779 return true;
780 }
781
782 return false;
783 }
784
785 /**
786 * Filter user caps to dynamically grant our view cap based on allowed roles
787 *
788 * @param $allcaps
789 * @param $caps
790 * @param $args
791 * @param $user
792 *
793 * @filter user_has_cap
794 *
795 * @return array
796 */
797 public function filter_user_caps( $allcaps, $caps, $args, $user = null ) {
798 global $wp_roles;
799
800 $_wp_roles = isset( $wp_roles ) ? $wp_roles : new WP_Roles();
801
802 $user = is_a( $user, 'WP_User' ) ? $user : wp_get_current_user();
803
804 // @see
805 // https://github.com/WordPress/WordPress/blob/c67c9565f1495255807069fdb39dac914046b1a0/wp-includes/capabilities.php#L758
806 $roles = array_unique(
807 array_merge(
808 $user->roles,
809 array_filter(
810 array_keys( $user->caps ),
811 array( $_wp_roles, 'is_role' )
812 )
813 )
814 );
815
816 $stream_view_caps = array( $this->view_cap );
817
818 foreach ( $caps as $cap ) {
819 if ( in_array( $cap, $stream_view_caps, true ) ) {
820 foreach ( $roles as $role ) {
821 if ( $this->role_can_view( $role ) ) {
822 $allcaps[ $cap ] = true;
823
824 break 2;
825 }
826 }
827 }
828 }
829
830 return $allcaps;
831 }
832
833 /**
834 * Filter role caps to dynamically grant our view cap based on allowed roles
835 *
836 * @filter role_has_cap
837 *
838 * @param $allcaps
839 * @param $cap
840 * @param $role
841 *
842 * @return array
843 */
844 public function filter_role_caps( $allcaps, $cap, $role ) {
845 $stream_view_caps = array( $this->view_cap );
846
847 if ( in_array( $cap, $stream_view_caps, true ) && $this->role_can_view( $role ) ) {
848 $allcaps[ $cap ] = true;
849 }
850
851 return $allcaps;
852 }
853
854 /**
855 * @action wp_ajax_wp_stream_filters
856 */
857 public function ajax_filters() {
858 switch ( wp_stream_filter_input( INPUT_GET, 'filter' ) ) {
859 case 'user_id':
860 $users = array_merge(
861 array( 0 => (object) array( 'display_name' => 'WP-CLI' ) ),
862 get_users()
863 );
864
865 $search = wp_stream_filter_input( INPUT_GET, 'q' );
866 if ( $search ) {
867 // `search` arg for get_users() is not enough
868 $users = array_filter(
869 $users,
870 function( $user ) use ( $search ) {
871 return false !== mb_strpos( mb_strtolower( $user->display_name ), mb_strtolower( $search ) );
872 }
873 );
874 }
875
876 if ( count( $users ) > $this->preload_users_max ) {
877 $users = array_slice( $users, 0, $this->preload_users_max );
878 }
879
880 // Get gravatar / roles for final result set
881 $results = $this->get_users_record_meta( $users );
882
883 break;
884 }
885
886 if ( isset( $results ) ) {
887 echo wp_stream_json_encode( array_values( $results ) ); // xss ok
888 }
889
890 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
891 return;
892 }
893
894 die();
895 }
896
897 /**
898 * @action wp_ajax_wp_stream_get_filter_value_by_id
899 */
900 public function get_filter_value_by_id() {
901 $filter = wp_stream_filter_input( INPUT_POST, 'filter' );
902
903 switch ( $filter ) {
904 case 'user_id':
905 $id = wp_stream_filter_input( INPUT_POST, 'id' );
906
907 if ( '0' === $id ) {
908 $value = 'WP-CLI';
909
910 break;
911 }
912
913 $user = get_userdata( $id );
914
915 if ( ! $user || is_wp_error( $user ) ) {
916 $value = '';
917 } else {
918 $value = $user->display_name;
919 }
920
921 break;
922 default:
923 $value = '';
924 }
925
926 echo wp_stream_json_encode( $value ); // xss ok
927
928 if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
929 return;
930 }
931
932 die();
933 }
934
935 public function get_users_record_meta( $authors ) {
936 $authors_records = array();
937
938 foreach ( $authors as $user_id => $args ) {
939 $author = new Author( $user_id );
940
941 $authors_records[ $user_id ] = array(
942 'text' => $author->get_display_name(),
943 'id' => $user_id,
944 'label' => $author->get_display_name(),
945 'icon' => $author->get_avatar_src( 32 ),
946 'title' => '',
947 );
948 }
949
950 return $authors_records;
951 }
952
953 /**
954 * Get user meta in a way that is also safe for VIP
955 *
956 * @param int $user_id
957 * @param string $meta_key
958 * @param bool $single (optional)
959 *
960 * @return mixed
961 */
962 function get_user_meta( $user_id, $meta_key, $single = true ) {
963 if ( wp_stream_is_vip() && function_exists( 'get_user_attribute' ) ) {
964 return get_user_attribute( $user_id, $meta_key );
965 }
966 return get_user_meta( $user_id, $meta_key, $single );
967 }
968
969 /**
970 * Update user meta in a way that is also safe for VIP
971 *
972 * @param int $user_id
973 * @param string $meta_key
974 * @param mixed $meta_value
975 * @param mixed $prev_value (optional)
976 *
977 * @return int|bool
978 */
979 function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
980 if ( wp_stream_is_vip() && function_exists( 'update_user_attribute' ) ) {
981 return update_user_attribute( $user_id, $meta_key, $meta_value );
982 }
983 return update_user_meta( $user_id, $meta_key, $meta_value, $prev_value );
984 }
985
986 /**
987 * Delete user meta in a way that is also safe for VIP
988 *
989 * @param int $user_id
990 * @param string $meta_key
991 * @param mixed $meta_value (optional)
992 *
993 * @return bool
994 */
995 function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) {
996 if ( wp_stream_is_vip() && function_exists( 'delete_user_attribute' ) ) {
997 return delete_user_attribute( $user_id, $meta_key, $meta_value );
998 }
999 return delete_user_meta( $user_id, $meta_key, $meta_value );
1000 }
1001 }
1002