• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

xwp / stream / 5862

pending completion
5862

push

travis-ci-com

schlessera
Adapt version strings and changelog for hotfix release 3.9.3

3922 of 8537 relevant lines covered (45.94%)

4.97 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.05
/classes/class-admin.php
1
<?php
2
/**
3
 * Centralized manager for WordPress backend functionality.
4
 *
5
 * @package WP_Stream
6
 */
7

8
namespace WP_Stream;
9

10
use DateTime;
11
use DateTimeZone;
12
use DateInterval;
13
use \WP_CLI;
14
use \WP_Roles;
15

16
/**
17
 * Class - Admin
18
 */
19
class Admin {
20

21
        /**
22
         * Holds Instance of plugin object
23
         *
24
         * @var Plugin
25
         */
26
        public $plugin;
27

28
        /**
29
         * Holds Network class
30
         *
31
         * @var Network
32
         */
33
        public $network;
34

35
        /**
36
         * Holds Live Update class
37
         *
38
         * @var Live_Update
39
         */
40
        public $live_update;
41

42
        /**
43
         * Holds Export class
44
         *
45
         * @var Export
46
         */
47
        public $export;
48

49
        /**
50
         * Menu page screen id
51
         *
52
         * @var string
53
         */
54
        public $screen_id = array();
55

56
        /**
57
         * List table object
58
         *
59
         * @var List_Table
60
         */
61
        public $list_table = null;
62

63
        /**
64
         * Option to disable access to Stream
65
         *
66
         * @var bool
67
         */
68
        public $disable_access = false;
69

70
        /**
71
         * Class applied to the body of the admin screen
72
         *
73
         * @var string
74
         */
75
        public $admin_body_class = 'wp_stream_screen';
76

77
        /**
78
         * Slug of the records page
79
         *
80
         * @var string
81
         */
82
        public $records_page_slug = 'wp_stream';
83

84
        /**
85
         * Slug of the settings page
86
         *
87
         * @var string
88
         */
89
        public $settings_page_slug = 'wp_stream_settings';
90

91
        /**
92
         * Parent page of the records and settings pages
93
         *
94
         * @var string
95
         */
96
        public $admin_parent_page = 'admin.php';
97

98
        /**
99
         * Capability name for viewing records
100
         *
101
         * @var string
102
         */
103
        public $view_cap = 'view_stream';
104

105
        /**
106
         * Capability name for viewing settings
107
         *
108
         * @var string
109
         */
110
        public $settings_cap = 'manage_options';
111

112
        /**
113
         * Total amount of authors to pre-load
114
         *
115
         * @var int
116
         */
117
        public $preload_users_max = 50;
118

119
        /**
120
         * Admin notices, collected and displayed on proper action
121
         *
122
         * @var array
123
         */
124
        public $notices = array();
125

126
        /**
127
         * Class constructor.
128
         *
129
         * @param Plugin $plugin Instance of plugin object.
130
         */
131
        public function __construct( $plugin ) {
132
                $this->plugin = $plugin;
×
133

134
                add_action( 'init', array( $this, 'init' ) );
×
135

136
                // Ensure function used in various methods is pre-loaded.
137
                if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
×
138
                        require_once ABSPATH . '/wp-admin/includes/plugin.php';
×
139
                }
140

141
                // User and role caps.
142
                add_filter( 'user_has_cap', array( $this, 'filter_user_caps' ), 10, 4 );
×
143
                add_filter( 'role_has_cap', array( $this, 'filter_role_caps' ), 10, 3 );
×
144

145
                if ( is_multisite() && $plugin->is_network_activated() && ! is_network_admin() ) {
×
146
                        $options = (array) get_site_option( 'wp_stream_network', array() );
×
147
                        $option  = isset( $options['general_site_access'] ) ? absint( $options['general_site_access'] ) : 1;
×
148

149
                        $this->disable_access = ( $option ) ? false : true;
×
150
                }
151

152
                // Register settings page.
153
                if ( ! $this->disable_access ) {
×
154
                        add_action( 'admin_menu', array( $this, 'register_menu' ) );
×
155
                }
156

157
                // Admin notices.
158
                add_action( 'admin_notices', array( $this, 'prepare_admin_notices' ) );
×
159
                add_action( 'shutdown', array( $this, 'admin_notices' ) );
×
160

161
                // Add admin body class.
162
                add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
×
163

164
                // Plugin action links.
165
                add_filter(
×
166
                        'plugin_action_links',
×
167
                        array(
168
                                $this,
×
169
                                'plugin_action_links',
×
170
                        ),
171
                        10,
×
172
                        2
×
173
                );
174

175
                // Load admin scripts and styles.
176
                add_action(
×
177
                        'admin_enqueue_scripts',
×
178
                        array(
179
                                $this,
×
180
                                'admin_enqueue_scripts',
×
181
                        )
182
                );
183
                add_action( 'admin_enqueue_scripts', array( $this, 'admin_menu_css' ) );
×
184

185
                // Reset Streams database.
186
                add_action(
×
187
                        'wp_ajax_wp_stream_reset',
×
188
                        array(
189
                                $this,
×
190
                                'wp_ajax_reset',
×
191
                        )
192
                );
193

194
                // Auto purge setup.
195
                add_action( 'wp_loaded', array( $this, 'purge_schedule_setup' ) );
×
196
                add_action(
×
197
                        'wp_stream_auto_purge',
×
198
                        array(
199
                                $this,
×
200
                                'purge_scheduled_action',
×
201
                        )
202
                );
203

204
                // Ajax users list.
205
                add_action(
×
206
                        'wp_ajax_wp_stream_filters',
×
207
                        array(
208
                                $this,
×
209
                                'ajax_filters',
×
210
                        )
211
                );
212
        }
