Completed
Branch barista (e00b71)
by
unknown
20:01 queued 09:17
created

EE_Admin_Page::hideStatusChangeNotice()   A

Complexity

Conditions 2
Paths 3

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 3
nop 0
dl 0
loc 13
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
use EventEspresso\core\domain\services\assets\EspressoLegacyAdminAssetManager;
4
use EventEspresso\core\domain\services\assets\LegacyAccountingAssetManager;
5
use EventEspresso\core\exceptions\InvalidDataTypeException;
6
use EventEspresso\core\exceptions\InvalidInterfaceException;
7
use EventEspresso\core\interfaces\InterminableInterface;
8
use EventEspresso\core\services\loaders\LoaderFactory;
9
use EventEspresso\core\services\loaders\LoaderInterface;
10
11
/**
12
 * EE_Admin_Page class
13
 *
14
 * @package           Event Espresso
15
 * @subpackage        includes/core/admin/EE_Admin_Page.core.php
16
 * @abstract
17
 * @author            Brent Christensen, Darren Ethier
18
 */
19
abstract class EE_Admin_Page extends EE_Base implements InterminableInterface
20
{
21
    /**
22
     * @var EE_Admin_Config
23
     */
24
    protected $admin_config;
25
26
    /**
27
     * @var LoaderInterface $loader
28
     */
29
    protected $loader;
30
31
    // set in _init_page_props()
32
    public $page_slug;
33
34
    public $page_label;
35
36
    public $page_folder;
37
38
    // set in define_page_props()
39
    protected $_admin_base_url;
40
41
    protected $_admin_base_path;
42
43
    protected $_admin_page_title;
44
45
    protected $_labels;
46
47
48
    // set early within EE_Admin_Init
49
    protected $_wp_page_slug;
50
51
    // nav tabs
52
    protected $_nav_tabs;
53
54
    protected $_default_nav_tab_name;
55
56
    /**
57
     * @var array $_help_tour
58
     */
59
    protected $_help_tour = array();
60
61
62
    // template variables (used by templates)
63
    protected $_template_path;
64
65
    protected $_column_template_path;
66
67
    /**
68
     * @var array $_template_args
69
     */
70
    protected $_template_args = array();
71
72
    /**
73
     * this will hold the list table object for a given view.
74
     *
75
     * @var EE_Admin_List_Table $_list_table_object
76
     */
77
    protected $_list_table_object;
78
79
    // boolean
80
    protected $_is_UI_request; // this starts at null so we can have no header routes progress through two states.
81
82
    protected $_routing;
83
84
    // list table args
85
    protected $_view;
86
87
    protected $_views;
88
89
90
    // action => method pairs used for routing incoming requests
91
    protected $_page_routes;
92
93
    /**
94
     * @var array $_page_config
95
     */
96
    protected $_page_config;
97
98
    /**
99
     * the current page route and route config
100
     *
101
     * @var string $_route
102
     */
103
    protected $_route;
104
105
    /**
106
     * @var string $_cpt_route
107
     */
108
    protected $_cpt_route;
109
110
    /**
111
     * @var array $_route_config
112
     */
113
    protected $_route_config;
114
115
    /**
116
     * Used to hold default query args for list table routes to help preserve stickiness of filters for carried out
117
     * actions.
118
     *
119
     * @since 4.6.x
120
     * @var array.
121
     */
122
    protected $_default_route_query_args;
123
124
    // set via request page and action args.
125
    protected $_current_page;
126
127
    protected $_current_view;
128
129
    protected $_current_page_view_url;
130
131
    // sanitized request action (and nonce)
132
133
    /**
134
     * @var string $_req_action
135
     */
136
    protected $_req_action;
137
138
    /**
139
     * @var string $_req_nonce
140
     */
141
    protected $_req_nonce;
142
143
    // search related
144
    protected $_search_btn_label;
145
146
    protected $_search_box_callback;
147
148
    /**
149
     * WP Current Screen object
150
     *
151
     * @var WP_Screen
152
     */
153
    protected $_current_screen;
154
155
    // for holding EE_Admin_Hooks object when needed (set via set_hook_object())
156
    protected $_hook_obj;
157
158
    // for holding incoming request data
159
    protected $_req_data;
160
161
    // yes / no array for admin form fields
162
    protected $_yes_no_values = array();
163
164
    // some default things shared by all child classes
165
    protected $_default_espresso_metaboxes;
166
167
    /**
168
     *    EE_Registry Object
169
     *
170
     * @var    EE_Registry
171
     */
172
    protected $EE;
173
174
175
    /**
176
     * This is just a property that flags whether the given route is a caffeinated route or not.
177
     *
178
     * @var boolean
179
     */
180
    protected $_is_caf = false;
181
182
183
    /**
184
     * @Constructor
185
     * @param bool $routing indicate whether we want to just load the object and handle routing or just load the object.
186
     * @throws InvalidArgumentException
187
     * @throws InvalidDataTypeException
188
     * @throws InvalidInterfaceException
189
     * @throws ReflectionException
190
     */
191
    public function __construct($routing = true)
192
    {
193
        $this->loader = LoaderFactory::getLoader();
194
        $this->admin_config = $this->loader->getShared('EE_Admin_Config');
195
        if (strpos($this->_get_dir(), 'caffeinated') !== false) {
196
            $this->_is_caf = true;
197
        }
198
        $this->_yes_no_values = array(
199
            array('id' => true, 'text' => esc_html__('Yes', 'event_espresso')),
200
            array('id' => false, 'text' => esc_html__('No', 'event_espresso')),
201
        );
202
        // set the _req_data property.
203
        $this->_req_data = array_merge($_GET, $_POST);
204
        // routing enabled?
205
        $this->_routing = $routing;
206
    }
207
208
209
    /**
210
     * This logic used to be in the constructor, but that caused a chicken <--> egg scenario
211
     * for child classes that needed to set properties prior to these methods getting called,
212
     * but also needed the parent class to have its construction completed as well.
213
     * Bottom line is that constructors should ONLY be used for setting initial properties
214
     * and any complex initialization logic should only run after instantiation is complete.
215
     *
216
     * This method gets called immediately after construction from within
217
     *      EE_Admin_Page_Init::_initialize_admin_page()
218
     *
219
     * @throws EE_Error
220
     * @throws InvalidArgumentException
221
     * @throws InvalidDataTypeException
222
     * @throws InvalidInterfaceException
223
     * @throws ReflectionException
224
     * @since $VID:$
225
     */
226
    public function initializePage()
227
    {
228
        // set initial page props (child method)
229
        $this->_init_page_props();
230
        // set global defaults
231
        $this->_set_defaults();
232
        // set early because incoming requests could be ajax related and we need to register those hooks.
233
        $this->_global_ajax_hooks();
234
        $this->_ajax_hooks();
235
        // other_page_hooks have to be early too.
236
        $this->_do_other_page_hooks();
237
        // This just allows us to have extending classes do something specific
238
        // before the parent constructor runs _page_setup().
239
        if (method_exists($this, '_before_page_setup')) {
240
            $this->_before_page_setup();
241
        }
242
        // set up page dependencies
243
        $this->_page_setup();
244
    }
245
246
247
    /**
248
     * _init_page_props
249
     * Child classes use to set at least the following properties:
250
     * $page_slug.
251
     * $page_label.
252
     *
253
     * @abstract
254
     * @return void
255
     */
256
    abstract protected function _init_page_props();
257
258
259
    /**
260
     * _ajax_hooks
261
     * child classes put all their add_action('wp_ajax_{name_of_hook}') hooks in here.
262
     * Note: within the ajax callback methods.
263
     *
264
     * @abstract
265
     * @return void
266
     */
267
    abstract protected function _ajax_hooks();
268
269
270
    /**
271
     * _define_page_props
272
     * child classes define page properties in here.  Must include at least:
273
     * $_admin_base_url = base_url for all admin pages
274
     * $_admin_page_title = default admin_page_title for admin pages
275
     * $_labels = array of default labels for various automatically generated elements:
276
     *    array(
277
     *        'buttons' => array(
278
     *            'add' => esc_html__('label for add new button'),
279
     *            'edit' => esc_html__('label for edit button'),
280
     *            'delete' => esc_html__('label for delete button')
281
     *            )
282
     *        )
283
     *
284
     * @abstract
285
     * @return void
286
     */
287
    abstract protected function _define_page_props();
288
289
290
    /**
291
     * _set_page_routes
292
     * child classes use this to define the page routes for all subpages handled by the class.  Page routes are
293
     * assigned to a action => method pairs in an array and to the $_page_routes property.  Each page route must also
294
     * have a 'default' route. Here's the format
295
     * $this->_page_routes = array(
296
     *        'default' => array(
297
     *            'func' => '_default_method_handling_route',
298
     *            'args' => array('array','of','args'),
299
     *            'noheader' => true, //add this in if this page route is processed before any headers are loaded (i.e.
300
     *            ajax request, backend processing)
301
     *            'headers_sent_route'=>'headers_route_reference', //add this if noheader=>true, and you want to load a
302
     *            headers route after.  The string you enter here should match the defined route reference for a
303
     *            headers sent route.
304
     *            'capability' => 'route_capability', //indicate a string for minimum capability required to access
305
     *            this route.
306
     *            'obj_id' => 10 // if this route has an object id, then this can include it (used for capability
307
     *            checks).
308
     *        ),
309
     *        'insert_item' => '_method_for_handling_insert_item' //this can be used if all we need to have is a
310
     *        handling method.
311
     *        )
312
     * )
313
     *
314
     * @abstract
315
     * @return void
316
     */
317
    abstract protected function _set_page_routes();
318
319
320
    /**
321
     * _set_page_config
322
     * child classes use this to define the _page_config array for all subpages handled by the class. Each key in the
323
     * array corresponds to the page_route for the loaded page. Format:
324
     * $this->_page_config = array(
325
     *        'default' => array(
326
     *            'labels' => array(
327
     *                'buttons' => array(
328
     *                    'add' => esc_html__('label for adding item'),
329
     *                    'edit' => esc_html__('label for editing item'),
330
     *                    'delete' => esc_html__('label for deleting item')
331
     *                ),
332
     *                'publishbox' => esc_html__('Localized Title for Publish metabox', 'event_espresso')
333
     *            ), //optional an array of custom labels for various automatically generated elements to use on the
334
     *            page. If this isn't present then the defaults will be used as set for the $this->_labels in
335
     *            _define_page_props() method
336
     *            'nav' => array(
337
     *                'label' => esc_html__('Label for Tab', 'event_espresso').
338
     *                'url' => 'http://someurl', //automatically generated UNLESS you define
339
     *                'css_class' => 'css-class', //automatically generated UNLESS you define
340
     *                'order' => 10, //required to indicate tab position.
341
     *                'persistent' => false //if you want the nav tab to ONLY display when the specific route is
342
     *                displayed then add this parameter.
343
     *            'list_table' => 'name_of_list_table' //string for list table class to be loaded for this admin_page.
344
     *            'metaboxes' => array('metabox1', 'metabox2'), //if present this key indicates we want to load
345
     *            metaboxes set for eventespresso admin pages.
346
     *            'has_metaboxes' => true, //this boolean flag can simply be used to indicate if the route will have
347
     *            metaboxes.  Typically this is used if the 'metaboxes' index is not used because metaboxes are added
348
     *            later.  We just use this flag to make sure the necessary js gets enqueued on page load.
349
     *            'has_help_popups' => false //defaults(true) //this boolean flag can simply be used to indicate if the
350
     *            given route has help popups setup and if it does then we need to make sure thickbox is enqueued.
351
     *            'columns' => array(4, 2), //this key triggers the setup of a page that uses columns (metaboxes).  The
352
     *            array indicates the max number of columns (4) and the default number of columns on page load (2).
353
     *            There is an option in the "screen_options" dropdown that is setup so users can pick what columns they
354
     *            want to display.
355
     *            'help_tabs' => array( //this is used for adding help tabs to a page
356
     *                'tab_id' => array(
357
     *                    'title' => 'tab_title',
358
     *                    'filename' => 'name_of_file_containing_content', //this is the primary method for setting
359
     *                    help tab content.  The fallback if it isn't present is to try a the callback.  Filename
360
     *                    should match a file in the admin folder's "help_tabs" dir (ie..
361
     *                    events/help_tabs/name_of_file_containing_content.help_tab.php)
362
     *                    'callback' => 'callback_method_for_content', //if 'filename' isn't present then system will
363
     *                    attempt to use the callback which should match the name of a method in the class
364
     *                    ),
365
     *                'tab2_id' => array(
366
     *                    'title' => 'tab2 title',
367
     *                    'filename' => 'file_name_2'
368
     *                    'callback' => 'callback_method_for_content',
369
     *                 ),
370
     *            'help_sidebar' => 'callback_for_sidebar_content', //this is used for setting up the sidebar in the
371
     *            help tab area on an admin page. @link
372
     *            http://make.wordpress.org/core/2011/12/06/help-and-screen-api-changes-in-3-3/
373
     *            'help_tour' => array(
374
     *                'name_of_help_tour_class', //all help tours shoudl be a child class of EE_Help_Tour and located
375
     *                in a folder for this admin page named "help_tours", a file name matching the key given here
376
     *                (name_of_help_tour_class.class.php), and class matching key given here (name_of_help_tour_class)
377
     *            ),
378
     *            'require_nonce' => TRUE //this is used if you want to set a route to NOT require a nonce (default is
379
     *            true if it isn't present).  To remove the requirement for a nonce check when this route is visited
380
     *            just set
381
     *            'require_nonce' to FALSE
382
     *            )
383
     * )
384
     *
385
     * @abstract
386
     * @return void
387
     */
388
    abstract protected function _set_page_config();
389
390
391
392
393
394
    /** end sample help_tour methods **/
395
    /**
396
     * _add_screen_options
397
     * Child classes can add any extra wp_screen_options within this method using built-in WP functions/methods for
398
     * doing so. Note child classes can also define _add_screen_options_($this->_current_view) to limit screen options
399
     * to a particular view.
400
     *
401
     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
402
     *         see also WP_Screen object documents...
403
     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
404
     * @abstract
405
     * @return void
406
     */
407
    abstract protected function _add_screen_options();
408
409
410
    /**
411
     * _add_feature_pointers
412
     * Child classes should use this method for implementing any "feature pointers" (using built-in WP styling js).
413
     * Note child classes can also define _add_feature_pointers_($this->_current_view) to limit screen options to a
414
     * particular view. Note: this is just a placeholder for now.  Implementation will come down the road See:
415
     * WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
416
     * extended) also see:
417
     *
418
     * @link   http://eamann.com/tech/wordpress-portland/
419
     * @abstract
420
     * @return void
421
     */
422
    abstract protected function _add_feature_pointers();
423
424
425
    /**
426
     * load_scripts_styles
427
     * child classes put their wp_enqueue_script and wp_enqueue_style hooks in here for anything they need loaded for
428
     * their pages/subpages.  Note this is for all pages/subpages of the system.  You can also load only specific
429
     * scripts/styles per view by putting them in a dynamic function in this format
430
     * (load_scripts_styles_{$this->_current_view}) which matches your page route (action request arg)
431
     *
432
     * @abstract
433
     * @return void
434
     */
435
    abstract public function load_scripts_styles();
436
437
438
    /**
439
     * admin_init
440
     * Anything that should be set/executed at 'admin_init' WP hook runtime should be put in here.  This will apply to
441
     * all pages/views loaded by child class.
442
     *
443
     * @abstract
444
     * @return void
445
     */
446
    abstract public function admin_init();
447
448
449
    /**
450
     * admin_notices
451
     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply to
452
     * all pages/views loaded by child class.
453
     *
454
     * @abstract
455
     * @return void
456
     */
457
    abstract public function admin_notices();
458
459
460
    /**
461
     * admin_footer_scripts
462
     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
463
     * will apply to all pages/views loaded by child class.
464
     *
465
     * @return void
466
     */
467
    abstract public function admin_footer_scripts();
468
469
470
    /**
471
     * admin_footer
472
     * anything triggered by the 'admin_footer' WP action hook should be added to here. This particular method will
473
     * apply to all pages/views loaded by child class.
474
     *
475
     * @return void
476
     */
477
    public function admin_footer()
478
    {
479
    }
480
481
482
    /**
483
     * _global_ajax_hooks
484
     * all global add_action('wp_ajax_{name_of_hook}') hooks in here.
485
     * Note: within the ajax callback methods.
486
     *
487
     * @abstract
488
     * @return void
489
     */
490
    protected function _global_ajax_hooks()
491
    {
492
        // for lazy loading of metabox content
493
        add_action('wp_ajax_espresso-ajax-content', array($this, 'ajax_metabox_content'), 10);
494
495
        add_action(
496
            'wp_ajax_espresso_hide_status_change_notice',
497
            [$this, 'hideStatusChangeNotice']
498
        );
499
        add_action(
500
            'wp_ajax_nopriv_espresso_hide_status_change_notice',
501
            [$this, 'hideStatusChangeNotice']
502
        );
503
    }
504
505
506
    public function ajax_metabox_content()
507
    {
508
        $contentid = isset($this->_req_data['contentid']) ? $this->_req_data['contentid'] : '';
509
        $url = isset($this->_req_data['contenturl']) ? $this->_req_data['contenturl'] : '';
510
        EE_Admin_Page::cached_rss_display($contentid, $url);
511
        wp_die();
512
    }
513
514
515
    public function hideStatusChangeNotice()
516
    {
517
        $response = [];
518
        try {
519
            /** @var EventEspresso\core\admin\StatusChangeNotice $status_change_notice */
520
            $status_change_notice = $this->loader->getShared('EventEspresso\core\admin\StatusChangeNotice');
521
            $response['success'] = $status_change_notice->dismiss() > -1;
522
        } catch (Exception $exception) {
523
            $response['errors'] = $exception->getMessage();
524
        }
525
        echo wp_json_encode($response);
526
        exit();
527
    }
528
529
530
    /**
531
     * _page_setup
532
     * Makes sure any things that need to be loaded early get handled.  We also escape early here if the page requested
533
     * doesn't match the object.
534
     *
535
     * @final
536
     * @return void
537
     * @throws EE_Error
538
     * @throws InvalidArgumentException
539
     * @throws ReflectionException
540
     * @throws InvalidDataTypeException
541
     * @throws InvalidInterfaceException
542
     */
543
    final protected function _page_setup()
544
    {
545
        // requires?
546
        // admin_init stuff - global - we're setting this REALLY early
547
        // so if EE_Admin pages have to hook into other WP pages they can.
548
        // But keep in mind, not everything is available from the EE_Admin Page object at this point.
549
        add_action('admin_init', array($this, 'admin_init_global'), 5);
550
        // next verify if we need to load anything...
551
        $this->_current_page = ! empty($_GET['page']) ? sanitize_key($_GET['page']) : '';
552
        $this->page_folder = strtolower(
553
            str_replace(array('_Admin_Page', 'Extend_'), '', get_class($this))
554
        );
555
        global $ee_menu_slugs;
556
        $ee_menu_slugs = (array) $ee_menu_slugs;
557
        if (! defined('DOING_AJAX') && (! $this->_current_page || ! isset($ee_menu_slugs[ $this->_current_page ]))) {
558
            return;
559
        }
560
        // becuz WP List tables have two duplicate select inputs for choosing bulk actions, we need to copy the action from the second to the first
561
        if (isset($this->_req_data['action2']) && $this->_req_data['action'] === '-1') {
562
            $this->_req_data['action'] = ! empty($this->_req_data['action2']) && $this->_req_data['action2'] !== '-1'
563
                ? $this->_req_data['action2']
564
                : $this->_req_data['action'];
565
        }
566
        // then set blank or -1 action values to 'default'
567
        $this->_req_action = isset($this->_req_data['action'])
568
                             && ! empty($this->_req_data['action'])
569
                             && $this->_req_data['action'] !== '-1'
570
            ? sanitize_key($this->_req_data['action'])
571
            : 'default';
572
        // if action is 'default' after the above BUT we have  'route' var set, then let's use the route as the action.
573
        //  This covers cases where we're coming in from a list table that isn't on the default route.
574
        $this->_req_action = $this->_req_action === 'default' && isset($this->_req_data['route'])
575
            ? $this->_req_data['route'] : $this->_req_action;
576
        // however if we are doing_ajax and we've got a 'route' set then that's what the req_action will be
577
        $this->_req_action = defined('DOING_AJAX') && isset($this->_req_data['route'])
578
            ? $this->_req_data['route']
579
            : $this->_req_action;
580
        $this->_current_view = $this->_req_action;
581
        $this->_req_nonce = $this->_req_action . '_nonce';
582
        $this->_define_page_props();
583
        $this->_current_page_view_url = add_query_arg(
584
            array('page' => $this->_current_page, 'action' => $this->_current_view),
585
            $this->_admin_base_url
586
        );
587
        // default things
588
        $this->_default_espresso_metaboxes = array(
589
            '_espresso_news_post_box',
590
            '_espresso_links_post_box',
591
            '_espresso_ratings_request',
592
            '_espresso_sponsors_post_box',
593
        );
594
        // set page configs
595
        $this->_set_page_routes();
596
        $this->_set_page_config();
597
        // let's include any referrer data in our default_query_args for this route for "stickiness".
598
        if (isset($this->_req_data['wp_referer'])) {
599
            $this->_default_route_query_args['wp_referer'] = $this->_req_data['wp_referer'];
600
        }
601
        // for caffeinated and other extended functionality.
602
        //  If there is a _extend_page_config method
603
        // then let's run that to modify the all the various page configuration arrays
604
        if (method_exists($this, '_extend_page_config')) {
605
            $this->_extend_page_config();
606
        }
607
        // for CPT and other extended functionality.
608
        // If there is an _extend_page_config_for_cpt
609
        // then let's run that to modify all the various page configuration arrays.
610
        if (method_exists($this, '_extend_page_config_for_cpt')) {
611
            $this->_extend_page_config_for_cpt();
612
        }
613
        // filter routes and page_config so addons can add their stuff. Filtering done per class
614
        $this->_page_routes = apply_filters(
615
            'FHEE__' . get_class($this) . '__page_setup__page_routes',
616
            $this->_page_routes,
617
            $this
618
        );
619
        $this->_page_config = apply_filters(
620
            'FHEE__' . get_class($this) . '__page_setup__page_config',
621
            $this->_page_config,
622
            $this
623
        );
624
        // if AHEE__EE_Admin_Page__route_admin_request_$this->_current_view method is present
625
        // then we call it hooked into the AHEE__EE_Admin_Page__route_admin_request action
626
        if (method_exists($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view)) {
627
            add_action(
628
                'AHEE__EE_Admin_Page__route_admin_request',
629
                array($this, 'AHEE__EE_Admin_Page__route_admin_request_' . $this->_current_view),
630
                10,
631
                2
632
            );
633
        }
634
        // next route only if routing enabled
635
        if ($this->_routing && ! defined('DOING_AJAX')) {
636
            $this->_verify_routes();
637
            // next let's just check user_access and kill if no access
638
            $this->check_user_access();
639
            if ($this->_is_UI_request) {
640
                // admin_init stuff - global, all views for this page class, specific view
641
                add_action('admin_init', array($this, 'admin_init'), 10);
642
                if (method_exists($this, 'admin_init_' . $this->_current_view)) {
643
                    add_action('admin_init', array($this, 'admin_init_' . $this->_current_view), 15);
644
                }
645
            } else {
646
                // hijack regular WP loading and route admin request immediately
647
                @ini_set('memory_limit', apply_filters('admin_memory_limit', WP_MAX_MEMORY_LIMIT));
648
                $this->route_admin_request();
649
            }
650
        }
651
    }
652
653
654
    /**
655
     * Provides a way for related child admin pages to load stuff on the loaded admin page.
656
     *
657
     * @return void
658
     * @throws EE_Error
659
     */
660
    private function _do_other_page_hooks()
661
    {
662
        $registered_pages = apply_filters('FHEE_do_other_page_hooks_' . $this->page_slug, array());
663
        foreach ($registered_pages as $page) {
664
            // now let's setup the file name and class that should be present
665
            $classname = str_replace('.class.php', '', $page);
666
            // autoloaders should take care of loading file
667
            if (! class_exists($classname)) {
668
                $error_msg[] = sprintf(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$error_msg was never initialized. Although not strictly required by PHP, it is generally a good practice to add $error_msg = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
669
                    esc_html__(
670
                        'Something went wrong with loading the %s admin hooks page.',
671
                        'event_espresso'
672
                    ),
673
                    $page
674
                );
675
                $error_msg[] = $error_msg[0]
676
                               . "\r\n"
677
                               . sprintf(
678
                                   esc_html__(
679
                                       'There is no class in place for the %1$s admin hooks page.%2$sMake sure you have %3$s defined. If this is a non-EE-core admin page then you also must have an autoloader in place for your class',
680
                                       'event_espresso'
681
                                   ),
682
                                   $page,
683
                                   '<br />',
684
                                   '<strong>' . $classname . '</strong>'
685
                               );
686
                throw new EE_Error(implode('||', $error_msg));
687
            }
688
            // // notice we are passing the instance of this class to the hook object.
689
            $this->loader->getShared($classname, [$this]);
690
        }
691
    }
692
693
694
    /**
695
     * @throws DomainException
696
     * @throws EE_Error
697
     * @throws InvalidArgumentException
698
     * @throws InvalidDataTypeException
699
     * @throws InvalidInterfaceException
700
     * @throws ReflectionException
701
     * @since $VID:$
702
     */
703
    public function load_page_dependencies()
704
    {
705
        try {
706
            $this->_load_page_dependencies();
707
        } catch (EE_Error $e) {
708
            $e->get_error();
709
        }
710
    }
711
712
713
    /**
714
     * load_page_dependencies
715
     * loads things specific to this page class when its loaded.  Really helps with efficiency.
716
     *
717
     * @return void
718
     * @throws DomainException
719
     * @throws EE_Error
720
     * @throws InvalidArgumentException
721
     * @throws InvalidDataTypeException
722
     * @throws InvalidInterfaceException
723
     * @throws ReflectionException
724
     */
725
    protected function _load_page_dependencies()
726
    {
727
        // let's set the current_screen and screen options to override what WP set
728
        $this->_current_screen = get_current_screen();
729
        // load admin_notices - global, page class, and view specific
730
        add_action('admin_notices', array($this, 'admin_notices_global'), 5);
731
        add_action('admin_notices', array($this, 'admin_notices'), 10);
732
        if (method_exists($this, 'admin_notices_' . $this->_current_view)) {
733
            add_action('admin_notices', array($this, 'admin_notices_' . $this->_current_view), 15);
734
        }
735
        // load network admin_notices - global, page class, and view specific
736
        add_action('network_admin_notices', array($this, 'network_admin_notices_global'), 5);
737
        if (method_exists($this, 'network_admin_notices_' . $this->_current_view)) {
738
            add_action('network_admin_notices', array($this, 'network_admin_notices_' . $this->_current_view));
739
        }
740
        // this will save any per_page screen options if they are present
741
        $this->_set_per_page_screen_options();
742
        // setup list table properties
743
        $this->_set_list_table();
744
        // child classes can "register" a metabox to be automatically handled via the _page_config array property.
745
        // However in some cases the metaboxes will need to be added within a route handling callback.
746
        $this->_add_registered_meta_boxes();
747
        $this->_add_screen_columns();
748
        // add screen options - global, page child class, and view specific
749
        $this->_add_global_screen_options();
750
        $this->_add_screen_options();
751
        $add_screen_options = "_add_screen_options_{$this->_current_view}";
752
        if (method_exists($this, $add_screen_options)) {
753
            $this->{$add_screen_options}();
754
        }
755
        // add help tab(s) and tours- set via page_config and qtips.
756
        $this->_add_help_tour();
757
        $this->_add_help_tabs();
758
        $this->_add_qtips();
759
        // add feature_pointers - global, page child class, and view specific
760
        $this->_add_feature_pointers();
761
        $this->_add_global_feature_pointers();
762
        $add_feature_pointer = "_add_feature_pointer_{$this->_current_view}";
763
        if (method_exists($this, $add_feature_pointer)) {
764
            $this->{$add_feature_pointer}();
765
        }
766
        // enqueue scripts/styles - global, page class, and view specific
767
        add_action('admin_enqueue_scripts', array($this, 'load_global_scripts_styles'), 5);
768
        add_action('admin_enqueue_scripts', array($this, 'load_scripts_styles'), 10);
769
        if (method_exists($this, "load_scripts_styles_{$this->_current_view}")) {
770
            add_action('admin_enqueue_scripts', array($this, "load_scripts_styles_{$this->_current_view}"), 15);
771
        }
772
        add_action('admin_enqueue_scripts', array($this, 'admin_footer_scripts_eei18n_js_strings'), 100);
773
        // admin_print_footer_scripts - global, page child class, and view specific.
774
        // NOTE, despite the name, whenever possible, scripts should NOT be loaded using this.
775
        // In most cases that's doing_it_wrong().  But adding hidden container elements etc.
776
        // is a good use case. Notice the late priority we're giving these
777
        add_action('admin_print_footer_scripts', array($this, 'admin_footer_scripts_global'), 99);
778
        add_action('admin_print_footer_scripts', array($this, 'admin_footer_scripts'), 100);
779
        if (method_exists($this, "admin_footer_scripts_{$this->_current_view}")) {
780
            add_action('admin_print_footer_scripts', array($this, "admin_footer_scripts_{$this->_current_view}"), 101);
781
        }
782
        // admin footer scripts
783
        add_action('admin_footer', array($this, 'admin_footer_global'), 99);
784
        add_action('admin_footer', array($this, 'admin_footer'), 100);
785
        if (method_exists($this, "admin_footer_{$this->_current_view}")) {
786
            add_action('admin_footer', array($this, "admin_footer_{$this->_current_view}"), 101);
787
        }
788
        do_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', $this->page_slug);
789
        // targeted hook
790
        do_action(
791
            "FHEE__EE_Admin_Page___load_page_dependencies__after_load__{$this->page_slug}__{$this->_req_action}"
792
        );
793
    }
794
795
796
    /**
797
     * _set_defaults
798
     * This sets some global defaults for class properties.
799
     */
800
    private function _set_defaults()
801
    {
802
        $this->_current_screen = $this->_admin_page_title = $this->_req_action = $this->_req_nonce = null;
803
        $this->_event = $this->_template_path = $this->_column_template_path = null;
804
        $this->_nav_tabs = $this->_views = $this->_page_routes = array();
805
        $this->_page_config = $this->_default_route_query_args = array();
806
        $this->_default_nav_tab_name = 'overview';
807
        // init template args
808
        $this->_template_args = array(
809
            'admin_page_header'  => '',
810
            'admin_page_content' => '',
811
            'post_body_content'  => '',
812
            'before_list_table'  => '',
813
            'after_list_table'   => '',
814
        );
815
    }
816
817
818
    /**
819
     * route_admin_request
820
     *
821
     * @see    _route_admin_request()
822
     * @return exception|void error
823
     * @throws InvalidArgumentException
824
     * @throws InvalidInterfaceException
825
     * @throws InvalidDataTypeException
826
     * @throws EE_Error
827
     * @throws ReflectionException
828
     */
829
    public function route_admin_request()
830
    {
831
        try {
832
            $this->_route_admin_request();
833
        } catch (EE_Error $e) {
834
            $e->get_error();
835
        }
836
    }
837
838
839
    public function set_wp_page_slug($wp_page_slug)
840
    {
841
        $this->_wp_page_slug = $wp_page_slug;
842
        // if in network admin then we need to append "-network" to the page slug. Why? Because that's how WP rolls...
843
        if (is_network_admin()) {
844
            $this->_wp_page_slug .= '-network';
845
        }
846
    }
847
848
849
    /**
850
     * _verify_routes
851
     * All this method does is verify the incoming request and make sure that routes exist for it.  We do this early so
852
     * we know if we need to drop out.
853
     *
854
     * @return bool
855
     * @throws EE_Error
856
     */
857
    protected function _verify_routes()
858
    {
859
        if (! $this->_current_page && ! defined('DOING_AJAX')) {
860
            return false;
861
        }
862
        $this->_route = false;
863
        // check that the page_routes array is not empty
864 View Code Duplication
        if (empty($this->_page_routes)) {
865
            // user error msg
866
            $error_msg = sprintf(
867
                esc_html__('No page routes have been set for the %s admin page.', 'event_espresso'),
868
                $this->_admin_page_title
869
            );
870
            // developer error msg
871
            $error_msg .= '||' . $error_msg
872
                          . esc_html__(
873
                              ' Make sure the "set_page_routes()" method exists, and is setting the "_page_routes" array properly.',
874
                              'event_espresso'
875
                          );
876
            throw new EE_Error($error_msg);
877
        }
878
        // and that the requested page route exists
879
        if (array_key_exists($this->_req_action, $this->_page_routes)) {
880
            $this->_route = $this->_page_routes[ $this->_req_action ];
881
            $this->_route_config = isset($this->_page_config[ $this->_req_action ])
882
                ? $this->_page_config[ $this->_req_action ] : array();
883 View Code Duplication
        } else {
884
            // user error msg
885
            $error_msg = sprintf(
886
                esc_html__(
887
                    'The requested page route does not exist for the %s admin page.',
888
                    'event_espresso'
889
                ),
890
                $this->_admin_page_title
891
            );
892
            // developer error msg
893
            $error_msg .= '||' . $error_msg
894
                          . sprintf(
895
                              esc_html__(
896
                                  ' Create a key in the "_page_routes" array named "%s" and set its value to the appropriate method.',
897
                                  'event_espresso'
898
                              ),
899
                              $this->_req_action
900
                          );
901
            throw new EE_Error($error_msg);
902
        }
903
        // and that a default route exists
904 View Code Duplication
        if (! array_key_exists('default', $this->_page_routes)) {
905
            // user error msg
906
            $error_msg = sprintf(
907
                esc_html__(
908
                    'A default page route has not been set for the % admin page.',
909
                    'event_espresso'
910
                ),
911
                $this->_admin_page_title
912
            );
913
            // developer error msg
914
            $error_msg .= '||' . $error_msg
915
                          . esc_html__(
916
                              ' Create a key in the "_page_routes" array named "default" and set its value to your default page method.',
917
                              'event_espresso'
918
                          );
919
            throw new EE_Error($error_msg);
920
        }
921
922
        // first lets' catch if the UI request has EVER been set.
923
        if ($this->_is_UI_request === null) {
924
            // lets set if this is a UI request or not.
925
            $this->_is_UI_request = ! isset($this->_req_data['noheader']) || $this->_req_data['noheader'] !== true;
926
            // wait a minute... we might have a noheader in the route array
927
            $this->_is_UI_request = is_array($this->_route)
928
                                    && isset($this->_route['noheader'])
929
                                    && $this->_route['noheader'] ? false : $this->_is_UI_request;
930
        }
931
        $this->_set_current_labels();
932
        return true;
933
    }
934
935
936
    /**
937
     * this method simply verifies a given route and makes sure its an actual route available for the loaded page
938
     *
939
     * @param  string $route the route name we're verifying
940
     * @return mixed (bool|Exception)      we'll throw an exception if this isn't a valid route.
941
     * @throws EE_Error
942
     */
943
    protected function _verify_route($route)
944
    {
945
        if (array_key_exists($this->_req_action, $this->_page_routes)) {
946
            return true;
947
        }
948
        // user error msg
949
        $error_msg = sprintf(
950
            esc_html__('The given page route does not exist for the %s admin page.', 'event_espresso'),
951
            $this->_admin_page_title
952
        );
953
        // developer error msg
954
        $error_msg .= '||' . $error_msg
955
                      . sprintf(
956
                          esc_html__(
957
                              ' Check the route you are using in your method (%s) and make sure it matches a route set in your "_page_routes" array property',
958
                              'event_espresso'
959
                          ),
960
                          $route
961
                      );
962
        throw new EE_Error($error_msg);
963
    }
964
965
966
    /**
967
     * perform nonce verification
968
     * This method has be encapsulated here so that any ajax requests that bypass normal routes can verify their nonces
969
     * using this method (and save retyping!)
970
     *
971
     * @param string $nonce     The nonce sent
972
     * @param string $nonce_ref The nonce reference string (name0)
973
     * @return void
974
     * @throws EE_Error
975
     * @throws InvalidArgumentException
976
     * @throws InvalidDataTypeException
977
     * @throws InvalidInterfaceException
978
     */
979
    protected function _verify_nonce($nonce, $nonce_ref)
980
    {
981
        // verify nonce against expected value
982
        if (! wp_verify_nonce($nonce, $nonce_ref)) {
983
            // these are not the droids you are looking for !!!
984
            $msg = sprintf(
985
                esc_html__('%sNonce Fail.%s', 'event_espresso'),
986
                '<a href="http://www.youtube.com/watch?v=56_S0WeTkzs">',
987
                '</a>'
988
            );
989
            if (WP_DEBUG) {
990
                $msg .= "\n  "
991
                        . sprintf(
992
                            esc_html__(
993
                                'In order to dynamically generate nonces for your actions, use the %s::add_query_args_and_nonce() method. May the Nonce be with you!',
994
                                'event_espresso'
995
                            ),
996
                            EE_Admin_Page::class
997
                        );
998
            }
999
            if (! defined('DOING_AJAX')) {
1000
                wp_die($msg);
1001
            } else {
1002
                EE_Error::add_error($msg, __FILE__, __FUNCTION__, __LINE__);
1003
                $this->_return_json();
1004
            }
1005
        }
1006
    }
1007
1008
1009
    /**
1010
     * _route_admin_request()
1011
     * Meat and potatoes of the class.  Basically, this dude checks out what's being requested and sees if there are
1012
     * some doodads to work the magic and handle the flingjangy. Translation:  Checks if the requested action is listed
1013
     * in the page routes and then will try to load the corresponding method.
1014
     *
1015
     * @return void
1016
     * @throws EE_Error
1017
     * @throws InvalidArgumentException
1018
     * @throws InvalidDataTypeException
1019
     * @throws InvalidInterfaceException
1020
     * @throws ReflectionException
1021
     */
1022
    protected function _route_admin_request()
1023
    {
1024
        if (! $this->_is_UI_request) {
1025
            $this->_verify_routes();
1026
        }
1027
        $nonce_check = isset($this->_route_config['require_nonce'])
1028
            ? $this->_route_config['require_nonce']
1029
            : true;
1030 View Code Duplication
        if ($this->_req_action !== 'default' && $nonce_check) {
1031
            // set nonce from post data
1032
            $nonce = isset($this->_req_data[ $this->_req_nonce ])
1033
                ? sanitize_text_field($this->_req_data[ $this->_req_nonce ])
1034
                : '';
1035
            $this->_verify_nonce($nonce, $this->_req_nonce);
1036
        }
1037
        // set the nav_tabs array but ONLY if this is  UI_request
1038
        if ($this->_is_UI_request) {
1039
            $this->_set_nav_tabs();
1040
        }
1041
        // grab callback function
1042
        $func = is_array($this->_route) && isset($this->_route['func']) ? $this->_route['func'] : $this->_route;
1043
        // check if callback has args
1044
        $args = is_array($this->_route) && isset($this->_route['args']) ? $this->_route['args'] : array();
1045
        $error_msg = '';
1046
        // action right before calling route
1047
        // (hook is something like 'AHEE__Registrations_Admin_Page__route_admin_request')
1048
        if (! did_action('AHEE__EE_Admin_Page__route_admin_request')) {
1049
            do_action('AHEE__EE_Admin_Page__route_admin_request', $this->_current_view, $this);
1050
        }
1051
        // right before calling the route, let's remove _wp_http_referer from the
1052
        // $_SERVER[REQUEST_URI] global (its now in _req_data for route processing).
1053
        $_SERVER['REQUEST_URI'] = remove_query_arg(
1054
            '_wp_http_referer',
1055
            wp_unslash($_SERVER['REQUEST_URI'])
1056
        );
1057
        if (! empty($func)) {
1058
            if (is_array($func)) {
1059
                list($class, $method) = $func;
1060
            } elseif (strpos($func, '::') !== false) {
1061
                list($class, $method) = explode('::', $func);
1062
            } else {
1063
                $class = $this;
1064
                $method = $func;
1065
            }
1066
            if (! (is_object($class) && $class === $this)) {
1067
                // send along this admin page object for access by addons.
1068
                $args['admin_page_object'] = $this;
1069
            }
1070
            // is it a method on a class that doesn't work?
1071
            if (((method_exists($class, $method)
1072
                  && call_user_func_array(array($class, $method), $args) === false)
1073
                 && (// is it a standalone function that doesn't work?
1074
                     function_exists($method)
1075
                     && call_user_func_array(
1076
                         $func,
1077
                         array_merge(array('admin_page_object' => $this), $args)
1078
                     ) === false
1079
                 )) || (// is it neither a class method NOR a standalone function?
1080
                    ! function_exists($method)
1081
                    && ! method_exists($class, $method)
1082
                )
1083
            ) {
1084
                // user error msg
1085
                $error_msg = esc_html__(
1086
                    'An error occurred. The  requested page route could not be found.',
1087
                    'event_espresso'
1088
                );
1089
                // developer error msg
1090
                $error_msg .= '||';
1091
                $error_msg .= sprintf(
1092
                    esc_html__(
1093
                        'Page route "%s" could not be called. Check that the spelling for method names and actions in the "_page_routes" array are all correct.',
1094
                        'event_espresso'
1095
                    ),
1096
                    $method
1097
                );
1098
            }
1099
            if (! empty($error_msg)) {
1100
                throw new EE_Error($error_msg);
1101
            }
1102
        }
1103
        // if we've routed and this route has a no headers route AND a sent_headers_route,
1104
        // then we need to reset the routing properties to the new route.
1105
        // now if UI request is FALSE and noheader is true AND we have a headers_sent_route in the route array then let's set UI_request to true because the no header route has a second func after headers have been sent.
1106
        if ($this->_is_UI_request === false
1107
            && is_array($this->_route)
1108
            && ! empty($this->_route['headers_sent_route'])
1109
        ) {
1110
            $this->_reset_routing_properties($this->_route['headers_sent_route']);
1111
        }
1112
    }
1113
1114
1115
    /**
1116
     * This method just allows the resetting of page properties in the case where a no headers
1117
     * route redirects to a headers route in its route config.
1118
     *
1119
     * @since   4.3.0
1120
     * @param  string $new_route New (non header) route to redirect to.
1121
     * @return   void
1122
     * @throws ReflectionException
1123
     * @throws InvalidArgumentException
1124
     * @throws InvalidInterfaceException
1125
     * @throws InvalidDataTypeException
1126
     * @throws EE_Error
1127
     */
1128
    protected function _reset_routing_properties($new_route)
1129
    {
1130
        $this->_is_UI_request = true;
1131
        // now we set the current route to whatever the headers_sent_route is set at
1132
        $this->_req_data['action'] = $new_route;
1133
        // rerun page setup
1134
        $this->_page_setup();
1135
    }
1136
1137
1138
    /**
1139
     * _add_query_arg
1140
     * adds nonce to array of arguments then calls WP add_query_arg function
1141
     *(internally just uses EEH_URL's function with the same name)
1142
     *
1143
     * @param array  $args
1144
     * @param string $url
1145
     * @param bool   $sticky                  if true, then the existing Request params will be appended to the
1146
     *                                        generated url in an associative array indexed by the key 'wp_referer';
1147
     *                                        Example usage: If the current page is:
1148
     *                                        http://mydomain.com/wp-admin/admin.php?page=espresso_registrations
1149
     *                                        &action=default&event_id=20&month_range=March%202015
1150
     *                                        &_wpnonce=5467821
1151
     *                                        and you call:
1152
     *                                        EE_Admin_Page::add_query_args_and_nonce(
1153
     *                                        array(
1154
     *                                        'action' => 'resend_something',
1155
     *                                        'page=>espresso_registrations'
1156
     *                                        ),
1157
     *                                        $some_url,
1158
     *                                        true
1159
     *                                        );
1160
     *                                        It will produce a url in this structure:
1161
     *                                        http://{$some_url}/?page=espresso_registrations&action=resend_something
1162
     *                                        &wp_referer[action]=default&wp_referer[event_id]=20&wpreferer[
1163
     *                                        month_range]=March%202015
1164
     * @param   bool $exclude_nonce           If true, the the nonce will be excluded from the generated nonce.
1165
     * @return string
1166
     */
1167
    public static function add_query_args_and_nonce(
1168
        $args = array(),
1169
        $url = '',
1170
        $sticky = false,
1171
        $exclude_nonce = false
1172
    ) {
1173
        // if there is a _wp_http_referer include the values from the request but only if sticky = true
1174
        if ($sticky) {
1175
            $request = $_REQUEST;
1176
            unset($request['_wp_http_referer'], $request['wp_referer']);
1177
            foreach ($request as $key => $value) {
1178
                // do not add nonces
1179
                if (strpos($key, 'nonce') !== false) {
1180
                    continue;
1181
                }
1182
                $args[ 'wp_referer[' . $key . ']' ] = $value;
1183
            }
1184
        }
1185
        return EEH_URL::add_query_args_and_nonce($args, $url, $exclude_nonce);
1186
    }
1187
1188
1189
    /**
1190
     * This returns a generated link that will load the related help tab.
1191
     *
1192
     * @param  string $help_tab_id the id for the connected help tab
1193
     * @param  string $icon_style  (optional) include css class for the style you want to use for the help icon.
1194
     * @param  string $help_text   (optional) send help text you want to use for the link if default not to be used
1195
     * @uses EEH_Template::get_help_tab_link()
1196
     * @return string              generated link
1197
     */
1198
    protected function _get_help_tab_link($help_tab_id, $icon_style = '', $help_text = '')
1199
    {
1200
        return EEH_Template::get_help_tab_link(
1201
            $help_tab_id,
1202
            $this->page_slug,
1203
            $this->_req_action,
1204
            $icon_style,
1205
            $help_text
1206
        );
1207
    }
1208
1209
1210
    /**
1211
     * _add_help_tabs
1212
     * Note child classes define their help tabs within the page_config array.
1213
     *
1214
     * @link   http://codex.wordpress.org/Function_Reference/add_help_tab
1215
     * @return void
1216
     * @throws DomainException
1217
     * @throws EE_Error
1218
     * @throws ReflectionException
1219
     */
1220
    protected function _add_help_tabs()
1221
    {
1222
        $tour_buttons = '';
1223
        if (isset($this->_page_config[ $this->_req_action ])) {
1224
            $config = $this->_page_config[ $this->_req_action ];
1225
            // is there a help tour for the current route?  if there is let's setup the tour buttons
1226
            if (isset($this->_help_tour[ $this->_req_action ])) {
1227
                $tb = array();
1228
                $tour_buttons = '<div class="ee-abs-container"><div class="ee-help-tour-restart-buttons">';
1229
                foreach ($this->_help_tour['tours'] as $tour) {
1230
                    // if this is the end tour then we don't need to setup a button
1231
                    if ($tour instanceof EE_Help_Tour_final_stop || ! $tour instanceof EE_Help_Tour) {
1232
                        continue;
1233
                    }
1234
                    $tb[] = '<button id="trigger-tour-'
1235
                            . $tour->get_slug()
1236
                            . '" class="button-primary trigger-ee-help-tour">'
1237
                            . $tour->get_label()
1238
                            . '</button>';
1239
                }
1240
                $tour_buttons .= implode('<br />', $tb);
1241
                $tour_buttons .= '</div></div>';
1242
            }
1243
            // let's see if there is a help_sidebar set for the current route and we'll set that up for usage as well.
1244
            if (is_array($config) && isset($config['help_sidebar'])) {
1245
                // check that the callback given is valid
1246
                if (! method_exists($this, $config['help_sidebar'])) {
1247
                    throw new EE_Error(
1248
                        sprintf(
1249
                            esc_html__(
1250
                                'The _page_config array has a callback set for the "help_sidebar" option.  However the callback given (%s) is not a valid callback.  Doublecheck the spelling and make sure this method exists for the class %s',
1251
                                'event_espresso'
1252
                            ),
1253
                            $config['help_sidebar'],
1254
                            get_class($this)
1255
                        )
1256
                    );
1257
                }
1258
                $content = apply_filters(
1259
                    'FHEE__' . get_class($this) . '__add_help_tabs__help_sidebar',
1260
                    $this->{$config['help_sidebar']}()
1261
                );
1262
                $content .= $tour_buttons; // add help tour buttons.
1263
                // do we have any help tours setup?  Cause if we do we want to add the buttons
1264
                $this->_current_screen->set_help_sidebar($content);
1265
            }
1266
            // if there ARE tour buttons...
1267
            if (! empty($tour_buttons)) {
1268
                // if we DON'T have config help sidebar then we'll just add the tour buttons to the sidebar.
1269
                if (! isset($config['help_sidebar'])) {
1270
                    $this->_current_screen->set_help_sidebar($tour_buttons);
1271
                }
1272
                // handle if no help_tabs are set so the sidebar will still show for the help tour buttons
1273
                if (! isset($config['help_tabs'])) {
1274
                    $_ht['id'] = $this->page_slug;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$_ht was never initialized. Although not strictly required by PHP, it is generally a good practice to add $_ht = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1275
                    $_ht['title'] = esc_html__('Help Tours', 'event_espresso');
1276
                    $_ht['content'] = '<p>'
1277
                                      . esc_html__(
1278
                                          'The buttons to the right allow you to start/restart any help tours available for this page',
1279
                                          'event_espresso'
1280
                                      ) . '</p>';
1281
                    $this->_current_screen->add_help_tab($_ht);
1282
                }
1283
            }
1284
            if (! isset($config['help_tabs'])) {
1285
                return;
1286
            } //no help tabs for this route
1287
            foreach ((array) $config['help_tabs'] as $tab_id => $cfg) {
1288
                // we're here so there ARE help tabs!
1289
                // make sure we've got what we need
1290
                if (! isset($cfg['title'])) {
1291
                    throw new EE_Error(
1292
                        esc_html__(
1293
                            'The _page_config array is not set up properly for help tabs.  It is missing a title',
1294
                            'event_espresso'
1295
                        )
1296
                    );
1297
                }
1298
                if (! isset($cfg['filename']) && ! isset($cfg['callback']) && ! isset($cfg['content'])) {
1299
                    throw new EE_Error(
1300
                        esc_html__(
1301
                            'The _page_config array is not setup properly for help tabs. It is missing a either a filename reference, or a callback reference or a content reference so there is no way to know the content for the help tab',
1302
                            'event_espresso'
1303
                        )
1304
                    );
1305
                }
1306
                // first priority goes to content.
1307
                if (! empty($cfg['content'])) {
1308
                    $content = ! empty($cfg['content']) ? $cfg['content'] : null;
1309
                    // second priority goes to filename
1310
                } elseif (! empty($cfg['filename'])) {
1311
                    $file_path = $this->_get_dir() . '/help_tabs/' . $cfg['filename'] . '.help_tab.php';
1312
                    // it's possible that the file is located on decaf route (and above sets up for caf route, if this is the case then lets check decaf route too)
1313
                    $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1314
                                                             . basename($this->_get_dir())
1315
                                                             . '/help_tabs/'
1316
                                                             . $cfg['filename']
1317
                                                             . '.help_tab.php' : $file_path;
1318
                    // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1319
                    if (! isset($cfg['callback']) && ! is_readable($file_path)) {
1320
                        EE_Error::add_error(
1321
                            sprintf(
1322
                                esc_html__(
1323
                                    'The filename given for the help tab %s is not a valid file and there is no other configuration for the tab content.  Please check that the string you set for the help tab on this route (%s) is the correct spelling.  The file should be in %s',
1324
                                    'event_espresso'
1325
                                ),
1326
                                $tab_id,
1327
                                key($config),
1328
                                $file_path
1329
                            ),
1330
                            __FILE__,
1331
                            __FUNCTION__,
1332
                            __LINE__
1333
                        );
1334
                        return;
1335
                    }
1336
                    $template_args['admin_page_obj'] = $this;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$template_args was never initialized. Although not strictly required by PHP, it is generally a good practice to add $template_args = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1337
                    $content = EEH_Template::display_template(
1338
                        $file_path,
1339
                        $template_args,
1340
                        true
1341
                    );
1342
                } else {
1343
                    $content = '';
1344
                }
1345
                // check if callback is valid
1346
                if (empty($content) && (
1347
                        ! isset($cfg['callback']) || ! method_exists($this, $cfg['callback'])
1348
                    )
1349
                ) {
1350
                    EE_Error::add_error(
1351
                        sprintf(
1352
                            esc_html__(
1353
                                'The callback given for a %s help tab on this page does not content OR a corresponding method for generating the content.  Check the spelling or make sure the method is present.',
1354
                                'event_espresso'
1355
                            ),
1356
                            $cfg['title']
1357
                        ),
1358
                        __FILE__,
1359
                        __FUNCTION__,
1360
                        __LINE__
1361
                    );
1362
                    return;
1363
                }
1364
                // setup config array for help tab method
1365
                $id = $this->page_slug . '-' . $this->_req_action . '-' . $tab_id;
1366
                $_ht = array(
1367
                    'id'       => $id,
1368
                    'title'    => $cfg['title'],
1369
                    'callback' => isset($cfg['callback']) && empty($content) ? array($this, $cfg['callback']) : null,
1370
                    'content'  => $content,
1371
                );
1372
                $this->_current_screen->add_help_tab($_ht);
1373
            }
1374
        }
1375
    }
1376
1377
1378
    /**
1379
     * This basically checks loaded $_page_config property to see if there are any help_tours defined.  "help_tours" is
1380
     * an array with properties for setting up usage of the joyride plugin
1381
     *
1382
     * @link   http://zurb.com/playground/jquery-joyride-feature-tour-plugin
1383
     * @see    instructions regarding the format and construction of the "help_tour" array element is found in the
1384
     *         _set_page_config() comments
1385
     * @return void
1386
     * @throws EE_Error
1387
     * @throws InvalidArgumentException
1388
     * @throws InvalidDataTypeException
1389
     * @throws InvalidInterfaceException
1390
     * @throws ReflectionException
1391
     */
1392
    protected function _add_help_tour()
1393
    {
1394
        $tours = array();
1395
        $this->_help_tour = array();
1396
        // exit early if help tours are turned off globally
1397
        if ((defined('EE_DISABLE_HELP_TOURS') && EE_DISABLE_HELP_TOURS)
1398
            || ! EE_Registry::instance()->CFG->admin->help_tour_activation
1399
        ) {
1400
            return;
1401
        }
1402
        // loop through _page_config to find any help_tour defined
1403
        foreach ($this->_page_config as $route => $config) {
1404
            // we're only going to set things up for this route
1405
            if ($route !== $this->_req_action) {
1406
                continue;
1407
            }
1408
            if (isset($config['help_tour'])) {
1409
                foreach ($config['help_tour'] as $tour) {
1410
                    $file_path = $this->_get_dir() . '/help_tours/' . $tour . '.class.php';
1411
                    // let's see if we can get that file...
1412
                    // if not its possible this is a decaf route not set in caffeinated
1413
                    // so lets try and get the caffeinated equivalent
1414
                    $file_path = ! is_readable($file_path) ? EE_ADMIN_PAGES
1415
                                                             . basename($this->_get_dir())
1416
                                                             . '/help_tours/'
1417
                                                             . $tour
1418
                                                             . '.class.php' : $file_path;
1419
                    // if file is STILL not readable then let's do a EE_Error so its more graceful than a fatal error.
1420 View Code Duplication
                    if (! is_readable($file_path)) {
1421
                        EE_Error::add_error(
1422
                            sprintf(
1423
                                esc_html__(
1424
                                    'The file path given for the help tour (%s) is not a valid path.  Please check that the string you set for the help tour on this route (%s) is the correct spelling',
1425
                                    'event_espresso'
1426
                                ),
1427
                                $file_path,
1428
                                $tour
1429
                            ),
1430
                            __FILE__,
1431
                            __FUNCTION__,
1432
                            __LINE__
1433
                        );
1434
                        return;
1435
                    }
1436
                    require_once $file_path;
1437
                    if (! class_exists($tour)) {
1438
                        $error_msg = [];
1439
                        $error_msg[] = sprintf(
1440
                            esc_html__('Something went wrong with loading the %s Help Tour Class.', 'event_espresso'),
1441
                            $tour
1442
                        );
1443
                        $error_msg[] = $error_msg[0] . "\r\n"
1444
                                       . sprintf(
1445
                                           esc_html__(
1446
                                               'There is no class in place for the %s help tour.%s Make sure you have <strong>%s</strong> defined in the "help_tour" array for the %s route of the % admin page.',
1447
                                               'event_espresso'
1448
                                           ),
1449
                                           $tour,
1450
                                           '<br />',
1451
                                           $tour,
1452
                                           $this->_req_action,
1453
                                           get_class($this)
1454
                                       );
1455
                        throw new EE_Error(implode('||', $error_msg));
1456
                    }
1457
                    $tour_obj = new $tour($this->_is_caf);
1458
                    $tours[] = $tour_obj;
1459
                    $this->_help_tour[ $route ][] = EEH_Template::help_tour_stops_generator($tour_obj);
1460
                }
1461
                // let's inject the end tour stop element common to all pages... this will only get seen once per machine.
1462
                $end_stop_tour = new EE_Help_Tour_final_stop($this->_is_caf);
1463
                $tours[] = $end_stop_tour;
1464
                $this->_help_tour[ $route ][] = EEH_Template::help_tour_stops_generator($end_stop_tour);
1465
            }
1466
        }
1467
1468
        if (! empty($tours)) {
1469
            $this->_help_tour['tours'] = $tours;
1470
        }
1471
        // that's it!  Now that the $_help_tours property is set (or not)
1472
        // the scripts and html should be taken care of automatically.
1473
1474
        /**
1475
         * Allow extending the help tours variable.
1476
         *
1477
         * @param array $_help_tour The array containing all help tour information to be displayed.
1478
         */
1479
        $this->_help_tour = apply_filters('FHEE__EE_Admin_Page___add_help_tour___help_tour', $this->_help_tour);
1480
    }
1481
1482
1483
    /**
1484
     * This simply sets up any qtips that have been defined in the page config
1485
     *
1486
     * @return void
1487
     * @throws ReflectionException
1488
     * @throws EE_Error
1489
     */
1490
    protected function _add_qtips()
1491
    {
1492
        if (isset($this->_route_config['qtips'])) {
1493
            $qtips = (array) $this->_route_config['qtips'];
1494
            // load qtip loader
1495
            $path = array(
1496
                $this->_get_dir() . '/qtips/',
1497
                EE_ADMIN_PAGES . basename($this->_get_dir()) . '/qtips/',
1498
            );
1499
            $qtip_loader = EEH_Qtip_Loader::instance();
1500
            if ($qtip_loader instanceof EEH_Qtip_Loader) {
1501
                $qtip_loader->register($qtips, $path);
1502
            }
1503
        }
1504
    }
1505
1506
1507
    /**
1508
     * _set_nav_tabs
1509
     * This sets up the nav tabs from the page_routes array.  This method can be overwritten by child classes if you
1510
     * wish to add additional tabs or modify accordingly.
1511
     *
1512
     * @return void
1513
     * @throws InvalidArgumentException
1514
     * @throws InvalidInterfaceException
1515
     * @throws InvalidDataTypeException
1516
     */
1517
    protected function _set_nav_tabs()
1518
    {
1519
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1520
        $i = 0;
1521
        foreach ($this->_page_config as $slug => $config) {
1522
            if (! is_array($config)
1523
                || (
1524
                    is_array($config)
1525
                    && (
1526
                        (isset($config['nav']) && ! $config['nav'])
1527
                        || ! isset($config['nav'])
1528
                    )
1529
                )
1530
            ) {
1531
                continue;
1532
            }
1533
            // no nav tab for this config
1534
            // check for persistent flag
1535
            if ($slug !== $this->_req_action && isset($config['nav']['persistent']) && ! $config['nav']['persistent']) {
1536
                // nav tab is only to appear when route requested.
1537
                continue;
1538
            }
1539
            if (! $this->check_user_access($slug, true)) {
1540
                // no nav tab because current user does not have access.
1541
                continue;
1542
            }
1543
            $css_class = isset($config['css_class']) ? $config['css_class'] . ' ' : '';
1544
            $this->_nav_tabs[ $slug ] = array(
1545
                'url'       => isset($config['nav']['url'])
1546
                    ? $config['nav']['url']
1547
                    : EE_Admin_Page::add_query_args_and_nonce(
1548
                        array('action' => $slug),
1549
                        $this->_admin_base_url
1550
                    ),
1551
                'link_text' => isset($config['nav']['label'])
1552
                    ? $config['nav']['label']
1553
                    : ucwords(
1554
                        str_replace('_', ' ', $slug)
1555
                    ),
1556
                'css_class' => $this->_req_action === $slug ? $css_class . 'nav-tab-active' : $css_class,
1557
                'order'     => isset($config['nav']['order']) ? $config['nav']['order'] : $i,
1558
            );
1559
            $i++;
1560
        }
1561
        // if $this->_nav_tabs is empty then lets set the default
1562
        if (empty($this->_nav_tabs)) {
1563
            $this->_nav_tabs[ $this->_default_nav_tab_name ] = array(
1564
                'url'       => $this->_admin_base_url,
1565
                'link_text' => ucwords(str_replace('_', ' ', $this->_default_nav_tab_name)),
1566
                'css_class' => 'nav-tab-active',
1567
                'order'     => 10,
1568
            );
1569
        }
1570
        // now let's sort the tabs according to order
1571
        usort($this->_nav_tabs, array($this, '_sort_nav_tabs'));
1572
    }
1573
1574
1575
    /**
1576
     * _set_current_labels
1577
     * This method modifies the _labels property with any optional specific labels indicated in the _page_routes
1578
     * property array
1579
     *
1580
     * @return void
1581
     */
1582
    private function _set_current_labels()
1583
    {
1584
        if (is_array($this->_route_config) && isset($this->_route_config['labels'])) {
1585
            foreach ($this->_route_config['labels'] as $label => $text) {
1586
                if (is_array($text)) {
1587
                    foreach ($text as $sublabel => $subtext) {
1588
                        $this->_labels[ $label ][ $sublabel ] = $subtext;
1589
                    }
1590
                } else {
1591
                    $this->_labels[ $label ] = $text;
1592
                }
1593
            }
1594
        }
1595
    }
1596
1597
1598
    /**
1599
     *        verifies user access for this admin page
1600
     *
1601
     * @param string $route_to_check if present then the capability for the route matching this string is checked.
1602
     * @param bool   $verify_only    Default is FALSE which means if user check fails then wp_die().  Otherwise just
1603
     *                               return false if verify fail.
1604
     * @return bool
1605
     * @throws InvalidArgumentException
1606
     * @throws InvalidDataTypeException
1607
     * @throws InvalidInterfaceException
1608
     */
1609
    public function check_user_access($route_to_check = '', $verify_only = false)
1610
    {
1611
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
1612
        $route_to_check = empty($route_to_check) ? $this->_req_action : $route_to_check;
1613
        $capability = ! empty($route_to_check) && isset($this->_page_routes[ $route_to_check ])
1614
                      && is_array(
1615
                          $this->_page_routes[ $route_to_check ]
1616
                      )
1617
                      && ! empty($this->_page_routes[ $route_to_check ]['capability'])
1618
            ? $this->_page_routes[ $route_to_check ]['capability'] : null;
1619
        if (empty($capability) && empty($route_to_check)) {
1620
            $capability = is_array($this->_route) && empty($this->_route['capability']) ? 'manage_options'
1621
                : $this->_route['capability'];
1622
        } else {
1623
            $capability = empty($capability) ? 'manage_options' : $capability;
1624
        }
1625
        $id = is_array($this->_route) && ! empty($this->_route['obj_id']) ? $this->_route['obj_id'] : 0;
1626
        if (! defined('DOING_AJAX')
1627
            && (
1628
                ! function_exists('is_admin')
1629
                || ! EE_Registry::instance()->CAP->current_user_can(
1630
                    $capability,
1631
                    $this->page_slug
1632
                    . '_'
1633
                    . $route_to_check,
1634
                    $id
1635
                )
1636
            )
1637
        ) {
1638
            if ($verify_only) {
1639
                return false;
1640
            }
1641
            if (is_user_logged_in()) {
1642
                wp_die(__('You do not have access to this route.', 'event_espresso'));
1643
            } else {
1644
                return false;
1645
            }
1646
        }
1647
        return true;
1648
    }
1649
1650
1651
    /**
1652
     * admin_init_global
1653
     * This runs all the code that we want executed within the WP admin_init hook.
1654
     * This method executes for ALL EE Admin pages.
1655
     *
1656
     * @return void
1657
     */
1658
    public function admin_init_global()
1659
    {
1660
    }
1661
1662
1663
    /**
1664
     * wp_loaded_global
1665
     * This runs all the code that we want executed within the WP wp_loaded hook.  This method is optional for an
1666
     * EE_Admin page and will execute on every EE Admin Page load
1667
     *
1668
     * @return void
1669
     */
1670
    public function wp_loaded()
1671
    {
1672
    }
1673
1674
1675
    /**
1676
     * admin_notices
1677
     * Anything triggered by the 'admin_notices' WP hook should be put in here.  This particular method will apply on
1678
     * ALL EE_Admin pages.
1679
     *
1680
     * @return void
1681
     */
1682
    public function admin_notices_global()
1683
    {
1684
        $this->_display_no_javascript_warning();
1685
        $this->_display_espresso_notices();
1686
    }
1687
1688
1689
    public function network_admin_notices_global()
1690
    {
1691
        $this->_display_no_javascript_warning();
1692
        $this->_display_espresso_notices();
1693
    }
1694
1695
1696
    /**
1697
     * admin_footer_scripts_global
1698
     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
1699
     * will apply on ALL EE_Admin pages.
1700
     *
1701
     * @return void
1702
     */
1703
    public function admin_footer_scripts_global()
1704
    {
1705
        $this->_add_admin_page_ajax_loading_img();
1706
        $this->_add_admin_page_overlay();
1707
        // if metaboxes are present we need to add the nonce field
1708
        if (isset($this->_route_config['metaboxes'])
1709
            || isset($this->_route_config['list_table'])
1710
            || (isset($this->_route_config['has_metaboxes']) && $this->_route_config['has_metaboxes'])
1711
        ) {
1712
            wp_nonce_field('closedpostboxes', 'closedpostboxesnonce', false);
1713
            wp_nonce_field('meta-box-order', 'meta-box-order-nonce', false);
1714
        }
1715
    }
1716
1717
1718
    /**
1719
     * admin_footer_global
1720
     * Anything triggered by the wp 'admin_footer' wp hook should be put in here.
1721
     * This particular method will apply on ALL EE_Admin Pages.
1722
     *
1723
     * @return void
1724
     * @throws InvalidArgumentException
1725
     * @throws InvalidDataTypeException
1726
     * @throws InvalidInterfaceException
1727
     */
1728
    public function admin_footer_global()
1729
    {
1730
        // dialog container for dialog helper
1731
        $d_cont = '<div class="ee-admin-dialog-container auto-hide hidden">' . "\n";
1732
        $d_cont .= '<div class="ee-notices"></div>';
1733
        $d_cont .= '<div class="ee-admin-dialog-container-inner-content"></div>';
1734
        $d_cont .= '</div>';
1735
        echo $d_cont;
1736
        // help tour stuff?
1737
        if (isset($this->_help_tour[ $this->_req_action ])) {
1738
            echo implode('<br />', $this->_help_tour[ $this->_req_action ]);
1739
        }
1740
        // current set timezone for timezone js
1741
        echo '<span id="current_timezone" class="hidden">' . EEH_DTT_Helper::get_timezone() . '</span>';
1742
    }
1743
1744
1745
    /**
1746
     * This function sees if there is a method for help popup content existing for the given route.  If there is then
1747
     * we'll use the retrieved array to output the content using the template. For child classes: If you want to have
1748
     * help popups then in your templates or your content you set "triggers" for the content using the
1749
     * "_set_help_trigger('help_trigger_id')" where "help_trigger_id" is what you will use later in your custom method
1750
     * for the help popup content on that page. Then in your Child_Admin_Page class you need to define a help popup
1751
     * method for the content in the format "_help_popup_content_{route_name}()"  So if you are setting help content
1752
     * for the
1753
     * 'edit_event' route you should have a method named "_help_popup_content_edit_route". In your defined
1754
     * "help_popup_content_..." method.  You must prepare and return an array in the following format array(
1755
     *    'help_trigger_id' => array(
1756
     *        'title' => esc_html__('localized title for popup', 'event_espresso'),
1757
     *        'content' => esc_html__('localized content for popup', 'event_espresso')
1758
     *    )
1759
     * );
1760
     * Then the EE_Admin_Parent will take care of making sure that is setup properly on the correct route.
1761
     *
1762
     * @param array $help_array
1763
     * @param bool  $display
1764
     * @return string content
1765
     * @throws DomainException
1766
     * @throws EE_Error
1767
     */
1768
    protected function _set_help_popup_content($help_array = array(), $display = false)
1769
    {
1770
        $content = '';
1771
        $help_array = empty($help_array) ? $this->_get_help_content() : $help_array;
1772
        // loop through the array and setup content
1773
        foreach ($help_array as $trigger => $help) {
1774
            // make sure the array is setup properly
1775
            if (! isset($help['title'], $help['content'])) {
1776
                throw new EE_Error(
1777
                    esc_html__(
1778
                        'Does not look like the popup content array has been setup correctly.  Might want to double check that.  Read the comments for the _get_help_popup_content method found in "EE_Admin_Page" class',
1779
                        'event_espresso'
1780
                    )
1781
                );
1782
            }
1783
            // we're good so let'd setup the template vars and then assign parsed template content to our content.
1784
            $template_args = array(
1785
                'help_popup_id'      => $trigger,
1786
                'help_popup_title'   => $help['title'],
1787
                'help_popup_content' => $help['content'],
1788
            );
1789
            $content .= EEH_Template::display_template(
1790
                EE_ADMIN_TEMPLATE . 'admin_help_popup.template.php',
1791
                $template_args,
1792
                true
1793
            );
1794
        }
1795
        if ($display) {
1796
            echo $content;
1797
            return '';
1798
        }
1799
        return $content;
1800
    }
1801
1802
1803
    /**
1804
     * All this does is retrieve the help content array if set by the EE_Admin_Page child
1805
     *
1806
     * @return array properly formatted array for help popup content
1807
     * @throws EE_Error
1808
     */
1809
    private function _get_help_content()
1810
    {
1811
        // what is the method we're looking for?
1812
        $method_name = '_help_popup_content_' . $this->_req_action;
1813
        // if method doesn't exist let's get out.
1814
        if (! method_exists($this, $method_name)) {
1815
            return array();
1816
        }
1817
        // k we're good to go let's retrieve the help array
1818
        $help_array = $this->{$method_name}();
1819
        // make sure we've got an array!
1820
        if (! is_array($help_array)) {
1821
            throw new EE_Error(
1822
                esc_html__(
1823
                    'Something went wrong with help popup content generation. Expecting an array and well, this ain\'t no array bub.',
1824
                    'event_espresso'
1825
                )
1826
            );
1827
        }
1828
        return $help_array;
1829
    }
1830
1831
1832
    /**
1833
     * EE Admin Pages can use this to set a properly formatted trigger for a help popup.
1834
     * By default the trigger html is printed.  Otherwise it can be returned if the $display flag is set "false"
1835
     * See comments made on the _set_help_content method for understanding other parts to the help popup tool.
1836
     *
1837
     * @param string  $trigger_id reference for retrieving the trigger content for the popup
1838
     * @param boolean $display    if false then we return the trigger string
1839
     * @param array   $dimensions an array of dimensions for the box (array(h,w))
1840
     * @return string
1841
     * @throws DomainException
1842
     * @throws EE_Error
1843
     */
1844
    protected function _set_help_trigger($trigger_id, $display = true, $dimensions = array('400', '640'))
1845
    {
1846
        if (defined('DOING_AJAX')) {
1847
            return '';
1848
        }
1849
        // let's check and see if there is any content set for this popup.  If there isn't then we'll include a default title and content so that developers know something needs to be corrected
1850
        $help_array = $this->_get_help_content();
1851
        $help_content = '';
1852
        if (empty($help_array) || ! isset($help_array[ $trigger_id ])) {
1853
            $help_array[ $trigger_id ] = array(
1854
                'title'   => esc_html__('Missing Content', 'event_espresso'),
1855
                'content' => esc_html__(
1856
                    'A trigger has been set that doesn\'t have any corresponding content. Make sure you have set the help content. (see the "_set_help_popup_content" method in the EE_Admin_Page for instructions.)',
1857
                    'event_espresso'
1858
                ),
1859
            );
1860
            $help_content = $this->_set_help_popup_content($help_array);
1861
        }
1862
        // let's setup the trigger
1863
        $content = '<a class="ee-dialog" href="?height='
1864
                   . $dimensions[0]
1865
                   . '&width='
1866
                   . $dimensions[1]
1867
                   . '&inlineId='
1868
                   . $trigger_id
1869
                   . '" target="_blank"><span class="question ee-help-popup-question"></span></a>';
1870
        $content .= $help_content;
1871
        if ($display) {
1872
            echo $content;
1873
            return '';
1874
        }
1875
        return $content;
1876
    }
1877
1878
1879
    /**
1880
     * _add_global_screen_options
1881
     * Add any extra wp_screen_options within this method using built-in WP functions/methods for doing so.
1882
     * This particular method will add_screen_options on ALL EE_Admin Pages
1883
     *
1884
     * @link   http://chrismarslender.com/wp-tutorials/wordpress-screen-options-tutorial/
1885
     *         see also WP_Screen object documents...
1886
     * @link   http://codex.wordpress.org/Class_Reference/WP_Screen
1887
     * @abstract
1888
     * @return void
1889
     */
1890
    private function _add_global_screen_options()
1891
    {
1892
    }
1893
1894
1895
    /**
1896
     * _add_global_feature_pointers
1897
     * This method is used for implementing any "feature pointers" (using built-in WP styling js).
1898
     * This particular method will implement feature pointers for ALL EE_Admin pages.
1899
     * Note: this is just a placeholder for now.  Implementation will come down the road
1900
     *
1901
     * @see    WP_Internal_Pointers class in wp-admin/includes/template.php for example (its a final class so can't be
1902
     *         extended) also see:
1903
     * @link   http://eamann.com/tech/wordpress-portland/
1904
     * @abstract
1905
     * @return void
1906
     */
1907
    private function _add_global_feature_pointers()
1908
    {
1909
    }
1910
1911
1912
    /**
1913
     * load_global_scripts_styles
1914
     * The scripts and styles enqueued in here will be loaded on every EE Admin page
1915
     *
1916
     * @return void
1917
     * @throws EE_Error
1918
     */
1919
    public function load_global_scripts_styles()
1920
    {
1921
        // add debugging styles
1922
        if (WP_DEBUG) {
1923
            add_action('admin_head', array($this, 'add_xdebug_style'));
1924
        }
1925
        // taking care of metaboxes
1926
        if (empty($this->_cpt_route)
1927
            && (isset($this->_route_config['metaboxes']) || isset($this->_route_config['has_metaboxes']))
1928
        ) {
1929
            wp_enqueue_script('dashboard');
1930
        }
1931
1932
        // LOCALIZED DATA
1933
        // localize script for ajax lazy loading
1934
        wp_localize_script(
1935
            EspressoLegacyAdminAssetManager::JS_HANDLE_EE_ADMIN,
1936
            'eeLazyLoadingContainers',
1937
            apply_filters(
1938
                'FHEE__EE_Admin_Page_Core__load_global_scripts_styles__loader_containers',
1939
                ['espresso_news_post_box_content']
1940
            )
1941
        );
1942
        /**
1943
         * help tour stuff
1944
         */
1945
        if (EE_Registry::instance()->CFG->admin->help_tour_activation && ! empty($this->_help_tour)) {
1946
            add_filter('FHEE_load_joyride', '__return_true');
1947
            $tours = array();
1948
            // setup tours for the js tour object
1949
            foreach ($this->_help_tour['tours'] as $tour) {
1950
                if ($tour instanceof EE_Help_Tour) {
1951
                    $tours[] = array(
1952
                        'id'      => $tour->get_slug(),
1953
                        'options' => $tour->get_options(),
1954
                    );
1955
                }
1956
            }
1957
            wp_localize_script(
1958
                EspressoLegacyAdminAssetManager::JS_HANDLE_EE_HELP_TOUR,
1959
                'EE_HELP_TOUR',
1960
                ['tours' => $tours]
1961
            );
1962
            // admin_footer_global will take care of making sure our help_tour skeleton gets printed via the info stored in $this->_help_tour
1963
        }
1964
    }
1965
1966
1967
    /**
1968
     *        admin_footer_scripts_eei18n_js_strings
1969
     *
1970
     * @return        void
1971
     */
1972
    public function admin_footer_scripts_eei18n_js_strings()
1973
    {
1974
        EE_Registry::$i18n_js_strings['ajax_url'] = WP_AJAX_URL;
1975
        EE_Registry::$i18n_js_strings['confirm_delete'] = esc_html__(
1976
            'Are you absolutely sure you want to delete this item?\nThis action will delete ALL DATA associated with this item!!!\nThis can NOT be undone!!!',
1977
            'event_espresso'
1978
        );
1979
        EE_Registry::$i18n_js_strings['January'] = esc_html__('January', 'event_espresso');
1980
        EE_Registry::$i18n_js_strings['February'] = esc_html__('February', 'event_espresso');
1981
        EE_Registry::$i18n_js_strings['March'] = esc_html__('March', 'event_espresso');
1982
        EE_Registry::$i18n_js_strings['April'] = esc_html__('April', 'event_espresso');
1983
        EE_Registry::$i18n_js_strings['May'] = esc_html__('May', 'event_espresso');
1984
        EE_Registry::$i18n_js_strings['June'] = esc_html__('June', 'event_espresso');
1985
        EE_Registry::$i18n_js_strings['July'] = esc_html__('July', 'event_espresso');
1986
        EE_Registry::$i18n_js_strings['August'] = esc_html__('August', 'event_espresso');
1987
        EE_Registry::$i18n_js_strings['September'] = esc_html__('September', 'event_espresso');
1988
        EE_Registry::$i18n_js_strings['October'] = esc_html__('October', 'event_espresso');
1989
        EE_Registry::$i18n_js_strings['November'] = esc_html__('November', 'event_espresso');
1990
        EE_Registry::$i18n_js_strings['December'] = esc_html__('December', 'event_espresso');
1991
        EE_Registry::$i18n_js_strings['Jan'] = esc_html__('Jan', 'event_espresso');
1992
        EE_Registry::$i18n_js_strings['Feb'] = esc_html__('Feb', 'event_espresso');
1993
        EE_Registry::$i18n_js_strings['Mar'] = esc_html__('Mar', 'event_espresso');
1994
        EE_Registry::$i18n_js_strings['Apr'] = esc_html__('Apr', 'event_espresso');
1995
        EE_Registry::$i18n_js_strings['May'] = esc_html__('May', 'event_espresso');
1996
        EE_Registry::$i18n_js_strings['Jun'] = esc_html__('Jun', 'event_espresso');
1997
        EE_Registry::$i18n_js_strings['Jul'] = esc_html__('Jul', 'event_espresso');
1998
        EE_Registry::$i18n_js_strings['Aug'] = esc_html__('Aug', 'event_espresso');
1999
        EE_Registry::$i18n_js_strings['Sep'] = esc_html__('Sep', 'event_espresso');
2000
        EE_Registry::$i18n_js_strings['Oct'] = esc_html__('Oct', 'event_espresso');
2001
        EE_Registry::$i18n_js_strings['Nov'] = esc_html__('Nov', 'event_espresso');
2002
        EE_Registry::$i18n_js_strings['Dec'] = esc_html__('Dec', 'event_espresso');
2003
        EE_Registry::$i18n_js_strings['Sunday'] = esc_html__('Sunday', 'event_espresso');
2004
        EE_Registry::$i18n_js_strings['Monday'] = esc_html__('Monday', 'event_espresso');
2005
        EE_Registry::$i18n_js_strings['Tuesday'] = esc_html__('Tuesday', 'event_espresso');
2006
        EE_Registry::$i18n_js_strings['Wednesday'] = esc_html__('Wednesday', 'event_espresso');
2007
        EE_Registry::$i18n_js_strings['Thursday'] = esc_html__('Thursday', 'event_espresso');
2008
        EE_Registry::$i18n_js_strings['Friday'] = esc_html__('Friday', 'event_espresso');
2009
        EE_Registry::$i18n_js_strings['Saturday'] = esc_html__('Saturday', 'event_espresso');
2010
        EE_Registry::$i18n_js_strings['Sun'] = esc_html__('Sun', 'event_espresso');
2011
        EE_Registry::$i18n_js_strings['Mon'] = esc_html__('Mon', 'event_espresso');
2012
        EE_Registry::$i18n_js_strings['Tue'] = esc_html__('Tue', 'event_espresso');
2013
        EE_Registry::$i18n_js_strings['Wed'] = esc_html__('Wed', 'event_espresso');
2014
        EE_Registry::$i18n_js_strings['Thu'] = esc_html__('Thu', 'event_espresso');
2015
        EE_Registry::$i18n_js_strings['Fri'] = esc_html__('Fri', 'event_espresso');
2016
        EE_Registry::$i18n_js_strings['Sat'] = esc_html__('Sat', 'event_espresso');
2017
    }
2018
2019
2020
    /**
2021
     *        load enhanced xdebug styles for ppl with failing eyesight
2022
     *
2023
     * @return        void
2024
     */
2025
    public function add_xdebug_style()
2026
    {
2027
        echo '<style>.xdebug-error { font-size:1.5em; }</style>';
2028
    }
2029
2030
2031
    /************************/
2032
    /** LIST TABLE METHODS **/
2033
    /************************/
2034
    /**
2035
     * this sets up the list table if the current view requires it.
2036
     *
2037
     * @return void
2038
     * @throws EE_Error
2039
     * @throws InvalidArgumentException
2040
     * @throws InvalidDataTypeException
2041
     * @throws InvalidInterfaceException
2042
     */
2043
    protected function _set_list_table()
2044
    {
2045
        // first is this a list_table view?
2046
        if (! isset($this->_route_config['list_table'])) {
2047
            return;
2048
        } //not a list_table view so get out.
2049
        // list table functions are per view specific (because some admin pages might have more than one list table!)
2050
        $list_table_view = '_set_list_table_views_' . $this->_req_action;
2051
        if (! method_exists($this, $list_table_view) || $this->{$list_table_view}() === false) {
2052
            // user error msg
2053
            $error_msg = esc_html__(
2054
                'An error occurred. The requested list table views could not be found.',
2055
                'event_espresso'
2056
            );
2057
            // developer error msg
2058
            $error_msg .= '||'
2059
                          . sprintf(
2060
                              esc_html__(
2061
                                  'List table views for "%s" route could not be setup. Check that you have the corresponding method, "%s" set up for defining list_table_views for this route.',
2062
                                  'event_espresso'
2063
                              ),
2064
                              $this->_req_action,
2065
                              $list_table_view
2066
                          );
2067
            throw new EE_Error($error_msg);
2068
        }
2069
        // let's provide the ability to filter the views per PAGE AND ROUTE, per PAGE, and globally
2070
        $this->_views = apply_filters(
2071
            'FHEE_list_table_views_' . $this->page_slug . '_' . $this->_req_action,
2072
            $this->_views
2073
        );
2074
        $this->_views = apply_filters('FHEE_list_table_views_' . $this->page_slug, $this->_views);
2075
        $this->_views = apply_filters('FHEE_list_table_views', $this->_views);
2076
        $this->_set_list_table_view();
2077
        $this->_set_list_table_object();
2078
    }
2079
2080
2081
    /**
2082
     * set current view for List Table
2083
     *
2084
     * @return void
2085
     */
2086
    protected function _set_list_table_view()
2087
    {
2088
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2089
        // looking at active items or dumpster diving ?
2090
        if (! isset($this->_req_data['status']) || ! array_key_exists($this->_req_data['status'], $this->_views)) {
2091
            $this->_view = isset($this->_views['in_use']) ? 'in_use' : 'all';
2092
        } else {
2093
            $this->_view = sanitize_key($this->_req_data['status']);
2094
        }
2095
    }
2096
2097
2098
    /**
2099
     * _set_list_table_object
2100
     * WP_List_Table objects need to be loaded fairly early so automatic stuff WP does is taken care of.
2101
     *
2102
     * @throws InvalidInterfaceException
2103
     * @throws InvalidArgumentException
2104
     * @throws InvalidDataTypeException
2105
     * @throws EE_Error
2106
     * @throws InvalidInterfaceException
2107
     */
2108
    protected function _set_list_table_object()
2109
    {
2110
        if (isset($this->_route_config['list_table'])) {
2111
            if (! class_exists($this->_route_config['list_table'])) {
2112
                throw new EE_Error(
2113
                    sprintf(
2114
                        esc_html__(
2115
                            'The %s class defined for the list table does not exist.  Please check the spelling of the class ref in the $_page_config property on %s.',
2116
                            'event_espresso'
2117
                        ),
2118
                        $this->_route_config['list_table'],
2119
                        get_class($this)
2120
                    )
2121
                );
2122
            }
2123
            $this->_list_table_object = $this->loader->getShared(
2124
                $this->_route_config['list_table'],
2125
                array($this)
2126
            );
2127
        }
2128
    }
2129
2130
2131
    /**
2132
     * get_list_table_view_RLs - get it? View RL ?? VU-RL???  URL ??
2133
     *
2134
     * @param array $extra_query_args                     Optional. An array of extra query args to add to the generated
2135
     *                                                    urls.  The array should be indexed by the view it is being
2136
     *                                                    added to.
2137
     * @return array
2138
     */
2139
    public function get_list_table_view_RLs($extra_query_args = array())
2140
    {
2141
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2142
        if (empty($this->_views)) {
2143
            $this->_views = array();
2144
        }
2145
        // cycle thru views
2146
        foreach ($this->_views as $key => $view) {
2147
            $query_args = array();
2148
            // check for current view
2149
            $this->_views[ $key ]['class'] = $this->_view === $view['slug'] ? 'current' : '';
2150
            $query_args['action'] = $this->_req_action;
2151
            $query_args[ $this->_req_action . '_nonce' ] = wp_create_nonce($query_args['action'] . '_nonce');
2152
            $query_args['status'] = $view['slug'];
2153
            // merge any other arguments sent in.
2154
            if (isset($extra_query_args[ $view['slug'] ])) {
2155
                foreach ($extra_query_args[ $view['slug'] ] as $extra_query_arg) {
2156
                    $query_args[] = $extra_query_arg;
2157
                }
2158
            }
2159
            $this->_views[ $key ]['url'] = EE_Admin_Page::add_query_args_and_nonce($query_args, $this->_admin_base_url);
2160
        }
2161
        return $this->_views;
2162
    }
2163
2164
2165
    /**
2166
     * _entries_per_page_dropdown
2167
     * generates a drop down box for selecting the number of visible rows in an admin page list table
2168
     *
2169
     * @todo   : Note: ideally this should be added to the screen options dropdown as that would be consistent with how
2170
     *         WP does it.
2171
     * @param int $max_entries total number of rows in the table
2172
     * @return string
2173
     */
2174
    protected function _entries_per_page_dropdown($max_entries = 0)
2175
    {
2176
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2177
        $values = array(10, 25, 50, 100);
2178
        $per_page = (! empty($this->_req_data['per_page'])) ? absint($this->_req_data['per_page']) : 10;
2179
        if ($max_entries) {
2180
            $values[] = $max_entries;
2181
            sort($values);
2182
        }
2183
        $entries_per_page_dropdown = '
2184
			<div id="entries-per-page-dv" class="alignleft actions">
2185
				<label class="hide-if-no-js">
2186
					Show
2187
					<select id="entries-per-page-slct" name="entries-per-page-slct">';
2188
        foreach ($values as $value) {
2189
            if ($value < $max_entries) {
2190
                $selected = $value === $per_page ? ' selected="' . $per_page . '"' : '';
2191
                $entries_per_page_dropdown .= '
2192
						<option value="' . $value . '"' . $selected . '>' . $value . '&nbsp;&nbsp;</option>';
2193
            }
2194
        }
2195
        $selected = $max_entries === $per_page ? ' selected="' . $per_page . '"' : '';
2196
        $entries_per_page_dropdown .= '
2197
						<option value="' . $max_entries . '"' . $selected . '>All&nbsp;&nbsp;</option>';
2198
        $entries_per_page_dropdown .= '
2199
					</select>
2200
					entries
2201
				</label>
2202
				<input id="entries-per-page-btn" class="button-secondary" type="submit" value="Go" >
2203
			</div>
2204
		';
2205
        return $entries_per_page_dropdown;
2206
    }
2207
2208
2209
    /**
2210
     *        _set_search_attributes
2211
     *
2212
     * @return        void
2213
     */
2214
    public function _set_search_attributes()
2215
    {
2216
        $this->_template_args['search']['btn_label'] = sprintf(
2217
            esc_html__('Search %s', 'event_espresso'),
2218
            empty($this->_search_btn_label) ? $this->page_label
2219
                : $this->_search_btn_label
2220
        );
2221
        $this->_template_args['search']['callback'] = 'search_' . $this->page_slug;
2222
    }
2223
2224
2225
2226
    /*** END LIST TABLE METHODS **/
2227
2228
2229
    /**
2230
     * _add_registered_metaboxes
2231
     *  this loads any registered metaboxes via the 'metaboxes' index in the _page_config property array.
2232
     *
2233
     * @link   http://codex.wordpress.org/Function_Reference/add_meta_box
2234
     * @return void
2235
     * @throws EE_Error
2236
     */
2237
    private function _add_registered_meta_boxes()
2238
    {
2239
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2240
        // we only add meta boxes if the page_route calls for it
2241
        if (is_array($this->_route_config) && isset($this->_route_config['metaboxes'])
2242
            && is_array(
2243
                $this->_route_config['metaboxes']
2244
            )
2245
        ) {
2246
            // this simply loops through the callbacks provided
2247
            // and checks if there is a corresponding callback registered by the child
2248
            // if there is then we go ahead and process the metabox loader.
2249
            foreach ($this->_route_config['metaboxes'] as $metabox_callback) {
2250
                // first check for Closures
2251
                if ($metabox_callback instanceof Closure) {
2252
                    $result = $metabox_callback();
2253
                } elseif (is_array($metabox_callback) && isset($metabox_callback[0], $metabox_callback[1])) {
2254
                    $result = call_user_func(array($metabox_callback[0], $metabox_callback[1]));
2255
                } else {
2256
                    $result = $this->{$metabox_callback}();
2257
                }
2258
                if ($result === false) {
2259
                    // user error msg
2260
                    $error_msg = esc_html__(
2261
                        'An error occurred. The  requested metabox could not be found.',
2262
                        'event_espresso'
2263
                    );
2264
                    // developer error msg
2265
                    $error_msg .= '||'
2266
                                  . sprintf(
2267
                                      esc_html__(
2268
                                          'The metabox with the string "%s" could not be called. Check that the spelling for method names and actions in the "_page_config[\'metaboxes\']" array are all correct.',
2269
                                          'event_espresso'
2270
                                      ),
2271
                                      $metabox_callback
2272
                                  );
2273
                    throw new EE_Error($error_msg);
2274
                }
2275
            }
2276
        }
2277
    }
2278
2279
2280
    /**
2281
     * _add_screen_columns
2282
     * This will check the _page_config array and if there is "columns" key index indicated, we'll set the template as
2283
     * the dynamic column template and we'll setup the column options for the page.
2284
     *
2285
     * @return void
2286
     */
2287
    private function _add_screen_columns()
2288
    {
2289
        if (is_array($this->_route_config)
2290
            && isset($this->_route_config['columns'])
2291
            && is_array($this->_route_config['columns'])
2292
            && count($this->_route_config['columns']) === 2
2293
        ) {
2294
            add_screen_option(
2295
                'layout_columns',
2296
                array(
2297
                    'max'     => (int) $this->_route_config['columns'][0],
2298
                    'default' => (int) $this->_route_config['columns'][1],
2299
                )
2300
            );
2301
            $this->_template_args['num_columns'] = $this->_route_config['columns'][0];
2302
            $screen_id = $this->_current_screen->id;
2303
            $screen_columns = (int) get_user_option("screen_layout_{$screen_id}");
2304
            $total_columns = ! empty($screen_columns)
2305
                ? $screen_columns
2306
                : $this->_route_config['columns'][1];
2307
            $this->_template_args['current_screen_widget_class'] = 'columns-' . $total_columns;
2308
            $this->_template_args['current_page'] = $this->_wp_page_slug;
2309
            $this->_template_args['screen'] = $this->_current_screen;
2310
            $this->_column_template_path = EE_ADMIN_TEMPLATE
2311
                                           . 'admin_details_metabox_column_wrapper.template.php';
2312
            // finally if we don't have has_metaboxes set in the route config
2313
            // let's make sure it IS set other wise the necessary hidden fields for this won't be loaded.
2314
            $this->_route_config['has_metaboxes'] = true;
2315
        }
2316
    }
2317
2318
2319
2320
    /** GLOBALLY AVAILABLE METABOXES **/
2321
2322
2323
    /**
2324
     * In this section we put any globally available EE metaboxes for all EE Admin pages.  They are called by simply
2325
     * referencing the callback in the _page_config array property.  This way you can be very specific about what pages
2326
     * these get loaded on.
2327
     */
2328 View Code Duplication
    private function _espresso_news_post_box()
2329
    {
2330
        $news_box_title = apply_filters(
2331
            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2332
            esc_html__('New @ Event Espresso', 'event_espresso')
2333
        );
2334
        add_meta_box(
2335
            'espresso_news_post_box',
2336
            $news_box_title,
2337
            array(
2338
                $this,
2339
                'espresso_news_post_box',
2340
            ),
2341
            $this->_wp_page_slug,
2342
            'side'
2343
        );
2344
    }
2345
2346
2347
    /**
2348
     * Code for setting up espresso ratings request metabox.
2349
     */
2350
    protected function _espresso_ratings_request()
2351
    {
2352
        if (! apply_filters('FHEE_show_ratings_request_meta_box', true)) {
2353
            return;
2354
        }
2355
        $ratings_box_title = apply_filters(
2356
            'FHEE__EE_Admin_Page___espresso_news_post_box__news_box_title',
2357
            esc_html__('Keep Event Espresso Decaf Free', 'event_espresso')
2358
        );
2359
        add_meta_box(
2360
            'espresso_ratings_request',
2361
            $ratings_box_title,
2362
            array(
2363
                $this,
2364
                'espresso_ratings_request',
2365
            ),
2366
            $this->_wp_page_slug,
2367
            'side'
2368
        );
2369
    }
2370
2371
2372
    /**
2373
     * Code for setting up espresso ratings request metabox content.
2374
     *
2375
     * @throws DomainException
2376
     */
2377
    public function espresso_ratings_request()
2378
    {
2379
        EEH_Template::display_template(EE_ADMIN_TEMPLATE . 'espresso_ratings_request_content.template.php');
2380
    }
2381
2382
2383
    public static function cached_rss_display($rss_id, $url)
2384
    {
2385
        $loading = '<p class="widget-loading hide-if-no-js">'
2386
                   . __('Loading&#8230;', 'event_espresso')
2387
                   . '</p><p class="hide-if-js">'
2388
                   . esc_html__('This widget requires JavaScript.', 'event_espresso')
2389
                   . '</p>';
2390
        $pre = '<div class="espresso-rss-display">' . "\n\t";
2391
        $pre .= '<span id="' . $rss_id . '_url" class="hidden">' . $url . '</span>';
2392
        $post = '</div>' . "\n";
2393
        $cache_key = 'ee_rss_' . md5($rss_id);
2394
        $output = get_transient($cache_key);
2395
        if ($output !== false) {
2396
            echo $pre . $output . $post;
2397
            return true;
2398
        }
2399
        if (! (defined('DOING_AJAX') && DOING_AJAX)) {
2400
            echo $pre . $loading . $post;
2401
            return false;
2402
        }
2403
        ob_start();
2404
        wp_widget_rss_output($url, array('show_date' => 0, 'items' => 5));
2405
        set_transient($cache_key, ob_get_flush(), 12 * HOUR_IN_SECONDS);
2406
        return true;
2407
    }
2408
2409
2410
    public function espresso_news_post_box()
2411
    {
2412
        ?>
2413
        <div class="padding">
2414
            <div id="espresso_news_post_box_content" class="infolinks">
2415
                <?php
2416
                // Get RSS Feed(s)
2417
                EE_Admin_Page::cached_rss_display(
2418
                    'espresso_news_post_box_content',
2419
                    urlencode(
2420
                        apply_filters(
2421
                            'FHEE__EE_Admin_Page__espresso_news_post_box__feed_url',
2422
                            'http://eventespresso.com/feed/'
2423
                        )
2424
                    )
2425
                );
2426
                ?>
2427
            </div>
2428
            <?php do_action('AHEE__EE_Admin_Page__espresso_news_post_box__after_content'); ?>
2429
        </div>
2430
        <?php
2431
    }
2432
2433
2434
    private function _espresso_links_post_box()
2435
    {
2436
        // Hiding until we actually have content to put in here...
2437
        // add_meta_box('espresso_links_post_box', esc_html__('Helpful Plugin Links', 'event_espresso'), array( $this, 'espresso_links_post_box'), $this->_wp_page_slug, 'side');
2438
    }
2439
2440
2441
    public function espresso_links_post_box()
2442
    {
2443
        // Hiding until we actually have content to put in here...
2444
        // EEH_Template::display_template(
2445
        //     EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_links.template.php'
2446
        // );
2447
    }
2448
2449
2450 View Code Duplication
    protected function _espresso_sponsors_post_box()
2451
    {
2452
        if (apply_filters('FHEE_show_sponsors_meta_box', true)) {
2453
            add_meta_box(
2454
                'espresso_sponsors_post_box',
2455
                esc_html__('Event Espresso Highlights', 'event_espresso'),
2456
                array($this, 'espresso_sponsors_post_box'),
2457
                $this->_wp_page_slug,
2458
                'side'
2459
            );
2460
        }
2461
    }
2462
2463
2464
    public function espresso_sponsors_post_box()
2465
    {
2466
        EEH_Template::display_template(
2467
            EE_ADMIN_TEMPLATE . 'admin_general_metabox_contents_espresso_sponsors.template.php'
2468
        );
2469
    }
2470
2471
2472
    private function _publish_post_box()
2473
    {
2474
        $meta_box_ref = 'espresso_' . $this->page_slug . '_editor_overview';
2475
        // if there is a array('label' => array('publishbox' => 'some title') ) present in the _page_config array
2476
        // then we'll use that for the metabox label.
2477
        // Otherwise we'll just use publish (publishbox itself could be an array of labels indexed by routes)
2478
        if (! empty($this->_labels['publishbox'])) {
2479
            $box_label = is_array($this->_labels['publishbox']) ? $this->_labels['publishbox'][ $this->_req_action ]
2480
                : $this->_labels['publishbox'];
2481
        } else {
2482
            $box_label = esc_html__('Publish', 'event_espresso');
2483
        }
2484
        $box_label = apply_filters(
2485
            'FHEE__EE_Admin_Page___publish_post_box__box_label',
2486
            $box_label,
2487
            $this->_req_action,
2488
            $this
2489
        );
2490
        add_meta_box(
2491
            $meta_box_ref,
2492
            $box_label,
2493
            array($this, 'editor_overview'),
2494
            $this->_current_screen->id,
2495
            'side',
2496
            'high'
2497
        );
2498
    }
2499
2500
2501
    public function editor_overview()
2502
    {
2503
        // if we have extra content set let's add it in if not make sure its empty
2504
        $this->_template_args['publish_box_extra_content'] = isset($this->_template_args['publish_box_extra_content'])
2505
            ? $this->_template_args['publish_box_extra_content']
2506
            : '';
2507
        echo EEH_Template::display_template(
2508
            EE_ADMIN_TEMPLATE . 'admin_details_publish_metabox.template.php',
2509
            $this->_template_args,
2510
            true
2511
        );
2512
    }
2513
2514
2515
    /** end of globally available metaboxes section **/
2516
2517
2518
    /**
2519
     * Public wrapper for the protected method.  Allows plugins/addons to externally call the
2520
     * protected method.
2521
     *
2522
     * @see   $this->_set_publish_post_box_vars for param details
2523
     * @since 4.6.0
2524
     * @param string $name
2525
     * @param int    $id
2526
     * @param bool   $delete
2527
     * @param string $save_close_redirect_URL
2528
     * @param bool   $both_btns
2529
     * @throws EE_Error
2530
     * @throws InvalidArgumentException
2531
     * @throws InvalidDataTypeException
2532
     * @throws InvalidInterfaceException
2533
     */
2534
    public function set_publish_post_box_vars(
2535
        $name = '',
2536
        $id = 0,
2537
        $delete = false,
2538
        $save_close_redirect_URL = '',
2539
        $both_btns = true
2540
    ) {
2541
        $this->_set_publish_post_box_vars(
2542
            $name,
2543
            $id,
2544
            $delete,
2545
            $save_close_redirect_URL,
2546
            $both_btns
2547
        );
2548
    }
2549
2550
2551
    /**
2552
     * Sets the _template_args arguments used by the _publish_post_box shortcut
2553
     * Note: currently there is no validation for this.  However if you want the delete button, the
2554
     * save, and save and close buttons to work properly, then you will want to include a
2555
     * values for the name and id arguments.
2556
     *
2557
     * @todo  Add in validation for name/id arguments.
2558
     * @param    string  $name                    key used for the action ID (i.e. event_id)
2559
     * @param    int     $id                      id attached to the item published
2560
     * @param    string  $delete                  page route callback for the delete action
2561
     * @param    string  $save_close_redirect_URL custom URL to redirect to after Save & Close has been completed
2562
     * @param    boolean $both_btns               whether to display BOTH the "Save & Close" and "Save" buttons or just
2563
     *                                            the Save button
2564
     * @throws EE_Error
2565
     * @throws InvalidArgumentException
2566
     * @throws InvalidDataTypeException
2567
     * @throws InvalidInterfaceException
2568
     */
2569
    protected function _set_publish_post_box_vars(
2570
        $name = '',
2571
        $id = 0,
2572
        $delete = '',
2573
        $save_close_redirect_URL = '',
2574
        $both_btns = true
2575
    ) {
2576
        // if Save & Close, use a custom redirect URL or default to the main page?
2577
        $save_close_redirect_URL = ! empty($save_close_redirect_URL)
2578
            ? $save_close_redirect_URL
2579
            : $this->_admin_base_url;
2580
        // create the Save & Close and Save buttons
2581
        $this->_set_save_buttons($both_btns, array(), array(), $save_close_redirect_URL);
2582
        // if we have extra content set let's add it in if not make sure its empty
2583
        $this->_template_args['publish_box_extra_content'] = isset($this->_template_args['publish_box_extra_content'])
2584
            ? $this->_template_args['publish_box_extra_content']
2585
            : '';
2586
        if ($delete && ! empty($id)) {
2587
            // make sure we have a default if just true is sent.
2588
            $delete = ! empty($delete) ? $delete : 'delete';
2589
            $delete_link_args = array($name => $id);
2590
            $delete = $this->get_action_link_or_button(
2591
                $delete,
2592
                $delete,
2593
                $delete_link_args,
2594
                'submitdelete deletion'
2595
            );
2596
        }
2597
        $this->_template_args['publish_delete_link'] = ! empty($id) ? $delete : '';
2598
        if (! empty($name) && ! empty($id)) {
2599
            $hidden_field_arr[ $name ] = array(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$hidden_field_arr was never initialized. Although not strictly required by PHP, it is generally a good practice to add $hidden_field_arr = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
2600
                'type'  => 'hidden',
2601
                'value' => $id,
2602
            );
2603
            $hf = $this->_generate_admin_form_fields($hidden_field_arr, 'array');
2604
        } else {
2605
            $hf = '';
2606
        }
2607
        // add hidden field
2608
        $this->_template_args['publish_hidden_fields'] = is_array($hf) && ! empty($name)
2609
            ? $hf[ $name ]['field']
2610
            : $hf;
2611
    }
2612
2613
2614
    /**
2615
     * displays an error message to ppl who have javascript disabled
2616
     *
2617
     * @return void
2618
     */
2619
    private function _display_no_javascript_warning()
2620
    {
2621
        ?>
2622
        <noscript>
2623
            <div id="no-js-message" class="error">
2624
                <p style="font-size:1.3em;">
2625
                    <span style="color:red;"><?php esc_html_e('Warning!', 'event_espresso'); ?></span>
2626
                    <?php esc_html_e(
2627
                        'Javascript is currently turned off for your browser. Javascript must be enabled in order for all of the features on this page to function properly. Please turn your javascript back on.',
2628
                        'event_espresso'
2629
                    ); ?>
2630
                </p>
2631
            </div>
2632
        </noscript>
2633
        <?php
2634
    }
2635
2636
2637
    /**
2638
     * displays espresso success and/or error notices
2639
     *
2640
     * @return void
2641
     */
2642
    private function _display_espresso_notices()
2643
    {
2644
        $notices = $this->_get_transient(true);
2645
        echo stripslashes($notices);
2646
    }
2647
2648
2649
    /**
2650
     * spinny things pacify the masses
2651
     *
2652
     * @return void
2653
     */
2654
    protected function _add_admin_page_ajax_loading_img()
2655
    {
2656
        ?>
2657
        <div id="espresso-ajax-loading" class="ajax-loading-grey">
2658
            <span class="ee-spinner ee-spin"></span><span class="hidden"><?php
2659
                esc_html_e('loading...', 'event_espresso'); ?></span>
2660
        </div>
2661
        <?php
2662
    }
2663
2664
2665
    /**
2666
     * add admin page overlay for modal boxes
2667
     *
2668
     * @return void
2669
     */
2670
    protected function _add_admin_page_overlay()
2671
    {
2672
        ?>
2673
        <div id="espresso-admin-page-overlay-dv" class=""></div>
2674
        <?php
2675
    }
2676
2677
2678
    /**
2679
     * facade for add_meta_box
2680
     *
2681
     * @param string  $action        where the metabox get's displayed
2682
     * @param string  $title         Title of Metabox (output in metabox header)
2683
     * @param string  $callback      If not empty and $create_fun is set to false then we'll use a custom callback
2684
     *                               instead of the one created in here.
2685
     * @param array   $callback_args an array of args supplied for the metabox
2686
     * @param string  $column        what metabox column
2687
     * @param string  $priority      give this metabox a priority (using accepted priorities for wp meta boxes)
2688
     * @param boolean $create_func   default is true.  Basically we can say we don't WANT to have the runtime function
2689
     *                               created but just set our own callback for wp's add_meta_box.
2690
     * @throws DomainException
2691
     */
2692
    public function _add_admin_page_meta_box(
2693
        $action,
2694
        $title,
2695
        $callback,
2696
        $callback_args,
2697
        $column = 'normal',
2698
        $priority = 'high',
2699
        $create_func = true
2700
    ) {
2701
        do_action('AHEE_log', __FILE__, __FUNCTION__, $callback);
2702
        // if we have empty callback args and we want to automatically create the metabox callback then we need to make sure the callback args are generated.
2703
        if (empty($callback_args) && $create_func) {
2704
            $callback_args = array(
2705
                'template_path' => $this->_template_path,
2706
                'template_args' => $this->_template_args,
2707
            );
2708
        }
2709
        // if $create_func is true (default) then we automatically create the function for displaying the actual meta box.  If false then we take the $callback reference passed through and use it instead (so callers can define their own callback function/method if they wish)
2710
        $call_back_func = $create_func
2711
            ? static function ($post, $metabox) {
2712
                do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2713
                echo EEH_Template::display_template(
2714
                    $metabox['args']['template_path'],
2715
                    $metabox['args']['template_args'],
2716
                    true
2717
                );
2718
            }
2719
            : $callback;
2720
        add_meta_box(
2721
            str_replace('_', '-', $action) . '-mbox',
2722
            $title,
2723
            $call_back_func,
2724
            $this->_wp_page_slug,
2725
            $column,
2726
            $priority,
2727
            $callback_args
2728
        );
2729
    }
2730
2731
2732
    /**
2733
     * generates HTML wrapper for and admin details page that contains metaboxes in columns
2734
     *
2735
     * @throws DomainException
2736
     * @throws EE_Error
2737
     * @throws InvalidArgumentException
2738
     * @throws InvalidDataTypeException
2739
     * @throws InvalidInterfaceException
2740
     */
2741
    public function display_admin_page_with_metabox_columns()
2742
    {
2743
        $this->_template_args['post_body_content'] = $this->_template_args['admin_page_content'];
2744
        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2745
            $this->_column_template_path,
2746
            $this->_template_args,
2747
            true
2748
        );
2749
        // the final wrapper
2750
        $this->admin_page_wrapper();
2751
    }
