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

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