×
213

214
        /**
215
         * Load admin classes
216
         *
217
         * @action init
218
         */
219
        public function init() {
220
                $this->network     = new Network( $this->plugin );
1✔
221
                $this->live_update = new Live_Update( $this->plugin );
1✔
222
                $this->export      = new Export( $this->plugin );
1✔
223
        }
1✔
224

225
        /**
226
         * Output specific updates passed as URL parameters.
227
         *
228
         * @action admin_notices
229
         *
230
         * @return void
231
         */
232
        public function prepare_admin_notices() {
233
                $message = wp_stream_filter_input( INPUT_GET, 'message' );
1✔
234

235
                switch ( $message ) {
236
                        case 'settings_reset':
1✔
237
                                $this->notice( esc_html__( 'All site settings have been successfully reset.', 'stream' ) );
1✔
238
                                break;
1✔
239
                }
240
        }
1✔
241

242
        /**
243
         * Handle notice messages according to the appropriate context (WP-CLI or the WP Admin)
244
         *
245
         * @param string $message Message to output.
246
         * @param bool   $is_error If the message is error_level (true) or warning (false).
247
         */
248
        public function notice( $message, $is_error = true ) {
249
                if ( defined( 'WP_CLI' ) && WP_CLI ) {
2✔
250
                        $message = wp_strip_all_tags( $message );
×
251

252
                        if ( $is_error ) {
×
253
                                WP_CLI::warning( $message );
×
254
                        } else {
255
                                WP_CLI::success( $message );
×
256
                        }
257
                } else {
258
                        // Trigger admin notices late, so that any notices which occur during page load are displayed.
259
                        add_action( 'shutdown', array( $this, 'admin_notices' ) );
2✔
260

261
                        $notice = compact( 'message', 'is_error' );
2✔
262

263
                        if ( ! in_array( $notice, $this->notices, true ) ) {
2✔
264
                                $this->notices[] = $notice;
2✔
265
                        }
266
                }
267
        }
2✔
268

269
        /**
270
         * Show an error or other message in the WP Admin
271
         *
272
         * @action shutdown
273
         */
274
        public function admin_notices() {
275
                global $allowedposttags;
3✔
276

277
                $custom = array(
278
                        'progress' => array(
279
                                'class' => true,
3✔
280
                                'id'    => true,
281
                                'max'   => true,
282
                                'style' => true,
283
                                'value' => true,
284
                        ),
285
                );
286

287
                $allowed_html = array_merge( $allowedposttags, $custom );
3✔
288

289
                ksort( $allowed_html );
3✔
290

291
                foreach ( $this->notices as $notice ) {
3✔
292
                        $class_name   = empty( $notice['is_error'] ) ? 'updated' : 'error';
2✔
293
                        $html_message = sprintf( '<div class="%s">%s</div>', esc_attr( $class_name ), wpautop( $notice['message'] ) );
2✔
294

295
                        echo wp_kses( $html_message, $allowed_html );
2✔
296
                }
297
        }
3✔
298

299
        /**
300
         * Register menu page
301
         *
302
         * @action admin_menu
303
         *
304
         * @return void
305
         */
306
        public function register_menu() {
307
                /**
308
                 * Filter the main admin menu title
309
                 *
310
                 * @return string
311
                 */
312
                $main_menu_title = apply_filters( 'wp_stream_admin_menu_title', esc_html__( 'Stream', 'stream' ) );
1✔
313

314
                /**
315
                 * Filter the main admin menu position
316
                 *
317
                 * Note: Using longtail decimal string to reduce the chance of position conflicts, see Codex
318
                 *
319
                 * @return string
320
                 */
321
                $main_menu_position = apply_filters( 'wp_stream_menu_position', '2.999999' );
1✔
322

323
                /**
324
                 * Filter the main admin page title
325
                 *
326
                 * @return string
327
                 */
328
                $main_page_title = apply_filters( 'wp_stream_admin_page_title', esc_html__( 'Stream Records', 'stream' ) );
1✔
329

330
                $this->screen_id['main'] = add_menu_page(
1✔
331
                        $main_page_title,
1✔
332
                        $main_menu_title,
333
                        $this->view_cap,
1✔
334
                        $this->records_page_slug,
1✔
335
                        array( $this, 'render_list_table' ),
1✔
336
                        'div',
1✔
337
                        $main_menu_position
338
                );
339

340
                /**
341
                 * Fires before submenu items are added to the Stream menu
342
                 * allowing plugins to add menu items before Settings
343
                 *
344
                 * @return void
345
                 */
346
                do_action( 'wp_stream_admin_menu' );
1✔
347

348
                /**
349
                 * Filter the Settings admin page title
350
                 *
351
                 * @return string
352
                 */
353
                $settings_page_title = apply_filters( 'wp_stream_settings_form_title', esc_html__( 'Stream Settings', 'stream' ) );
1✔
354

355
                $this->screen_id['settings'] = add_submenu_page(
1✔
356
                        $this->records_page_slug,
1✔
357
                        $settings_page_title,
358
                        esc_html__( 'Settings', 'stream' ),
1✔
359
                        $this->settings_cap,
1✔
360
                        $this->settings_page_slug,
1✔
361
                        array( $this, 'render_settings_page' )
1✔
362
                );
363

364
                if ( isset( $this->screen_id['main'] ) ) {
1✔
365
                        /**
366
                         * Fires just before the Stream list table is registered.
367
                         *
368
                         * @return void
369
                         */
370
                        do_action( 'wp_stream_admin_menu_screens' );
1✔
371

372
                        // Register the list table early, so it associates the column headers with 'Screen settings'.
373
                        add_action(
1✔
374
                                'load-' . $this->screen_id['main'],
1✔
375
                                array(
376
                                        $this,
1✔
377
                                        'register_list_table',
1✔
378
                                )
379
                        );
380
                }
381
        }