2752
2753
2754
    /**
2755
     * generates  HTML wrapper for an admin details page
2756
     *
2757
     * @return void
2758
     * @throws DomainException
2759
     * @throws EE_Error
2760
     * @throws InvalidArgumentException
2761
     * @throws InvalidDataTypeException
2762
     * @throws InvalidInterfaceException
2763
     */
2764
    public function display_admin_page_with_sidebar()
2765
    {
2766
        $this->_display_admin_page(true);
2767
    }
2768
2769
2770
    /**
2771
     * generates  HTML wrapper for an admin details page (except no sidebar)
2772
     *
2773
     * @return void
2774
     * @throws DomainException
2775
     * @throws EE_Error
2776
     * @throws InvalidArgumentException
2777
     * @throws InvalidDataTypeException
2778
     * @throws InvalidInterfaceException
2779
     */
2780
    public function display_admin_page_with_no_sidebar()
2781
    {
2782
        $this->_display_admin_page();
2783
    }
2784
2785
2786
    /**
2787
     * generates HTML wrapper for an EE about admin page (no sidebar)
2788
     *
2789
     * @return void
2790
     * @throws DomainException
2791
     * @throws EE_Error
2792
     * @throws InvalidArgumentException
2793
     * @throws InvalidDataTypeException
2794
     * @throws InvalidInterfaceException
2795
     */