1✔
382

383
        /**
384
         * Enqueue scripts/styles for admin screen
385
         *
386
         * @action admin_enqueue_scripts
387
         *
388
         * @param string $hook  Current hook.
389
         *
390
         * @return void
391
         */
392
        public function admin_enqueue_scripts( $hook ) {
393
                wp_register_script( 'wp-stream-select2', $this->plugin->locations['url'] . 'ui/lib/select2/js/select2.full.min.js', array( 'jquery' ), '3.5.2', true );
1✔
394
                wp_register_style( 'wp-stream-select2', $this->plugin->locations['url'] . 'ui/lib/select2/css/select2.min.css', array(), '3.5.2' );
1✔
395
                wp_register_script( 'wp-stream-timeago', $this->plugin->locations['url'] . 'ui/lib/timeago/jquery.timeago.js', array(), '1.4.1', true );
1✔
396

397
                $locale    = strtolower( substr( get_locale(), 0, 2 ) );
1✔
398
                $file_tmpl = 'ui/lib/timeago/locales/jquery.timeago.%s.js';
1✔
399

400
                if ( file_exists( $this->plugin->locations['dir'] . sprintf( $file_tmpl, $locale ) ) ) {
1✔
401
                        wp_register_script(
1✔
402
                                'wp-stream-timeago-locale',
1✔
403
                                $this->plugin->locations['url'] . sprintf( $file_tmpl, $locale ),
1✔
404
                                array( 'wp-stream-timeago' ),
1✔
405
                                '1',
1✔
406
                                false
1✔
407
                        );
408
                } else {
409
                        wp_register_script(
×
410
                                'wp-stream-timeago-locale',
×
411
                                $this->plugin->locations['url'] . sprintf( $file_tmpl, 'en' ),
×
412
                                array( 'wp-stream-timeago' ),
×
413
                                '1',
×
414
                                false
×
415
                        );
416
                }
417

418
                $min = wp_stream_min_suffix();
1✔
419
                wp_enqueue_style( 'wp-stream-admin', $this->plugin->locations['url'] . 'ui/css/admin.' . $min . 'css', array(), $this->plugin->get_version() );
1✔
420

421
                $script_screens = array( 'plugins.php' );
1✔
422

423
                if ( in_array( $hook, $this->screen_id, true ) || in_array( $hook, $script_screens, true ) ) {
1✔
424
                        wp_enqueue_script( 'wp-stream-select2' );
1✔
425
                        wp_enqueue_style( 'wp-stream-select2' );
1✔
426

427
                        wp_enqueue_script( 'wp-stream-timeago' );
1✔
428
                        wp_enqueue_script( 'wp-stream-timeago-locale' );
1✔
429

430
                        wp_enqueue_script(
1✔
431
                                'wp-stream-admin',
1✔
432
                                $this->plugin->locations['url'] . 'ui/js/admin.' . $min . 'js',
1✔
433
                                array(
434
                                        'jquery',
1✔
435
                                        'wp-stream-select2',
436
                                ),
437
                                $this->plugin->get_version(),
1✔
438
                                false
1✔
439
                        );
440
                        wp_enqueue_script(
1✔
441
                                'wp-stream-admin-exclude',
1✔
442
                                $this->plugin->locations['url'] . 'ui/js/exclude.' . $min . 'js',
1✔
443
                                array(
444
                                        'jquery',
1✔
445
                                        'wp-stream-select2',
446
                                ),
447
                                $this->plugin->get_version(),
1✔
448
                                false
1✔
449
                        );
450
                        wp_enqueue_script(
1✔
451
                                'wp-stream-live-updates',
1✔
452
                                $this->plugin->locations['url'] . 'ui/js/live-updates.' . $min . 'js',
1✔
453
                                array(
454
                                        'jquery',
1✔
455
                                        'heartbeat',
456
                                ),
457
                                $this->plugin->get_version(),
1✔
458
                                false
1✔
459
                        );
460

461
                        wp_localize_script(
1✔
462
                                'wp-stream-admin',
1✔
463
                                'wp_stream',
1✔
464
                                array(
465
                                        'i18n'       => array(
466
                                                'confirm_purge'     => esc_html__( 'Are you sure you want to delete all Stream activity records from the database? This cannot be undone.', 'stream' ),
1✔
467
                                                'confirm_defaults'  => esc_html__( 'Are you sure you want to reset all site settings to default? This cannot be undone.', 'stream' ),
1✔
468
                                        ),
469
                                        'locale'     => esc_js( $locale ),
1✔
470
                                        'gmt_offset' => get_option( 'gmt_offset' ),
1✔
471
                                )
472
                        );
473

474
                        $order_types = array( 'asc', 'desc' );
1✔
475

476
                        wp_localize_script(
1✔
477
                                'wp-stream-live-updates',
1✔
478
                                'wp_stream_live_updates',
1✔
479
                                array(
480
                                        'current_screen'      => $hook,
1✔
481
                                        'current_page'        => isset( $_GET['paged'] ) ? absint( wp_unslash( $_GET['paged'] ) ) : '1', // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1✔
482
                                        'current_order'       => isset( $_GET['order'] ) && in_array( strtolower( $_GET['order'] ), $order_types, true ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1✔
483
                                                ? esc_js( $_GET['order'] ) // phpcs:ignore WordPress.Security.NonceVerification.Recommended
×
484
                                                : 'desc',
1✔
485
                                        'current_query'       => wp_stream_json_encode( $_GET ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1✔
486
                                        'current_query_count' => count( $_GET ), // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1✔
487
                                )
488
                        );
489
                }
490

491
                /**
492
                 * The maximum number of items that can be updated in bulk without receiving a warning.
493
                 *
494
                 * Stream watches for bulk actions performed in the WordPress Admin (such as updating
495
                 * many posts at once) and warns the user before proceeding if the number of items they
496
                 * are attempting to update exceeds this threshold value. Since Stream will try to save
497
                 * a log for each item, it will take longer than usual to complete the operation.
498
                 *
499
                 * The default threshold is 100 items.
500
                 *
501
                 * @return int
502
                 */
503
                $bulk_actions_threshold = apply_filters( 'wp_stream_bulk_actions_threshold', 100 );
1✔
504

505
                wp_enqueue_script(
1✔
506
                        'wp-stream-global',
1✔
507
                        $this->plugin->locations['url'] . 'ui/js/global.' . $min . 'js',
1✔
508
                        array( 'jquery' ),
1✔
509
                        $this->plugin->get_version(),
1✔
510
                        false
1✔
511
                );
512

513
                wp_localize_script(
1✔
514
                        'wp-stream-global',
1✔
515
                        'wp_stream_global',
1✔
516
                        array(
517
                                'bulk_actions'       => array(
518
                                        'i18n'      => array(
519
                                                /* translators: %s: a number of items (e.g. "1,742") */
520
                                                '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 ) ) ),
1✔
521
                                        ),
522
                                        'threshold' => absint( $bulk_actions_threshold ),
1✔
523
                                ),
524
                                'plugins_screen_url' => self_admin_url( 'plugins.php#stream' ),
1✔
525
                        )
526
                );
527
        }
1✔
528

529
        /**
530
         * Check whether or not the current admin screen belongs to Stream
531
         *
532
         * @return bool
533
         */
534
        public function is_stream_screen() {
535
                if ( is_admin() && false !== strpos( wp_stream_filter_input( INPUT_GET, 'page' ), $this->records_page_slug ) ) {
2✔
536
                        return true;
2✔
537
                }
538

539
                $screen = get_current_screen();
1✔
540
                if ( is_admin() && Alerts::POST_TYPE === $screen->post_type ) {
1✔
541
                        return true;
×
542
                }
543

544
                return false;
1✔
545
        }
546

547
        /**
548
         * Add a specific body class to all Stream admin screens
549
         *
550
         * @param string $classes  CSS classes to output to body.
551
         *
552
         * @filter admin_body_class
553
         *
554
         * @return string
555
         */
556
        public function admin_body_class( $classes ) {
557
                $stream_classes = array();
1✔
558

559
                if ( $this->is_stream_screen() ) {
1✔
560
                        $stream_classes[] = $this->admin_body_class;
1✔
561

562
                        if ( isset( $_GET['page'] ) ) { // // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1✔
563
                                $stream_classes[] = sanitize_key( $_GET['page'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1✔
564
                        }
565
                }
566

567
                /**
568
                 * Filter the Stream admin body classes
569
                 *
570
                 * @return array
571
                 */
572
                $stream_classes = apply_filters( 'wp_stream_admin_body_classes', $stream_classes );
1✔
573
                $stream_classes = implode( ' ', array_map( 'trim', $stream_classes ) );
1✔
574

575
                return sprintf( '%s %s ', $classes, $stream_classes );
1✔
576
        }
577

578
        /**
579
         * Add menu styles for various WP Admin skins
580
         *
581
         * @uses \wp_add_inline_style()
582
         *
583
         * @action admin_enqueue_scripts
584
         */
585
        public function admin_menu_css() {
586
                $min = wp_stream_min_suffix();
1✔
587
                wp_register_style( 'wp-stream-datepicker', $this->plugin->locations['url'] . 'ui/css/datepicker.' . $min . 'css', array(), $this->plugin->get_version() );
1✔
588
                wp_register_style( 'wp-stream-icons', $this->plugin->locations['url'] . 'ui/stream-icons/style.css', array(), $this->plugin->get_version() );
1✔
589

590
                // Make sure we're working off a clean version.
591
                if ( ! file_exists( ABSPATH . WPINC . '/version.php' ) ) {
1✔
592
                        return;
×
593
                }
594
                include ABSPATH . WPINC . '/version.php';
1✔
595

596
                if ( ! isset( $wp_version ) ) {
1✔
597
                        return;
×
598
                }
599

600
                $body_class   = $this->admin_body_class;
1✔
601
                $records_page = $this->records_page_slug;
1✔
602
                $stream_url   = $this->plugin->locations['url'];
1✔
603

604
                if ( version_compare( $wp_version, '3.8-alpha', '>=' ) ) {
1✔
605
                        wp_enqueue_style( 'wp-stream-icons' );
1✔
606

607
                        $css = "
1✔
608
                                #toplevel_page_{$records_page} .wp-menu-image:before {
1✔
609
                                        font-family: 'WP Stream' !important;
610
                                        content: '\\73' !important;
611
                                }
612
                                #toplevel_page_{$records_page} .wp-menu-image {
1✔
613
                                        background-repeat: no-repeat;
614
                                }
615
                                #menu-posts-feedback .wp-menu-image:before {
616
                                        font-family: dashicons !important;
617
                                        content: '\\f175';
618
                                }
619
                                #adminmenu #menu-posts-feedback div.wp-menu-image {
620
                                        background: none !important;
621
                                        background-repeat: no-repeat;
622
                                }
623
                                body.{$body_class} #wpbody-content .wrap h1:nth-child(1):before {
1✔
624
                                        font-family: 'WP Stream' !important;
625
                                        content: '\\73';
626
                                        padding: 0 8px 0 0;
627
                                }
628
                        ";
629
                } else {
630
                        $css = "
×
631
                                #toplevel_page_{$records_page} .wp-menu-image {
×
632
                                        background: url( {$stream_url}ui/stream-icons/menuicon-sprite.png ) 0 90% no-repeat;
×
633
                                }
634
                                /* Retina Stream Menu Icon */
635
                                @media  only screen and (-moz-min-device-pixel-ratio: 1.5),
636
                                                only screen and (-o-min-device-pixel-ratio: 3/2),
637
                                                only screen and (-webkit-min-device-pixel-ratio: 1.5),
638
                                                only screen and (min-device-pixel-ratio: 1.5) {
639
                                        #toplevel_page_{$records_page} .wp-menu-image {
×
640
                                                background: url( {$stream_url}ui/stream-icons/menuicon-sprite-2x.png ) 0 90% no-repeat;
×
641
                                                background-size:30px 64px;
642
                                        }
643
                                }
644
                                #toplevel_page_{$records_page}.current .wp-menu-image,
×
645
                                #toplevel_page_{$records_page}.wp-has-current-submenu .wp-menu-image,
×
646
                                #toplevel_page_{$records_page}:hover .wp-menu-image {
×
647
                                        background-position: top left;
648
                                }
649
                        ";
650
                }
651

652
                \wp_add_inline_style( 'wp-admin', $css );
1✔
653
        }
1✔
654

655
        /**
656
         * Handle the reset AJAX request to reset logs.
657
         *
658
         * @return bool
659
         */
660
        public function wp_ajax_reset() {
661
                check_ajax_referer( 'stream_nonce_reset', 'wp_stream_nonce_reset' );
1✔
662

663
                if ( ! current_user_can( $this->settings_cap ) ) {
1✔
664
                        wp_die(
×
665
                                esc_html__( "You don't have sufficient privileges to do this action.", 'stream' )
×
666
                        );
667
                }
668

669
                $this->erase_stream_records();
1✔
670

671
                if ( defined( 'WP_STREAM_TESTS' ) && WP_STREAM_TESTS ) {
1✔
672
                        return true;
1✔
673
                }
674

675
                wp_safe_redirect(
×
676
                        add_query_arg(
×
677
                                array(
678
                                        'page'    => is_network_admin() ? $this->network->network_settings_page_slug : $this->settings_page_slug,
×
679
                                        'message' => 'data_erased',
×
680
                                ),
681
                                self_admin_url( $this->admin_parent_page )
×
682
                        )
683
                );
684

685
                exit;
×
686
        }