2796
    public function display_about_admin_page()
2797
    {
2798
        $this->_display_admin_page(false, true);
2799
    }
2800
2801
2802
    /**
2803
     * display_admin_page
2804
     * contains the code for actually displaying an admin page
2805
     *
2806
     * @param boolean $sidebar true with sidebar, false without
2807
     * @param boolean $about   use the about admin wrapper instead of the default.
2808
     * @return void
2809
     * @throws DomainException
2810
     * @throws EE_Error
2811
     * @throws InvalidArgumentException
2812
     * @throws InvalidDataTypeException
2813
     * @throws InvalidInterfaceException
2814
     */
2815
    private function _display_admin_page($sidebar = false, $about = false)
2816
    {
2817
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
2818
        // custom remove metaboxes hook to add or remove any metaboxes to/from Admin pages.
2819
        do_action('AHEE__EE_Admin_Page___display_admin_page__modify_metaboxes');
2820
        // set current wp page slug - looks like: event-espresso_page_event_categories
2821
        // keep in mind "event-espresso" COULD be something else if the top level menu label has been translated.
2822
        $this->_template_args['current_page'] = $this->_wp_page_slug;
2823
        $this->_template_args['admin_page_wrapper_div_id'] = $this->_cpt_route
2824
            ? 'poststuff'
2825
            : 'espresso-default-admin';
2826
        $template_path = $sidebar
2827
            ? EE_ADMIN_TEMPLATE . 'admin_details_wrapper.template.php'
2828
            : EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar.template.php';
2829
        if (defined('DOING_AJAX') && DOING_AJAX) {
2830
            $template_path = EE_ADMIN_TEMPLATE . 'admin_details_wrapper_no_sidebar_ajax.template.php';
2831
        }
2832
        $template_path = ! empty($this->_column_template_path)
2833
            ? $this->_column_template_path : $template_path;
2834
        $this->_template_args['post_body_content'] = isset($this->_template_args['admin_page_content'])
2835
            ? $this->_template_args['admin_page_content']
2836
            : '';
2837
        $this->_template_args['before_admin_page_content'] = isset($this->_template_args['before_admin_page_content'])
2838
            ? $this->_template_args['before_admin_page_content']
2839
            : '';
2840
        $this->_template_args['after_admin_page_content'] = isset($this->_template_args['after_admin_page_content'])
2841
            ? $this->_template_args['after_admin_page_content']
2842
            : '';
2843
        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2844
            $template_path,
2845
            $this->_template_args,
2846
            true
2847
        );