687

688
        /**
689
         * Clears stream records from the database.
690
         *
691
         * @return void
692
         */
693
        private function erase_stream_records() {
694
                global $wpdb;
1✔
695

696
                $where = '';
1✔
697

698
                if ( is_multisite() && ! $this->plugin->is_network_activated() ) {
1✔
699
                        $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() );
1✔
700
                }
701

702
                $wpdb->query(
1✔
703
                        "DELETE `stream`, `meta`
×
704
                        FROM {$wpdb->stream} AS `stream`
1✔
705
                        LEFT JOIN {$wpdb->streammeta} AS `meta`
1✔
706
                        ON `meta`.`record_id` = `stream`.`ID`
707
                        WHERE 1=1 {$where};" // @codingStandardsIgnoreLine $where already prepared
1✔
708
                );
709
        }
1✔
710

711
        /**
712
         * Schedules a purge of records.
713
         *
714
         * @return void
715
         */
716
        public function purge_schedule_setup() {
717
                if ( ! wp_next_scheduled( 'wp_stream_auto_purge' ) ) {
1✔
718
                        wp_schedule_event( time(), 'twicedaily', 'wp_stream_auto_purge' );
1✔
719
                }
720
        }
1✔
721

722
        /**
723
         * Executes a scheduled purge
724
         *
725
         * @return void
726
         */
727
        public function purge_scheduled_action() {
728
                global $wpdb;
1✔
729

730
                // Don't purge when in Network Admin unless Stream is network activated.
731
                if (
732
                        is_multisite()
1✔
733
                        &&
734
                        is_network_admin()
1✔
735
                        &&
736
                        ! $this->plugin->is_network_activated()
1✔
737
                ) {
738
                        return;
×
739
                }
740

741
                $defaults = $this->plugin->settings->get_defaults();
1✔
742
                if ( is_multisite() && $this->plugin->is_network_activated() ) {
1✔
743
                        $options = (array) get_site_option( 'wp_stream_network', $defaults );
×
744
                } else {
745
                        $options = (array) get_option( 'wp_stream', $defaults );
1✔
746
                }
747

748
                if ( ! empty( $options['general_keep_records_indefinitely'] ) || ! isset( $options['general_records_ttl'] ) ) {
1✔
749
                        return;
×
750
                }
751

752
                $days     = $options['general_records_ttl'];
1✔
753
                $timezone = new DateTimeZone( 'UTC' );
1✔
754
                $date     = new DateTime( 'now', $timezone );
1✔
755

756
                $date->sub( DateInterval::createFromDateString( "$days days" ) );
1✔
757

758
                $where = $wpdb->prepare( ' AND `stream`.`created` < %s', $date->format( 'Y-m-d H:i:s' ) );
1✔
759

760
                // Multisite but NOT network activated, only purge the current blog.
761
                if ( is_multisite() && ! $this->plugin->is_network_activated() ) {
1✔
762
                        $where .= $wpdb->prepare( ' AND `blog_id` = %d', get_current_blog_id() );
1✔
763
                }
764

765
                $wpdb->query(
1✔
766
                        "DELETE `stream`, `meta`
×
767
                        FROM {$wpdb->stream} AS `stream`
1✔
768
                        LEFT JOIN {$wpdb->streammeta} AS `meta`
1✔
769
                        ON `meta`.`record_id` = `stream`.`ID`
770
                        WHERE 1=1 {$where};" // @codingStandardsIgnoreLine $where already prepared
1✔
771
                );
772
        }
1✔
773

774
        /**
775
         * Returns the admin action links.
776
         *
777
         * @filter plugin_action_links
778
         *
779
         * @param array  $links Action links.
780
         * @param string $file  Plugin file.
781
         *
782
         * @return array
783
         */
784
        public function plugin_action_links( $links, $file ) {
785
                if ( plugin_basename( $this->plugin->locations['dir'] . 'stream.php' ) !== $file ) {
1✔
786
                        return $links;
×
787
                }
788

789
                // Also don't show links in Network Admin if Stream isn't network enabled.
790
                if ( is_network_admin() && is_multisite() && ! $this->plugin->is_network_activated() ) {
1✔
791
                        return $links;
×
792
                }
793

794
                if ( is_network_admin() ) {
1✔
795
                        $admin_page_url = add_query_arg(
×
796
                                array(
797
                                        'page' => $this->network->network_settings_page_slug,
×
798
                                ),
799
                                network_admin_url( $this->admin_parent_page )
×
800
                        );
801
                } else {
802
                        $admin_page_url = add_query_arg(
1✔
803
                                array(
804
                                        'page' => $this->settings_page_slug,
1✔
805
                                ),
806
                                admin_url( $this->admin_parent_page )
1✔
807
                        );
808
                }
809

810
                $links[] = sprintf( '<a href="%s">%s</a>', esc_url( $admin_page_url ), esc_html__( 'Settings', 'default' ) );
1✔
811

812
                return $links;
1✔
813
        }
814

815
        /**
816
         * Render main page
817
         */
818
        public function render_list_table() {
819
                $this->list_table->prepare_items();
1✔
820
                ?>
821
                <div class="wrap">
1✔
822
                        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
823
                        <?php $this->list_table->display(); ?>
824
                </div>
1✔
825
                <?php
826
        }
1✔
827

828
        /**
829
         * Render settings page
830
         */