2848
        // the final template wrapper
2849
        $this->admin_page_wrapper($about);
2850
    }
2851
2852
2853
    /**
2854
     * This is used to display caf preview pages.
2855
     *
2856
     * @since 4.3.2
2857
     * @param string $utm_campaign_source what is the key used for google analytics link
2858
     * @param bool   $display_sidebar     whether to use the sidebar template or the full template for the page.  TRUE
2859
     *                                    = SHOW sidebar, FALSE = no sidebar. Default no sidebar.
2860
     * @return void
2861
     * @throws DomainException
2862
     * @throws EE_Error
2863
     * @throws InvalidArgumentException
2864
     * @throws InvalidDataTypeException
2865
     * @throws InvalidInterfaceException
2866
     */
2867
    public function display_admin_caf_preview_page($utm_campaign_source = '', $display_sidebar = true)
2868
    {
2869
        // let's generate a default preview action button if there isn't one already present.
2870
        $this->_labels['buttons']['buy_now'] = esc_html__(
2871
            'Upgrade to Event Espresso 4 Right Now',
2872
            'event_espresso'
2873
        );
2874
        $buy_now_url = add_query_arg(
2875
            array(
2876
                'ee_ver'       => 'ee4',
2877
                'utm_source'   => 'ee4_plugin_admin',
2878
                'utm_medium'   => 'link',
2879
                'utm_campaign' => $utm_campaign_source,
2880
                'utm_content'  => 'buy_now_button',
2881
            ),
2882
            'http://eventespresso.com/pricing/'
2883
        );
2884
        $this->_template_args['preview_action_button'] = ! isset($this->_template_args['preview_action_button'])
2885
            ? $this->get_action_link_or_button(
2886
                '',
2887
                'buy_now',
2888
                array(),
2889
                'button-primary button-large',
2890
                $buy_now_url,
2891
                true
2892
            )
2893
            : $this->_template_args['preview_action_button'];
2894
        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
2895
            EE_ADMIN_TEMPLATE . 'admin_caf_full_page_preview.template.php',
2896
            $this->_template_args,
2897
            true
2898
        );
2899
        $this->_display_admin_page($display_sidebar);
2900
    }
2901
2902
2903
    /**
2904
     * display_admin_list_table_page_with_sidebar
2905
     * generates HTML wrapper for an admin_page with list_table
2906
     *
2907
     * @return void
2908
     * @throws DomainException
2909
     * @throws EE_Error
2910
     * @throws InvalidArgumentException
2911
     * @throws InvalidDataTypeException
2912
     * @throws InvalidInterfaceException
2913
     */
2914
    public function display_admin_list_table_page_with_sidebar()
2915
    {
2916
        $this->_display_admin_list_table_page(true);
2917
    }
2918
2919
2920
    /**
2921
     * display_admin_list_table_page_with_no_sidebar
2922
     * generates HTML wrapper for an admin_page with list_table (but with no sidebar)
2923
     *
2924
     * @return void
2925
     * @throws DomainException
2926
     * @throws EE_Error
2927
     * @throws InvalidArgumentException
2928
     * @throws InvalidDataTypeException
2929
     * @throws InvalidInterfaceException
2930
     */
2931
    public function display_admin_list_table_page_with_no_sidebar()
2932
    {
2933
        $this->_display_admin_list_table_page();
2934
    }
2935
2936
2937
    /**
2938
     * generates html wrapper for an admin_list_table page
2939
     *
2940
     * @param boolean $sidebar whether to display with sidebar or not.
2941
     * @return void
2942
     * @throws DomainException
2943
     * @throws EE_Error
2944
     * @throws InvalidArgumentException
2945
     * @throws InvalidDataTypeException
2946
     * @throws InvalidInterfaceException
2947
     */
2948
    private function _display_admin_list_table_page($sidebar = false)
2949
    {
2950
        // setup search attributes
2951
        $this->_set_search_attributes();
2952
        $this->_template_args['current_page'] = $this->_wp_page_slug;
2953
        $template_path = EE_ADMIN_TEMPLATE . 'admin_list_wrapper.template.php';
2954
        $this->_template_args['table_url'] = defined('DOING_AJAX')
2955
            ? add_query_arg(array('noheader' => 'true', 'route' => $this->_req_action), $this->_admin_base_url)
2956
            : add_query_arg(array('route' => $this->_req_action), $this->_admin_base_url);
2957
        $this->_template_args['list_table'] = $this->_list_table_object;
2958
        $this->_template_args['current_route'] = $this->_req_action;
2959
        $this->_template_args['list_table_class'] = get_class($this->_list_table_object);
2960
        $ajax_sorting_callback = $this->_list_table_object->get_ajax_sorting_callback();
2961
        if (! empty($ajax_sorting_callback)) {
2962
            $sortable_list_table_form_fields = wp_nonce_field(
2963
                $ajax_sorting_callback . '_nonce',
2964
                $ajax_sorting_callback . '_nonce',
2965
                false,
2966
                false
2967
            );
2968
            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_page" name="ajax_table_sort_page" value="'
2969
                                                . $this->page_slug
2970
                                                . '" />';
2971
            $sortable_list_table_form_fields .= '<input type="hidden" id="ajax_table_sort_action" name="ajax_table_sort_action" value="'
2972
                                                . $ajax_sorting_callback
2973
                                                . '" />';
2974
        } else {
2975
            $sortable_list_table_form_fields = '';
2976
        }
2977
        $this->_template_args['sortable_list_table_form_fields'] = $sortable_list_table_form_fields;
2978
        $hidden_form_fields = isset($this->_template_args['list_table_hidden_fields'])
2979
            ? $this->_template_args['list_table_hidden_fields']
2980
            : '';
2981
        $nonce_ref = $this->_req_action . '_nonce';
2982
        $hidden_form_fields .= '<input type="hidden" name="'
2983
                               . $nonce_ref
2984
                               . '" value="'
2985
                               . wp_create_nonce($nonce_ref)
2986
                               . '">';
2987
        $this->_template_args['list_table_hidden_fields'] = $hidden_form_fields;
2988
        // display message about search results?
2989
        $this->_template_args['before_list_table'] .= ! empty($this->_req_data['s'])
2990
            ? '<p class="ee-search-results">' . sprintf(
2991
                esc_html__('Displaying search results for the search string: %1$s', 'event_espresso'),
2992
                trim($this->_req_data['s'], '%')
2993
            ) . '</p>'
2994
            : '';
2995
        // filter before_list_table template arg
2996
        $this->_template_args['before_list_table'] = apply_filters(
2997
            'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_arg',
2998
            $this->_template_args['before_list_table'],
2999
            $this->page_slug,
3000
            $this->_req_data,
3001
            $this->_req_action
3002
        );
3003
        // convert to array and filter again
3004
        // arrays are easier to inject new items in a specific location,
3005
        // but would not be backwards compatible, so we have to add a new filter
3006
        $this->_template_args['before_list_table'] = implode(
3007
            " \n",
3008
            (array) apply_filters(
3009
                'FHEE__EE_Admin_Page___display_admin_list_table_page__before_list_table__template_args_array',
3010
                (array) $this->_template_args['before_list_table'],
3011
                $this->page_slug,
3012
                $this->_req_data,
3013
                $this->_req_action
3014
            )
3015
        );
3016
        // filter after_list_table template arg
3017
        $this->_template_args['after_list_table'] = apply_filters(
3018
            'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_arg',
3019
            $this->_template_args['after_list_table'],
3020
            $this->page_slug,
3021
            $this->_req_data,
3022
            $this->_req_action
3023
        );
3024
        // convert to array and filter again
3025
        // arrays are easier to inject new items in a specific location,
3026
        // but would not be backwards compatible, so we have to add a new filter
3027
        $this->_template_args['after_list_table'] = implode(
3028
            " \n",
3029
            (array) apply_filters(
3030
                'FHEE__EE_Admin_Page___display_admin_list_table_page__after_list_table__template_args_array',
3031
                (array) $this->_template_args['after_list_table'],
3032
                $this->page_slug,
3033
                $this->_req_data,
3034
                $this->_req_action
3035
            )
3036
        );
3037
        $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3038
            $template_path,
3039
            $this->_template_args,
3040
            true
3041
        );
3042
        // the final template wrapper
3043
        if ($sidebar) {
3044
            $this->display_admin_page_with_sidebar();
3045
        } else {
3046
            $this->display_admin_page_with_no_sidebar();
3047
        }
3048
    }
3049
3050
3051
    /**
3052
     * This just prepares a legend using the given items and the admin_details_legend.template.php file and returns the
3053
     * html string for the legend.
3054
     * $items are expected in an array in the following format:
3055
     * $legend_items = array(
3056
     *        'item_id' => array(
3057
     *            'icon' => 'http://url_to_icon_being_described.png',
3058
     *            'desc' => esc_html__('localized description of item');
3059
     *        )
3060
     * );
3061
     *
3062
     * @param  array $items see above for format of array
3063
     * @return string html string of legend
3064
     * @throws DomainException
3065
     */
3066
    protected function _display_legend($items)
3067
    {
3068
        $this->_template_args['items'] = apply_filters(
3069
            'FHEE__EE_Admin_Page___display_legend__items',
3070
            (array) $items,
3071
            $this
3072
        );
3073
        /** @var EventEspresso\core\admin\StatusChangeNotice $status_change_notice */
3074
        $status_change_notice = $this->loader->getShared('EventEspresso\core\admin\StatusChangeNotice');
3075
        if (! $status_change_notice->isDismissed()) {
3076
            $this->_template_args['status_change_notice'] = EEH_Template::display_template(
3077
                EE_ADMIN_TEMPLATE . 'status_change_notice.template.php',
3078
                [ 'context' => '__admin-legend', 'page_slug' => $this->page_slug ],
3079
                true
3080
            );
3081
        }
3082
        return EEH_Template::display_template(
3083
            EE_ADMIN_TEMPLATE . 'admin_details_legend.template.php',
3084
            $this->_template_args,
3085
            true
3086
        );
3087
    }
3088
3089
3090
    /**
3091
     * This is used whenever we're DOING_AJAX to return a formatted json array that our calling javascript can expect
3092
     * The returned json object is created from an array in the following format:
3093
     * array(
3094
     *  'error' => FALSE, //(default FALSE), contains any errors and/or exceptions (exceptions return json early),
3095
     *  'success' => FALSE, //(default FALSE) - contains any special success message.
3096
     *  'notices' => '', // - contains any EE_Error formatted notices
3097
     *  'content' => 'string can be html', //this is a string of formatted content (can be html)
3098
     *  'data' => array() //this can be any key/value pairs that a method returns for later json parsing by the js.
3099
     *  We're also going to include the template args with every package (so js can pick out any specific template args
3100
     *  that might be included in here)
3101
     * )
3102
     * The json object is populated by whatever is set in the $_template_args property.
3103
     *
3104
     * @param bool  $sticky_notices    Used to indicate whether you want to ensure notices are added to a transient
3105
     *                                 instead of displayed.
3106
     * @param array $notices_arguments Use this to pass any additional args on to the _process_notices.
3107
     * @return void
3108
     * @throws EE_Error
3109
     * @throws InvalidArgumentException
3110
     * @throws InvalidDataTypeException
3111
     * @throws InvalidInterfaceException
3112
     */
3113
    protected function _return_json($sticky_notices = false, $notices_arguments = array())
3114
    {
3115
        // make sure any EE_Error notices have been handled.
3116
        $this->_process_notices($notices_arguments, true, $sticky_notices);
3117
        $data = isset($this->_template_args['data']) ? $this->_template_args['data'] : array();
3118
        unset($this->_template_args['data']);
3119
        $json = array(
3120
            'error'     => isset($this->_template_args['error']) ? $this->_template_args['error'] : false,
3121
            'success'   => isset($this->_template_args['success']) ? $this->_template_args['success'] : false,
3122
            'errors'    => isset($this->_template_args['errors']) ? $this->_template_args['errors'] : false,
3123
            'attention' => isset($this->_template_args['attention']) ? $this->_template_args['attention'] : false,
3124
            'notices'   => EE_Error::get_notices(),
3125
            'content'   => isset($this->_template_args['admin_page_content'])
3126
                ? $this->_template_args['admin_page_content'] : '',
3127
            'data'      => array_merge($data, array('template_args' => $this->_template_args)),
3128
            'isEEajax'  => true
3129
            // special flag so any ajax.Success methods in js can identify this return package as a EEajax package.
3130
        );
3131
        // make sure there are no php errors or headers_sent.  Then we can set correct json header.
3132
        if (null === error_get_last() || ! headers_sent()) {
3133
            header('Content-Type: application/json; charset=UTF-8');
3134
        }
3135
        echo wp_json_encode($json);
3136
        exit();
3137
    }
3138
3139
3140
    /**
3141
     * Simply a wrapper for the protected method so we can call this outside the class (ONLY when doing ajax)
3142
     *
3143
     * @return void
3144
     * @throws EE_Error
3145
     * @throws InvalidArgumentException
3146
     * @throws InvalidDataTypeException
3147
     * @throws InvalidInterfaceException
3148
     */
3149
    public function return_json()
3150
    {
3151
        if (defined('DOING_AJAX') && DOING_AJAX) {
3152
            $this->_return_json();
3153
        } else {
3154
            throw new EE_Error(
3155
                sprintf(
3156
                    esc_html__('The public %s method can only be called when DOING_AJAX = TRUE', 'event_espresso'),
3157
                    __FUNCTION__
3158
                )
3159
            );
3160
        }
3161
    }
3162
3163
3164
    /**
3165
     * This provides a way for child hook classes to send along themselves by reference so methods/properties within
3166
     * them can be accessed by EE_Admin_child pages. This is assigned to the $_hook_obj property.
3167
     *
3168
     * @param EE_Admin_Hooks $hook_obj This will be the object for the EE_Admin_Hooks child
3169
     */
3170
    public function set_hook_object(EE_Admin_Hooks $hook_obj)
3171
    {
3172
        $this->_hook_obj = $hook_obj;
3173
    }
3174
3175
3176
    /**
3177
     *        generates  HTML wrapper with Tabbed nav for an admin page
3178
     *
3179
     * @param boolean $about whether to use the special about page wrapper or default.
3180
     * @return void
3181
     * @throws DomainException
3182
     * @throws EE_Error
3183
     * @throws InvalidArgumentException
3184
     * @throws InvalidDataTypeException
3185
     * @throws InvalidInterfaceException
3186
     */
3187
    public function admin_page_wrapper($about = false)
3188
    {
3189
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3190
        $this->_nav_tabs = $this->_get_main_nav_tabs();
3191
        $this->_template_args['nav_tabs'] = $this->_nav_tabs;
3192
        $this->_template_args['admin_page_title'] = $this->_admin_page_title;
3193
        $this->_template_args['before_admin_page_content'] = apply_filters(
3194
            "FHEE_before_admin_page_content{$this->_current_page}{$this->_current_view}",
3195
            isset($this->_template_args['before_admin_page_content'])
3196
                ? $this->_template_args['before_admin_page_content']
3197
                : ''
3198
        );
3199
        $this->_template_args['after_admin_page_content'] = apply_filters(
3200
            "FHEE_after_admin_page_content{$this->_current_page}{$this->_current_view}",
3201
            isset($this->_template_args['after_admin_page_content'])
3202
                ? $this->_template_args['after_admin_page_content']
3203
                : ''
3204
        );
3205
        $this->_template_args['after_admin_page_content'] .= $this->_set_help_popup_content();
3206
        // load settings page wrapper template
3207
        $template_path = ! defined('DOING_AJAX')
3208
            ? EE_ADMIN_TEMPLATE . 'admin_wrapper.template.php'
3209
            : EE_ADMIN_TEMPLATE
3210
              . 'admin_wrapper_ajax.template.php';
3211
        // about page?
3212
        $template_path = $about
3213
            ? EE_ADMIN_TEMPLATE . 'about_admin_wrapper.template.php'
3214
            : $template_path;
3215
        if (defined('DOING_AJAX')) {
3216
            $this->_template_args['admin_page_content'] = EEH_Template::display_template(
3217
                $template_path,
3218
                $this->_template_args,
3219
                true
3220
            );
3221
            $this->_return_json();
3222
        } else {
3223
            EEH_Template::display_template($template_path, $this->_template_args);
3224
        }
3225
    }
3226
3227
3228
    /**
3229
     * This returns the admin_nav tabs html using the configuration in the _nav_tabs property
3230
     *
3231
     * @return string html
3232
     * @throws EE_Error
3233
     */
3234
    protected function _get_main_nav_tabs()
3235
    {
3236
        // let's generate the html using the EEH_Tabbed_Content helper.
3237
        // We do this here so that it's possible for child classes to add in nav tabs dynamically at the last minute
3238
        // (rather than setting in the page_routes array)
3239
        return EEH_Tabbed_Content::display_admin_nav_tabs($this->_nav_tabs);
3240
    }
3241
3242
3243
    /**
3244
     *        sort nav tabs
3245
     *
3246
     * @param $a
3247
     * @param $b
3248
     * @return int
3249
     */
3250
    private function _sort_nav_tabs($a, $b)
3251
    {
3252
        if ($a['order'] === $b['order']) {
3253
            return 0;
3254
        }
3255
        return ($a['order'] < $b['order']) ? -1 : 1;
3256
    }
3257
3258
3259
    /**
3260
     *    generates HTML for the forms used on admin pages
3261
     *
3262
     * @param    array $input_vars - array of input field details
3263
     * @param string   $generator  (options are 'string' or 'array', basically use this to indicate which generator to
3264
     *                             use)
3265
     * @param bool     $id
3266
     * @return string
3267
     * @uses   EEH_Form_Fields::get_form_fields (/helper/EEH_Form_Fields.helper.php)
3268
     * @uses   EEH_Form_Fields::get_form_fields_array (/helper/EEH_Form_Fields.helper.php)
3269
     */
3270
    protected function _generate_admin_form_fields($input_vars = array(), $generator = 'string', $id = false)
3271
    {
3272
        return $generator === 'string'
3273
            ? EEH_Form_Fields::get_form_fields($input_vars, $id)
3274
            : EEH_Form_Fields::get_form_fields_array($input_vars);
3275
    }
3276
3277
3278
    /**
3279
     * generates the "Save" and "Save & Close" buttons for edit forms
3280
     *
3281
     * @param bool             $both     if true then both buttons will be generated.  If false then just the "Save &
3282
     *                                   Close" button.
3283
     * @param array            $text     if included, generator will use the given text for the buttons ( array([0] =>
3284
     *                                   'Save', [1] => 'save & close')
3285
     * @param array            $actions  if included allows us to set the actions that each button will carry out (i.e.
3286
     *                                   via the "name" value in the button).  We can also use this to just dump
3287
     *                                   default actions by submitting some other value.
3288
     * @param bool|string|null $referrer if false then we just do the default action on save and close.  Other wise it
3289
     *                                   will use the $referrer string. IF null, then we don't do ANYTHING on save and
3290
     *                                   close (normal form handling).
3291
     */
3292
    protected function _set_save_buttons($both = true, $text = array(), $actions = array(), $referrer = null)
3293
    {
3294
        // make sure $text and $actions are in an array
3295
        $text = (array) $text;
3296
        $actions = (array) $actions;
3297
        $referrer_url = empty($referrer)
3298
            ? '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3299
              . $_SERVER['REQUEST_URI']
3300
              . '" />'
3301
            : '<input type="hidden" id="save_and_close_referrer" name="save_and_close_referrer" value="'
3302
              . $referrer
3303
              . '" />';
3304
        $button_text = ! empty($text)
3305
            ? $text
3306
            : array(
3307
                esc_html__('Save', 'event_espresso'),
3308
                esc_html__('Save and Close', 'event_espresso'),
3309
            );
3310
        $default_names = array('save', 'save_and_close');
3311
        // add in a hidden index for the current page (so save and close redirects properly)
3312
        $this->_template_args['save_buttons'] = $referrer_url;
3313
        foreach ($button_text as $key => $button) {
3314
            $ref = $default_names[ $key ];
3315
            $this->_template_args['save_buttons'] .= '<input type="submit" class="button-primary '
3316
                                                     . $ref
3317
                                                     . '" value="'
3318
                                                     . $button
3319
                                                     . '" name="'
3320
                                                     . (! empty($actions) ? $actions[ $key ] : $ref)
3321
                                                     . '" id="'
3322
                                                     . $this->_current_view . '_' . $ref
3323
                                                     . '" />';
3324
            if (! $both) {
3325
                break;
3326
            }
3327
        }
3328
    }
3329
3330
3331
    /**
3332
     * Wrapper for the protected function.  Allows plugins/addons to call this to set the form tags.
3333
     *
3334
     * @see   $this->_set_add_edit_form_tags() for details on params
3335
     * @since 4.6.0
3336
     * @param string $route
3337
     * @param array  $additional_hidden_fields
3338
     */
3339
    public function set_add_edit_form_tags($route = '', $additional_hidden_fields = array())
3340
    {
3341
        $this->_set_add_edit_form_tags($route, $additional_hidden_fields);
3342
    }
3343
3344
3345
    /**
3346
     * set form open and close tags on add/edit pages.
3347
     *
3348
     * @param string $route                    the route you want the form to direct to
3349
     * @param array  $additional_hidden_fields any additional hidden fields required in the form header
3350
     * @return void
3351
     */
3352
    protected function _set_add_edit_form_tags($route = '', $additional_hidden_fields = array())
3353
    {
3354
        if (empty($route)) {
3355
            $user_msg = esc_html__(
3356
                'An error occurred. No action was set for this page\'s form.',
3357
                'event_espresso'
3358
            );
3359
            $dev_msg = $user_msg . "\n"
3360
                       . sprintf(
3361
                           esc_html__('The $route argument is required for the %s->%s method.', 'event_espresso'),
3362
                           __FUNCTION__,
3363
                           EE_Admin_Page::class
3364
                       );
3365
            EE_Error::add_error($user_msg . '||' . $dev_msg, __FILE__, __FUNCTION__, __LINE__);
3366
        }
3367
        // open form
3368
        $this->_template_args['before_admin_page_content'] = '<form name="form" method="post" action="'
3369
                                                             . $this->_admin_base_url
3370
                                                             . '" id="'
3371
                                                             . $route
3372
                                                             . '_event_form" >';
3373
        // add nonce
3374
        $nonce = wp_nonce_field($route . '_nonce', $route . '_nonce', false, false);
3375
        $this->_template_args['before_admin_page_content'] .= "\n\t" . $nonce;
3376
        // add REQUIRED form action
3377
        $hidden_fields = array(
3378
            'action' => array('type' => 'hidden', 'value' => $route),
3379
        );
3380
        // merge arrays
3381
        $hidden_fields = is_array($additional_hidden_fields)
3382
            ? array_merge($hidden_fields, $additional_hidden_fields)
3383
            : $hidden_fields;
3384
        // generate form fields
3385
        $form_fields = $this->_generate_admin_form_fields($hidden_fields, 'array');
3386
        // add fields to form
3387
        foreach ((array) $form_fields as $field_name => $form_field) {
3388
            $this->_template_args['before_admin_page_content'] .= "\n\t" . $form_field['field'];
3389
        }
3390
        // close form
3391
        $this->_template_args['after_admin_page_content'] = '</form>';
3392
    }
3393
3394
3395
    /**
3396
     * Public Wrapper for _redirect_after_action() method since its
3397
     * discovered it would be useful for external code to have access.
3398
     *
3399
     * @param bool   $success
3400
     * @param string $what
3401
     * @param string $action_desc
3402
     * @param array  $query_args
3403
     * @param bool   $override_overwrite
3404
     * @throws EE_Error
3405
     * @throws InvalidArgumentException
3406
     * @throws InvalidDataTypeException
3407
     * @throws InvalidInterfaceException
3408
     * @see   EE_Admin_Page::_redirect_after_action() for params.
3409
     * @since 4.5.0
3410
     */
3411
    public function redirect_after_action(
3412
        $success = false,
3413
        $what = 'item',
3414
        $action_desc = 'processed',
3415
        $query_args = array(),
3416
        $override_overwrite = false
3417
    ) {
3418
        $this->_redirect_after_action(
3419
            $success,
3420
            $what,
3421
            $action_desc,
3422
            $query_args,
3423
            $override_overwrite
3424
        );
3425
    }
3426
3427
3428
    /**
3429
     * Helper method for merging existing request data with the returned redirect url.
3430
     *
3431
     * This is typically used for redirects after an action so that if the original view was a filtered view those
3432
     * filters are still applied.
3433
     *
3434
     * @param array $new_route_data
3435
     * @return array
3436
     */
3437
    protected function mergeExistingRequestParamsWithRedirectArgs(array $new_route_data)
3438
    {
3439
        foreach ($this->_req_data as $ref => $value) {
3440
            // unset nonces
3441
            if (strpos($ref, 'nonce') !== false) {
3442
                unset($this->_req_data[ $ref ]);
3443
                continue;
3444
            }
3445
            // urlencode values.
3446
            $value = is_array($value) ? array_map('urlencode', $value) : urlencode($value);
3447
            $this->_req_data[ $ref ] = $value;
3448
        }
3449
        return array_merge($this->_req_data, $new_route_data);
3450
    }
3451
3452
3453
    /**
3454
     *    _redirect_after_action
3455
     *
3456
     * @param int    $success            - whether success was for two or more records, or just one, or none
3457
     * @param string $what               - what the action was performed on
3458
     * @param string $action_desc        - what was done ie: updated, deleted, etc
3459
     * @param array  $query_args         - an array of query_args to be added to the URL to redirect to after the admin
3460
     *                                   action is completed
3461
     * @param BOOL   $override_overwrite by default all EE_Error::success messages are overwritten, this allows you to
3462
     *                                   override this so that they show.
3463
     * @return void
3464
     * @throws EE_Error
3465
     * @throws InvalidArgumentException
3466
     * @throws InvalidDataTypeException
3467
     * @throws InvalidInterfaceException
3468
     */
3469
    protected function _redirect_after_action(
3470
        $success = 0,
3471
        $what = 'item',
3472
        $action_desc = 'processed',
3473
        $query_args = array(),
3474
        $override_overwrite = false
3475
    ) {
3476
        do_action('AHEE_log', __FILE__, __FUNCTION__, '');
3477
        // class name for actions/filters.
3478
        $classname = get_class($this);
3479
        // set redirect url.
3480
        // Note if there is a "page" index in the $query_args then we go with vanilla admin.php route,
3481
        // otherwise we go with whatever is set as the _admin_base_url
3482
        $redirect_url = isset($query_args['page']) ? admin_url('admin.php') : $this->_admin_base_url;
3483
        $notices = EE_Error::get_notices(false);
3484
        // overwrite default success messages //BUT ONLY if overwrite not overridden
3485
        if (! $override_overwrite || ! empty($notices['errors'])) {
3486
            EE_Error::overwrite_success();
3487
        }
3488
        if (! empty($what) && ! empty($action_desc) && empty($notices['errors'])) {
3489
            // how many records affected ? more than one record ? or just one ?
3490
            if ($success > 1) {
3491
                // set plural msg
3492
                EE_Error::add_success(
3493
                    sprintf(
3494
                        esc_html__('The "%s" have been successfully %s.', 'event_espresso'),
3495
                        $what,
3496
                        $action_desc
3497
                    ),
3498
                    __FILE__,
3499
                    __FUNCTION__,
3500
                    __LINE__
3501
                );
3502
            } elseif ($success === 1) {
3503
                // set singular msg
3504
                EE_Error::add_success(
3505
                    sprintf(
3506
                        esc_html__('The "%s" has been successfully %s.', 'event_espresso'),
3507
                        $what,
3508
                        $action_desc
3509
                    ),
3510
                    __FILE__,
3511
                    __FUNCTION__,
3512
                    __LINE__
3513
                );
3514
            }
3515
        }
3516
        // check that $query_args isn't something crazy
3517
        if (! is_array($query_args)) {
3518
            $query_args = array();
3519
        }
3520
        /**
3521
         * Allow injecting actions before the query_args are modified for possible different
3522
         * redirections on save and close actions
3523
         *
3524
         * @since 4.2.0
3525
         * @param array $query_args       The original query_args array coming into the
3526
         *                                method.
3527
         */
3528
        do_action(
3529
            "AHEE__{$classname}___redirect_after_action__before_redirect_modification_{$this->_req_action}",
3530
            $query_args
3531
        );
3532
        // calculate where we're going (if we have a "save and close" button pushed)
3533
        if (isset($this->_req_data['save_and_close'], $this->_req_data['save_and_close_referrer'])) {
3534
            // even though we have the save_and_close referrer, we need to parse the url for the action in order to generate a nonce
3535
            $parsed_url = parse_url($this->_req_data['save_and_close_referrer']);
3536
            // regenerate query args array from referrer URL
3537
            parse_str($parsed_url['query'], $query_args);
3538
            // correct page and action will be in the query args now
3539
            $redirect_url = admin_url('admin.php');
3540
        }
3541
        // merge any default query_args set in _default_route_query_args property
3542
        if (! empty($this->_default_route_query_args) && ! $this->_is_UI_request) {
3543
            $args_to_merge = array();
3544
            foreach ($this->_default_route_query_args as $query_param => $query_value) {
3545
                // is there a wp_referer array in our _default_route_query_args property?
3546
                if ($query_param === 'wp_referer') {
3547
                    $query_value = (array) $query_value;
3548
                    foreach ($query_value as $reference => $value) {
3549
                        if (strpos($reference, 'nonce') !== false) {
3550
                            continue;
3551
                        }
3552
                        // finally we will override any arguments in the referer with
3553
                        // what might be set on the _default_route_query_args array.
3554
                        if (isset($this->_default_route_query_args[ $reference ])) {
3555
                            $args_to_merge[ $reference ] = urlencode($this->_default_route_query_args[ $reference ]);
3556
                        } else {
3557
                            $args_to_merge[ $reference ] = urlencode($value);
3558
                        }
3559
                    }
3560
                    continue;
3561
                }
3562
                $args_to_merge[ $query_param ] = $query_value;
3563
            }
3564
            // now let's merge these arguments but override with what was specifically sent in to the
3565
            // redirect.
3566
            $query_args = array_merge($args_to_merge, $query_args);
3567
        }
3568
        $this->_process_notices($query_args);
3569
        // generate redirect url
3570
        // if redirecting to anything other than the main page, add a nonce
3571
        if (isset($query_args['action'])) {
3572
            // manually generate wp_nonce and merge that with the query vars
3573
            // becuz the wp_nonce_url function wrecks havoc on some vars
3574
            $query_args['_wpnonce'] = wp_create_nonce($query_args['action'] . '_nonce');
3575
        }
3576
        // we're adding some hooks and filters in here for processing any things just before redirects
3577
        // (example: an admin page has done an insert or update and we want to run something after that).
3578
        do_action('AHEE_redirect_' . $classname . $this->_req_action, $query_args);
3579
        $redirect_url = apply_filters(
3580
            'FHEE_redirect_' . $classname . $this->_req_action,
3581
            EE_Admin_Page::add_query_args_and_nonce($query_args, $redirect_url),
3582
            $query_args
3583
        );
3584
        // check if we're doing ajax.  If we are then lets just return the results and js can handle how it wants.
3585
        if (defined('DOING_AJAX')) {
3586
            $default_data = array(
3587
                'close'        => true,
3588
                'redirect_url' => $redirect_url,
3589
                'where'        => 'main',
3590
                'what'         => 'append',
3591
            );
3592
            $this->_template_args['success'] = $success;
3593
            $this->_template_args['data'] = ! empty($this->_template_args['data']) ? array_merge(
3594
                $default_data,
3595
                $this->_template_args['data']
3596
            ) : $default_data;
3597
            $this->_return_json();
3598
        }
3599
        wp_safe_redirect($redirect_url);
3600
        exit();
3601
    }
3602
3603
3604
    /**
3605
     * process any notices before redirecting (or returning ajax request)
3606
     * This method sets the $this->_template_args['notices'] attribute;
3607
     *
3608
     * @param array $query_args         any query args that need to be used for notice transient ('action')
3609
     * @param bool  $skip_route_verify  This is typically used when we are processing notices REALLY early and
3610
     *                                  page_routes haven't been defined yet.
3611
     * @param bool  $sticky_notices     This is used to flag that regardless of whether this is doing_ajax or not, we
3612
     *                                  still save a transient for the notice.
3613
     * @return void
3614
     * @throws EE_Error
3615
     * @throws InvalidArgumentException
3616
     * @throws InvalidDataTypeException
3617
     * @throws InvalidInterfaceException
3618
     */
3619
    protected function _process_notices($query_args = array(), $skip_route_verify = false, $sticky_notices = true)
3620
    {
3621
        // first let's set individual error properties if doing_ajax and the properties aren't already set.
3622
        if (defined('DOING_AJAX') && DOING_AJAX) {
3623
            $notices = EE_Error::get_notices(false);
3624 View Code Duplication
            if (empty($this->_template_args['success'])) {
3625
                $this->_template_args['success'] = isset($notices['success']) ? $notices['success'] : false;
3626
            }
3627 View Code Duplication
            if (empty($this->_template_args['errors'])) {
3628
                $this->_template_args['errors'] = isset($notices['errors']) ? $notices['errors'] : false;
3629
            }
3630 View Code Duplication
            if (empty($this->_template_args['attention'])) {
3631
                $this->_template_args['attention'] = isset($notices['attention']) ? $notices['attention'] : false;
3632
            }
3633
        }
3634
        $this->_template_args['notices'] = EE_Error::get_notices();
3635
        // IF this isn't ajax we need to create a transient for the notices using the route (however, overridden if $sticky_notices == true)
3636
        if (! defined('DOING_AJAX') || $sticky_notices) {
3637
            $route = isset($query_args['action']) ? $query_args['action'] : 'default';
3638
            $this->_add_transient(
3639
                $route,
3640
                $this->_template_args['notices'],
3641
                true,
3642
                $skip_route_verify
3643
            );
3644
        }
3645
    }
3646
3647
3648
    /**
3649
     * get_action_link_or_button
3650
     * returns the button html for adding, editing, or deleting an item (depending on given type)
3651
     *
3652
     * @param string $action        use this to indicate which action the url is generated with.
3653
     * @param string $type          accepted strings must be defined in the $_labels['button'] array(as the key)
3654
     *                              property.
3655
     * @param array  $extra_request if the button requires extra params you can include them in $key=>$value pairs.
3656
     * @param string $class         Use this to give the class for the button. Defaults to 'button-primary'
3657
     * @param string $base_url      If this is not provided
3658
     *                              the _admin_base_url will be used as the default for the button base_url.
3659
     *                              Otherwise this value will be used.
3660
     * @param bool   $exclude_nonce If true then no nonce will be in the generated button link.
3661
     * @return string
3662
     * @throws InvalidArgumentException
3663
     * @throws InvalidInterfaceException
3664
     * @throws InvalidDataTypeException
3665
     * @throws EE_Error
3666
     */
3667
    public function get_action_link_or_button(
3668
        $action,
3669
        $type = 'add',
3670
        $extra_request = array(),
3671
        $class = 'button-primary',
3672
        $base_url = '',
3673
        $exclude_nonce = false
3674
    ) {
3675
        // first let's validate the action (if $base_url is FALSE otherwise validation will happen further along)
3676
        if (empty($base_url) && ! isset($this->_page_routes[ $action ])) {
3677
            throw new EE_Error(
3678
                sprintf(
3679
                    esc_html__(
3680
                        'There is no page route for given action for the button.  This action was given: %s',
3681
                        'event_espresso'
3682
                    ),
3683
                    $action
3684
                )
3685
            );
3686
        }
3687
        if (! isset($this->_labels['buttons'][ $type ])) {
3688
            throw new EE_Error(
3689
                sprintf(
3690
                    __(
3691
                        'There is no label for the given button type (%s). Labels are set in the <code>_page_config</code> property.',
3692
                        'event_espresso'
3693
                    ),
3694
                    $type
3695
                )
3696
            );
3697
        }
3698
        // finally check user access for this button.
3699
        $has_access = $this->check_user_access($action, true);
3700
        if (! $has_access) {
3701
            return '';
3702
        }
3703
        $_base_url = ! $base_url ? $this->_admin_base_url : $base_url;
3704
        $query_args = array(
3705
            'action' => $action,
3706
        );
3707
        // merge extra_request args but make sure our original action takes precedence and doesn't get overwritten.
3708
        if (! empty($extra_request)) {
3709
            $query_args = array_merge($extra_request, $query_args);
3710
        }
3711
        $url = EE_Admin_Page::add_query_args_and_nonce($query_args, $_base_url, false, $exclude_nonce);
3712
        return EEH_Template::get_button_or_link($url, $this->_labels['buttons'][ $type ], $class);
3713
    }
3714
3715
3716
    /**
3717
     * _per_page_screen_option
3718
     * Utility function for adding in a per_page_option in the screen_options_dropdown.
3719
     *
3720
     * @return void
3721
     * @throws InvalidArgumentException
3722
     * @throws InvalidInterfaceException
3723
     * @throws InvalidDataTypeException
3724
     */
3725
    protected function _per_page_screen_option()
3726
    {
3727
        $option = 'per_page';
3728
        $args = array(
3729
            'label'   => apply_filters(
3730
                'FHEE__EE_Admin_Page___per_page_screen_options___label',
3731
                $this->_admin_page_title,
3732
                $this
3733
            ),
3734
            'default' => (int) apply_filters(
3735
                'FHEE__EE_Admin_Page___per_page_screen_options__default',
3736
                20
3737
            ),
3738
            'option'  => $this->_current_page . '_' . $this->_current_view . '_per_page',
3739
        );
3740
        // ONLY add the screen option if the user has access to it.
3741
        if ($this->check_user_access($this->_current_view, true)) {
3742
            add_screen_option($option, $args);
3743
        }
3744
    }
3745
3746
3747
    /**
3748
     * set_per_page_screen_option
3749
     * All this does is make sure that WordPress saves any per_page screen options (if set) for the current page.
3750
     * we have to do this rather than running inside the 'set-screen-options' hook because it runs earlier than
3751
     * admin_menu.
3752
     *
3753
     * @return void
3754
     */
3755
    private function _set_per_page_screen_options()
3756
    {
3757
        if (isset($_POST['wp_screen_options']) && is_array($_POST['wp_screen_options'])) {
3758
            check_admin_referer('screen-options-nonce', 'screenoptionnonce');
3759
            if (! $user = wp_get_current_user()) {
3760
                return;
3761
            }
3762
            $option = $_POST['wp_screen_options']['option'];
3763
            $value = $_POST['wp_screen_options']['value'];
3764
            if ($option !== sanitize_key($option)) {
3765
                return;
3766
            }
3767
            $map_option = $option;
3768
            $option = str_replace('-', '_', $option);
3769
            switch ($map_option) {
3770
                case $this->_current_page . '_' . $this->_current_view . '_per_page':
3771
                    $value = (int) $value;
3772
                    $max_value = apply_filters(
3773
                        'FHEE__EE_Admin_Page___set_per_page_screen_options__max_value',
3774
                        999,
3775
                        $this->_current_page,
3776
                        $this->_current_view
3777
                    );
3778
                    if ($value < 1) {
3779
                        return;
3780
                    }
3781
                    $value = min($value, $max_value);
3782
                    break;
3783
                default:
3784
                    $value = apply_filters(
3785
                        'FHEE__EE_Admin_Page___set_per_page_screen_options__value',
3786
                        false,
3787
                        $option,
3788
                        $value
3789
                    );
3790
                    if (false === $value) {
3791
                        return;
3792
                    }
3793
                    break;
3794
            }
3795
            update_user_meta($user->ID, $option, $value);
3796
            wp_safe_redirect(remove_query_arg(array('pagenum', 'apage', 'paged'), wp_get_referer()));
3797
            exit;
3798
        }
3799
    }
3800
3801
3802
    /**
3803
     * This just allows for setting the $_template_args property if it needs to be set outside the object
3804
     *
3805
     * @param array $data array that will be assigned to template args.
3806
     */
3807
    public function set_template_args($data)
3808
    {
3809
        $this->_template_args = array_merge($this->_template_args, (array) $data);
3810
    }
3811
3812
3813
    /**
3814
     * This makes available the WP transient system for temporarily moving data between routes
3815
     *
3816
     * @param string $route             the route that should receive the transient
3817
     * @param array  $data              the data that gets sent
3818
     * @param bool   $notices           If this is for notices then we use this to indicate so, otherwise its just a
3819
     *                                  normal route transient.
3820
     * @param bool   $skip_route_verify Used to indicate we want to skip route verification.  This is usually ONLY used
3821
     *                                  when we are adding a transient before page_routes have been defined.
3822
     * @return void
3823
     * @throws EE_Error
3824
     */
3825
    protected function _add_transient($route, $data, $notices = false, $skip_route_verify = false)
3826
    {
3827
        $user_id = get_current_user_id();
3828
        if (! $skip_route_verify) {
3829
            $this->_verify_route($route);
3830
        }
3831
        // now let's set the string for what kind of transient we're setting
3832
        $transient = $notices
3833
            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3834
            : 'rte_tx_' . $route . '_' . $user_id;
3835
        $data = $notices ? array('notices' => $data) : $data;
3836
        // is there already a transient for this route?  If there is then let's ADD to that transient
3837
        $existing = is_multisite() && is_network_admin()
3838
            ? get_site_transient($transient)
3839
            : get_transient($transient);
3840
        if ($existing) {
3841
            $data = array_merge((array) $data, (array) $existing);
3842
        }
3843
        if (is_multisite() && is_network_admin()) {
3844
            set_site_transient($transient, $data, 8);
3845
        } else {
3846
            set_transient($transient, $data, 8);
3847
        }
3848
    }
3849
3850
3851
    /**
3852
     * this retrieves the temporary transient that has been set for moving data between routes.
3853
     *
3854
     * @param bool   $notices true we get notices transient. False we just return normal route transient
3855
     * @param string $route
3856
     * @return mixed data
3857
     */
3858
    protected function _get_transient($notices = false, $route = '')
3859
    {
3860
        $user_id = get_current_user_id();
3861
        $route = ! $route ? $this->_req_action : $route;
3862
        $transient = $notices
3863
            ? 'ee_rte_n_tx_' . $route . '_' . $user_id
3864
            : 'rte_tx_' . $route . '_' . $user_id;
3865
        $data = is_multisite() && is_network_admin()
3866
            ? get_site_transient($transient)
3867
            : get_transient($transient);
3868
        // delete transient after retrieval (just in case it hasn't expired);
3869
        if (is_multisite() && is_network_admin()) {
3870
            delete_site_transient($transient);
3871
        } else {
3872
            delete_transient($transient);
3873
        }
3874
        return $notices && isset($data['notices']) ? $data['notices'] : $data;
3875
    }
3876
3877
3878
    /**
3879
     * The purpose of this method is just to run garbage collection on any EE transients that might have expired but
3880
     * would not be called later. This will be assigned to run on a specific EE Admin page. (place the method in the
3881
     * default route callback on the EE_Admin page you want it run.)
3882
     *
3883
     * @return void
3884
     */
3885
    protected function _transient_garbage_collection()
3886
    {
3887
        global $wpdb;
3888
        // retrieve all existing transients
3889
        $query = "SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%rte_tx_%' OR option_name LIKE '%rte_n_tx_%'";
3890
        if ($results = $wpdb->get_results($query)) {
3891
            foreach ($results as $result) {
3892
                $transient = str_replace('_transient_', '', $result->option_name);
3893
                get_transient($transient);
3894
                if (is_multisite() && is_network_admin()) {
3895
                    get_site_transient($transient);
3896
                }
3897
            }
3898
        }
3899
    }
3900
3901
3902
    /**
3903
     * get_view
3904
     *
3905
     * @return string content of _view property
3906
     */
3907
    public function get_view()
3908
    {
3909
        return $this->_view;
3910
    }
3911
3912
3913
    /**
3914
     * getter for the protected $_views property
3915
     *
3916
     * @return array
3917
     */
3918
    public function get_views()
3919
    {
3920
        return $this->_views;
3921
    }
3922
3923
3924
    /**
3925
     * get_current_page
3926
     *
3927
     * @return string _current_page property value
3928
     */
3929
    public function get_current_page()
3930
    {
3931
        return $this->_current_page;
3932
    }
3933
3934
3935
    /**
3936
     * get_current_view
3937
     *
3938
     * @return string _current_view property value
3939
     */
3940
    public function get_current_view()
3941
    {
3942
        return $this->_current_view;
3943
    }
3944
3945
3946
    /**
3947
     * get_current_screen
3948
     *
3949
     * @return object The current WP_Screen object
3950
     */
3951
    public function get_current_screen()
3952
    {
3953
        return $this->_current_screen;
3954
    }
3955
3956
3957
    /**
3958
     * get_current_page_view_url
3959
     *
3960
     * @return string This returns the url for the current_page_view.
3961
     */
3962
    public function get_current_page_view_url()
3963
    {
3964
        return $this->_current_page_view_url;
3965
    }
3966
3967
3968
    /**
3969
     * just returns the _req_data property
3970
     *
3971
     * @return array
3972
     */
3973
    public function get_request_data()
3974
    {
3975
        return $this->_req_data;
3976
    }
3977
3978
3979
    /**
3980
     * returns the _req_data protected property
3981
     *
3982
     * @return string
3983
     */
3984
    public function get_req_action()
3985
    {
3986
        return $this->_req_action;
3987
    }
3988
3989
3990
    /**
3991
     * @return bool  value of $_is_caf property
3992
     */
3993
    public function is_caf()
3994
    {
3995
        return $this->_is_caf;
3996
    }
3997
3998
3999
    /**
4000
     * @return mixed
4001
     */
4002
    public function default_espresso_metaboxes()
4003
    {
4004
        return $this->_default_espresso_metaboxes;
4005
    }
4006
4007
4008
    /**
4009
     * @return mixed
4010
     */
4011
    public function admin_base_url()
4012
    {
4013
        return $this->_admin_base_url;
4014
    }
4015
4016
4017
    /**
4018
     * @return mixed
4019
     */
4020
    public function wp_page_slug()
4021
    {
4022
        return $this->_wp_page_slug;
4023
    }
4024
4025
4026
    /**
4027
     * updates  espresso configuration settings
4028
     *
4029
     * @param string                   $tab
4030
     * @param EE_Config_Base|EE_Config $config
4031
     * @param string                   $file file where error occurred
4032
     * @param string                   $func function  where error occurred
4033
     * @param string                   $line line no where error occurred
4034
     * @return boolean
4035
     */
4036
    protected function _update_espresso_configuration($tab, $config, $file = '', $func = '', $line = '')
4037
    {
4038
        // remove any options that are NOT going to be saved with the config settings.
4039
        if (isset($config->core->ee_ueip_optin)) {
4040
            // TODO: remove the following two lines and make sure values are migrated from 3.1
4041
            update_option('ee_ueip_optin', $config->core->ee_ueip_optin);
4042
            update_option('ee_ueip_has_notified', true);
4043
        }
4044
        // and save it (note we're also doing the network save here)
4045
        $net_saved = is_main_site() ? EE_Network_Config::instance()->update_config(false, false) : true;
4046
        $config_saved = EE_Config::instance()->update_espresso_config(false, false);
4047
        if ($config_saved && $net_saved) {
4048
            EE_Error::add_success(sprintf(__('"%s" have been successfully updated.', 'event_espresso'), $tab));
4049
            return true;
4050
        }
4051
        EE_Error::add_error(sprintf(__('The "%s" were not updated.', 'event_espresso'), $tab), $file, $func, $line);
4052
        return false;
4053
    }
4054
4055
4056
    /**
4057
     * Returns an array to be used for EE_FOrm_Fields.helper.php's select_input as the $values argument.
4058
     *
4059
     * @return array
4060
     */
4061
    public function get_yes_no_values()
4062
    {
4063
        return $this->_yes_no_values;
4064
    }
4065
4066
4067
    /**
4068
     * @return string
4069
     * @throws ReflectionException
4070
     * @since $VID:$
4071
     */
4072
    protected function _get_dir()
4073
    {
4074
        $reflector = new ReflectionClass(get_class($this));
4075
        return dirname($reflector->getFileName());
4076
    }
4077
4078
4079
    /**
4080
     * A helper for getting a "next link".
4081
     *
4082
     * @param string $url   The url to link to
4083
     * @param string $class The class to use.
4084
     * @return string
4085
     */
4086
    protected function _next_link($url, $class = 'dashicons dashicons-arrow-right')
4087
    {
4088
        return '<a class="' . $class . '" href="' . $url . '"></a>';
4089
    }
4090
4091
4092
    /**
4093
     * A helper for getting a "previous link".
4094
     *
4095
     * @param string $url   The url to link to
4096
     * @param string $class The class to use.
4097
     * @return string
4098
     */
4099
    protected function _previous_link($url, $class = 'dashicons dashicons-arrow-left')
4100
    {
4101
        return '<a class="' . $class . '" href="' . $url . '"></a>';
4102
    }
4103
4104
4105
4106
4107
4108
4109
4110
    // below are some messages related methods that should be available across the EE_Admin system.  Note, these methods are NOT page specific
4111
4112
4113
    /**
4114
     * This processes an request to resend a registration and assumes we have a _REG_ID for doing so. So if the caller
4115
     * knows that the _REG_ID isn't in the req_data array but CAN obtain it, the caller should ADD the _REG_ID to the
4116
     * _req_data array.
4117
     *
4118
     * @return bool success/fail
4119
     * @throws EE_Error
4120
     * @throws InvalidArgumentException
4121
     * @throws ReflectionException
4122
     * @throws InvalidDataTypeException
4123
     * @throws InvalidInterfaceException
4124
     */
4125
    protected function _process_resend_registration()
4126
    {
4127
        $this->_template_args['success'] = EED_Messages::process_resend($this->_req_data);
4128
        do_action(
4129
            'AHEE__EE_Admin_Page___process_resend_registration',
4130
            $this->_template_args['success'],
4131
            $this->_req_data
4132
        );
4133
        return $this->_template_args['success'];
4134
    }
4135
4136
4137
    /**
4138
     * This automatically processes any payment message notifications when manual payment has been applied.
4139
     *
4140
     * @param EE_Payment $payment
4141
     * @return bool success/fail
4142
     */
4143
    protected function _process_payment_notification(EE_Payment $payment)
4144
    {
4145
        add_filter('FHEE__EE_Payment_Processor__process_registration_payments__display_notifications', '__return_true');
4146
        do_action('AHEE__EE_Admin_Page___process_admin_payment_notification', $payment);
4147
        $this->_template_args['success'] = apply_filters(
4148
            'FHEE__EE_Admin_Page___process_admin_payment_notification__success',
4149
            false,
4150
            $payment
4151
        );
4152
        return $this->_template_args['success'];
4153
    }
4154
}
4155