831
        public function render_settings_page() {
832
                $option_key  = $this->plugin->settings->option_key;
1✔
833
                $form_action = apply_filters( 'wp_stream_settings_form_action', admin_url( 'options.php' ) );
1✔
834

835
                $page_description = apply_filters( 'wp_stream_settings_form_description', '' );
1✔
836

837
                $sections   = $this->plugin->settings->get_fields();
1✔
838
                $active_tab = wp_stream_filter_input( INPUT_GET, 'tab' );
1✔
839
                $min        = wp_stream_min_suffix();
1✔
840
                wp_enqueue_script( 'wp-stream-settings', $this->plugin->locations['url'] . 'ui/js/settings.' . $min . 'js', array( 'jquery' ), $this->plugin->get_version(), true );
1✔
841
                ?>
842
                <div class="wrap">
1✔
843
                        <h1><?php echo esc_html( get_admin_page_title() ); ?></h1>
844

845
                        <?php if ( ! empty( $page_description ) ) : ?>
846
                                <p><?php echo esc_html( $page_description ); ?></p>
847
                        <?php endif; ?>
848

849
                        <?php settings_errors(); ?>
850

851
                        <?php if ( count( $sections ) > 1 ) : ?>
852
                                <h2 class="nav-tab-wrapper">
1✔
853
                                        <?php $i = 0; ?>
854
                                        <?php foreach ( $sections as $section => $data ) : ?>
855
                                                <?php $i++; ?>
856
                                                <?php $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section ); ?>
857
                                                <a href="<?php echo esc_url( add_query_arg( 'tab', $section ) ); ?>" class="nav-tab <?php echo $is_active ? esc_attr( ' nav-tab-active' ) : ''; ?>">
858
                                                        <?php echo esc_html( $data['title'] ); ?>
859
                                                </a>
1✔
860
                                        <?php endforeach; ?>
861
                                </h2>
1✔
862
                        <?php endif; ?>
863

864
                        <div class="nav-tab-content" id="tab-content-settings">
865
                                <form method="post" action="<?php echo esc_attr( $form_action ); ?>" enctype="multipart/form-data">
866
                                        <div class="settings-sections">
867
                                                <?php
868
                                                $i = 0;
1✔
869
                                                foreach ( $sections as $section => $data ) {
1✔
870
                                                        $i++;
1✔
871

872
                                                        $is_active = ( ( 1 === $i && ! $active_tab ) || $active_tab === $section );
1✔
873

874
                                                        if ( $is_active ) {
1✔
875
                                                                settings_fields( $option_key );
1✔
876
                                                                do_settings_sections( $option_key );
1✔
877
                                                        }
878
                                                }
879
                                                ?>
880
                                        </div>
1✔
881
                                        <?php submit_button(); ?>
882
                                </form>
1✔
883
                        </div>
884
                </div>
885
                <?php
886
        }
1✔
887

888
        /**
889
         * Instantiate the list table
890
         */
891
        public function register_list_table() {
892
                $this->list_table = new List_Table(
3✔
893
                        $this->plugin,
3✔
894
                        array(
895
                                'screen' => $this->screen_id['main'],
3✔
896
                        )
897
                );
898
        }
3✔
899

900
        /**
901
         * Check if a particular role has access
902
         *
903
         * @param string $role  User role.
904
         *
905
         * @return bool
906
         */
907
        private function role_can_view( $role ) {
908
                if ( in_array( $role, $this->plugin->settings->options['general_role_access'], true ) ) {
3✔
909
                        return true;
3✔
910
                }
911

912
                return false;
2✔
913
        }
914

915
        /**
916
         * Filter user caps to dynamically grant our view cap based on allowed roles
917
         *
918
         * @param array   $allcaps  All capabilities.
919
         * @param array   $caps     Required caps.
920
         * @param array   $args     Unused.
921
         * @param WP_User $user     User.
922
         *
923
         * @filter user_has_cap
924
         *
925
         * @return array
926
         */
927
        public function filter_user_caps( $allcaps, $caps, $args, $user = null ) {
928
                global $wp_roles;
80✔
929

930
                $_wp_roles = isset( $wp_roles ) ? $wp_roles : new WP_Roles();
80✔
931

932
                $user = is_a( $user, 'WP_User' ) ? $user : wp_get_current_user();
80✔
933

934
                // @see
935
                // https://github.com/WordPress/WordPress/blob/c67c9565f1495255807069fdb39dac914046b1a0/wp-includes/capabilities.php#L758
936
                $roles = array_unique(
80✔
937
                        array_merge(
80✔
938
                                $user->roles,
80✔
939
                                array_filter(
80✔
940
                                        array_keys( $user->caps ),
80✔
941
                                        array( $_wp_roles, 'is_role' )
80✔
942
                                )
943
                        )
944
                );
945

946
                $stream_view_caps = array( $this->view_cap );
80✔
947

948
                foreach ( $caps as $cap ) {
80✔
949
                        if ( in_array( $cap, $stream_view_caps, true ) ) {
80✔
950
                                foreach ( $roles as $role ) {
2✔
951
                                        if ( $this->role_can_view( $role ) ) {
2✔
952
                                                $allcaps[ $cap ] = true;
2✔
953

954
                                                break 2;
2✔
955
                                        }
956
                                }
957
                        }
958
                }
959

960
                return $allcaps;
80✔
961
        }
962

963
        /**
964
         * Filter role caps to dynamically grant our view cap based on allowed roles
965
         *
966
         * @filter role_has_cap
967
         *
968
         * @param array  $allcaps  All capabilities.
969
         * @param string $cap      Require cap.
970
         * @param string $role     User role.
971
         *
972
         * @return array
973
         */
974
        public function filter_role_caps( $allcaps, $cap, $role ) {
975
                $stream_view_caps = array( $this->view_cap );
1✔
976

977
                if ( in_array( $cap, $stream_view_caps, true ) && $this->role_can_view( $role ) ) {
1✔
978
                        $allcaps[ $cap ] = true;
1✔
979
                }
980

981
                return $allcaps;
1✔
982
        }
983

984
        /**
985
         * Ajax callback for return a user list.
986
         *
987
         * @action wp_ajax_wp_stream_filters
988
         */
989
        public function ajax_filters() {
990
                if ( ! defined( 'DOING_AJAX' ) || ! current_user_can( $this->plugin->admin->settings_cap ) ) {
1✔
991
                        wp_die( '-1' );
1✔
992
                }
993

994
                check_ajax_referer( 'stream_filters_user_search_nonce', 'nonce' );
×
995

996
                switch ( wp_stream_filter_input( INPUT_GET, 'filter' ) ) {
×
997
                        case 'user_id':
×
998
                                $users = array_merge(
×
999
                                        array(
1000
                                                0 => (object) array(
1001
                                                        'display_name' => 'WP-CLI',
×
1002
                                                ),
1003
                                        ),
1004
                                        get_users()
×
1005
                                );
1006

1007
                                $search = wp_stream_filter_input( INPUT_GET, 'q' );
×
1008
                                if ( $search ) {
×
1009
                                        // `search` arg for get_users() is not enough
1010
                                        $users = array_filter(
×
1011
                                                $users,
×
1012
                                                function ( $user ) use ( $search ) {
1013
                                                        return false !== mb_strpos( mb_strtolower( $user->display_name ), mb_strtolower( $search ) );
×
1014
                                                }
×
1015
                                        );
1016
                                }
1017

1018
                                if ( count( $users ) > $this->preload_users_max ) {
×
1019
                                        $users = array_slice( $users, 0, $this->preload_users_max );
×
1020
                                }
1021

1022
                                // Get gravatar / roles for final result set.
1023
                                $results = $this->get_users_record_meta( $users );
×
1024

1025
                                break;
×
1026
                }
1027

1028
                if ( isset( $results ) ) {
×
1029
                        echo wp_stream_json_encode( $results ); // xss ok.
×
1030
                }
1031

1032
                die();
×
1033
        }
1034

1035
        /**
1036
         * Return relevant user meta data.
1037
         *
1038
         * @param array $authors  Author data.
1039
         * @return array
1040
         */
1041
        public function get_users_record_meta( $authors ) {
1042
                $authors_records = array();
1✔
1043

1044
                foreach ( $authors as $user_id => $args ) {
1✔
1045
                        $author = new Author( $args->ID );
1✔
1046

1047
                        $authors_records[ $user_id ] = array(
1✔
1048
                                'text'  => $author->get_display_name(),
1✔
1049
                                'id'    => $author->id,
1✔
1050
                                'label' => $author->get_display_name(),
1✔
1051
                                'icon'  => $author->get_avatar_src( 32 ),
1✔
1052
                                'title' => '',
1✔
1053
                        );
1054
                }
1055

1056
                return $authors_records;
1✔
1057
        }
1058

1059
        /**
1060
         * Get user meta in a way that is also safe for VIP
1061
         *
1062
         * @param int    $user_id   User ID.
1063
         * @param string $meta_key  Meta key.
1064
         * @param bool   $single    Return first found meta value connected to the meta key (optional).
1065
         *
1066
         * @return mixed
1067
         */
1068
        public function get_user_meta( $user_id, $meta_key, $single = true ) {
1069
                if ( wp_stream_is_vip() && function_exists( 'get_user_attribute' ) ) {
4✔
1070
                        return get_user_attribute( $user_id, $meta_key );
×
1071
                }
1072

1073
                return get_user_meta( $user_id, $meta_key, $single );
4✔
1074
        }
1075

1076
        /**
1077
         * Update user meta in a way that is also safe for VIP
1078
         *
1079
         * @param int    $user_id      User ID.
1080
         * @param string $meta_key     Meta key.
1081
         * @param mixed  $meta_value   Meta value.
1082
         * @param mixed  $prev_value   Previous meta value being overwritten (optional).
1083
         *
1084
         * @return int|bool
1085
         */
1086
        public function update_user_meta( $user_id, $meta_key, $meta_value, $prev_value = '' ) {
1087
                if ( wp_stream_is_vip() && function_exists( 'update_user_attribute' ) ) {
4✔
1088
                        return update_user_attribute( $user_id, $meta_key, $meta_value );
×
1089
                }
1090

1091
                return update_user_meta( $user_id, $meta_key, $meta_value, $prev_value );
4✔
1092
        }
1093

1094
        /**
1095
         * Delete user meta in a way that is also safe for VIP
1096
         *
1097
         * @param int    $user_id     User ID.
1098
         * @param string $meta_key    Meta key.
1099
         * @param mixed  $meta_value  Meta value (optional).
1100
         *
1101
         * @return bool
1102
         */
1103
        public function delete_user_meta( $user_id, $meta_key, $meta_value = '' ) {
1104
                if ( wp_stream_is_vip() && function_exists( 'delete_user_attribute' ) ) {
1✔
1105
                        return delete_user_attribute( $user_id, $meta_key, $meta_value );
×
1106
                }
1107

1108
                return delete_user_meta( $user_id, $meta_key, $meta_value );
1✔
1109
        }
1110
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc