Completed
Branch master (408cc8)
by
unknown
60:31 queued 52:11
created

EE_Admin_Page_CPT::getLoader()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
2
3
use EventEspresso\core\exceptions\InvalidDataTypeException;
4
use EventEspresso\core\exceptions\InvalidInterfaceException;
5
use EventEspresso\core\services\loaders\LoaderFactory;
6
use EventEspresso\core\services\loaders\LoaderInterface;
7
use EventEspresso\core\services\request\middleware\RecommendedVersions;
8
9
/**
10
 * EE_Admin_Page_CPT class
11
 * This class is for child classes that utilize core WP CPT views for add/edit pages.  All you have to do is extend
12
 * this class instead of the usual EE_Admin_Page class for your child. Please not the following caveats:
13
 * 1. When using add_meta_box() - it must use $this->wp_page_slug as the screen_id for the page NOT
14
 * $this->_current_screen->id.  This is b/c there is a bug with how WP renders its custom post type pages that doesn't
15
 * accept the default current_screen for metaboxes.
16
 * 2. the same is true for any help_tabs or screen_options you want to add to custom post type views.
17
 * 3. it is EXPECTED that $this->page_slug will be IDENTICAL to what slug/id was used when doing register_post_type().
18
 * So for instance, if you registered a "books" post type then $this->page_slug = 'espresso_books'  would NOT be valid.
19
 * So the correct id for the new post type would be "espresso_books".  Remember, you can still use something totally
20
 * different for front-end rewrite slugs in your configuration array for the register post type.
21
 *
22
 * @package        Event Espresso
23
 * @subpackage     includes/core/admin/EE_Admin_Page.core.php
24
 * @abstract
25
 * @author         Darren Ethier
26
 */
27
abstract class EE_Admin_Page_CPT extends EE_Admin_Page
28
{
29
30
31
    /**
32
     * This gets set in _setup_cpt
33
     * It will contain the object for the custom post type.
34
     *
35
     * @var EE_CPT_Base
36
     */
37
    protected $_cpt_object;
38
39
40
    /**
41
     * a boolean flag to set whether the current route is a cpt route or not.
42
     *
43
     * @var bool
44
     */
45
    protected $_cpt_route = false;
46
47
48
    /**
49
     * This property allows cpt classes to define multiple routes as cpt routes.
50
     * //in this array we define what the custom post type for this route is.
51
     * array(
52
     * 'route_name' => 'custom_post_type_slug'
53
     * )
54
     *
55
     * @var array
56
     */
57
    protected $_cpt_routes = array();
58
59
60
    /**
61
     * This simply defines what the corresponding routes WP will be redirected to after completing a post save/update.
62
     * in this format:
63
     * array(
64
     * 'post_type_slug' => 'edit_route'
65
     * )
66
     *
67
     * @var array
68
     */
69
    protected $_cpt_edit_routes = array();
70
71
72
    /**
73
     * If child classes set the name of their main model via the $_cpt_obj_models property, EE_Admin_Page_CPT will
74
     * attempt to retrieve the related object model for the edit pages and assign it to _cpt_page_object. the
75
     * _cpt_model_names property should be in the following format: array(
76
     * 'route_defined_by_action_param' => 'Model_Name')
77
     *
78
     * @var array $_cpt_model_names
79
     */
80
    protected $_cpt_model_names = array();
81
82
83
    /**
84
     * @var EE_CPT_Base
85
     */
86
    protected $_cpt_model_obj = false;
87
88
    /**
89
     * @var LoaderInterface $loader ;
90
     */
91
    protected $loader;
92
93
    /**
94
     * This will hold an array of autosave containers that will be used to obtain input values and hook into the WP
95
     * autosave so we can save our inputs on the save_post hook!  Children classes should add to this array by using
96
     * the _register_autosave_containers() method so that we don't override any other containers already registered.
97
     * Registration of containers should be done before load_page_dependencies() is run.
98
     *
99
     * @var array()
100
     */
101
    protected $_autosave_containers = array();
102
    protected $_autosave_fields = array();
103
104
    /**
105
     * Array mapping from admin actions to their equivalent wp core pages for custom post types. So when a user visits
106
     * a page for an action, it will appear as if they were visiting the wp core page for that custom post type
107
     *
108
     * @var array
109
     */
110
    protected $_pagenow_map;
111
112
113
    /**
114
     * This is hooked into the WordPress do_action('save_post') hook and runs after the custom post type has been
115
     * saved.  Child classes are required to declare this method.  Typically you would use this to save any additional
116
     * data. Keep in mind also that "save_post" runs on EVERY post update to the database. ALSO very important.  When a
117
     * post transitions from scheduled to published, the save_post action is fired but you will NOT have any _POST data
118
     * containing any extra info you may have from other meta saves.  So MAKE sure that you handle this accordingly.
119
     *
120
     * @access protected
121
     * @abstract
122
     * @param  string      $post_id The ID of the cpt that was saved (so you can link relationally)
123
     * @param  EE_CPT_Base $post    The post object of the cpt that was saved.
124
     * @return void
125
     */
126
    abstract protected function _insert_update_cpt_item($post_id, $post);
127
128
129
    /**
130
     * This is hooked into the WordPress do_action('trashed_post') hook and runs after a cpt has been trashed.
131
     *
132
     * @abstract
133
     * @access public
134
     * @param  string $post_id The ID of the cpt that was trashed
135
     * @return void
136
     */
137
    abstract public function trash_cpt_item($post_id);
138
139
140
    /**
141
     * This is hooked into the WordPress do_action('untrashed_post') hook and runs after a cpt has been untrashed
142
     *
143
     * @param  string $post_id theID of the cpt that was untrashed
144
     * @return void
145
     */
146
    abstract public function restore_cpt_item($post_id);
147
148
149
    /**
150
     * This is hooked into the WordPress do_action('delete_cpt_item') hook and runs after a cpt has been fully deleted
151
     * from the db
152
     *
153
     * @param  string $post_id the ID of the cpt that was deleted
154
     * @return void
155
     */
156
    abstract public function delete_cpt_item($post_id);
157
158
159
    /**
160
     * @return LoaderInterface
161
     * @throws InvalidArgumentException
162
     * @throws InvalidDataTypeException
163
     * @throws InvalidInterfaceException
164
     */
165
    protected function getLoader()
166
    {
167
        if (! $this->loader instanceof LoaderInterface) {
168
            $this->loader = LoaderFactory::getLoader();
169
        }
170
        return $this->loader;
171
    }
172
173
    /**
174
     * Just utilizing the method EE_Admin exposes for doing things before page setup.
175
     *
176
     * @access protected
177
     * @return void
178
     */
179
    protected function _before_page_setup()
180
    {
181
        $page = isset($this->_req_data['page']) ? $this->_req_data['page'] : $this->page_slug;
182
        $this->_cpt_routes = array_merge(
183
            array(
184
                'create_new' => $this->page_slug,
185
                'edit'       => $this->page_slug,
186
                'trash'      => $this->page_slug,
187
            ),
188
            $this->_cpt_routes
189
        );
190
        // let's see if the current route has a value for cpt_object_slug if it does we use that instead of the page
191
        $this->_cpt_object = isset($this->_req_data['action']) && isset($this->_cpt_routes[ $this->_req_data['action'] ])
192
            ? get_post_type_object($this->_cpt_routes[ $this->_req_data['action'] ])
193
            : get_post_type_object($page);
194
        // tweak pagenow for page loading.
195
        if (! $this->_pagenow_map) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_pagenow_map of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
196
            $this->_pagenow_map = array(
197
                'create_new' => 'post-new.php',
198
                'edit'       => 'post.php',
199
                'trash'      => 'post.php',
200
            );
201
        }
202
        add_action('current_screen', array($this, 'modify_pagenow'));
203
        // TODO the below will need to be reworked to account for the cpt routes that are NOT based off of page but action param.
204
        // get current page from autosave
205
        $current_page = isset($this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page'])
206
            ? $this->_req_data['ee_autosave_data']['ee-cpt-hidden-inputs']['current_page']
207
            : null;
208
        $this->_current_page = isset($this->_req_data['current_page'])
209
            ? $this->_req_data['current_page']
210
            : $current_page;
211
        // autosave... make sure its only for the correct page
212
        // if ( ! empty($this->_current_page) && $this->_current_page == $this->page_slug) {
213
        // setup autosave ajax hook
214
        // add_action('wp_ajax_ee-autosave', array( $this, 'do_extra_autosave_stuff' ), 10 ); //TODO reactivate when 4.2 autosave is implemented
215
        // }
216
    }
217
218
219
    /**
220
     * Simply ensure that we simulate the correct post route for cpt screens
221
     *
222
     * @param WP_Screen $current_screen
223
     * @return void
224
     */
225
    public function modify_pagenow($current_screen)
226
    {
227
        global $pagenow, $hook_suffix;
228
        // possibly reset pagenow.
229
        if (! empty($this->_req_data['page'])
230
            && $this->_req_data['page'] == $this->page_slug
231
            && ! empty($this->_req_data['action'])
232
            && isset($this->_pagenow_map[ $this->_req_data['action'] ])
233
        ) {
234
            $pagenow = $this->_pagenow_map[ $this->_req_data['action'] ];
235
            $hook_suffix = $pagenow;
236
        }
237
    }
238
239
240
    /**
241
     * This method is used to register additional autosave containers to the _autosave_containers property.
242
     *
243
     * @todo We should automate this at some point by creating a wrapper for add_post_metabox and in our wrapper we
244
     *       automatically register the id for the post metabox as a container.
245
     * @param  array $ids an array of ids for containers that hold form inputs we want autosave to pickup.  Typically
246
     *                    you would send along the id of a metabox container.
247
     * @return void
248
     */
249
    protected function _register_autosave_containers($ids)
250
    {
251
        $this->_autosave_containers = array_merge($this->_autosave_fields, (array) $ids);
252
    }
253
254
255
    /**
256
     * Something nifty.  We're going to loop through all the registered metaboxes and if the CALLBACK is an instance of
257
     * EE_Admin_Page OR EE_Admin_Hooks, then we'll add the id to our _autosave_containers array.
258
     */
259
    protected function _set_autosave_containers()
260
    {
261
        global $wp_meta_boxes;
262
        $containers = array();
263
        if (empty($wp_meta_boxes)) {
264
            return;
265
        }
266
        $current_metaboxes = isset($wp_meta_boxes[ $this->page_slug ]) ? $wp_meta_boxes[ $this->page_slug ] : array();
267
        foreach ($current_metaboxes as $box_context) {
268
            foreach ($box_context as $box_details) {
269
                foreach ($box_details as $box) {
270
                    if (is_array($box['callback'])
271
                        && (
272
                            $box['callback'][0] instanceof EE_Admin_Page
273
                            || $box['callback'][0] instanceof EE_Admin_Hooks
274
                        )
275
                    ) {
276
                        $containers[] = $box['id'];
277
                    }
278
                }
279
            }
280
        }
281
        $this->_autosave_containers = array_merge($this->_autosave_containers, $containers);
282
        // add hidden inputs container
283
        $this->_autosave_containers[] = 'ee-cpt-hidden-inputs';
284
    }
285
286
287
    protected function _load_autosave_scripts_styles()
288
    {
289
        /*wp_register_script('cpt-autosave', EE_ADMIN_URL . 'assets/ee-cpt-autosave.js', array('ee-serialize-full-array', 'event_editor_js'), EVENT_ESPRESSO_VERSION, TRUE );
290
        wp_enqueue_script('cpt-autosave');/**/ // todo re-enable when we start doing autosave again in 4.2
291
292
        // filter _autosave_containers
293
        $containers = apply_filters(
294
            'FHEE__EE_Admin_Page_CPT___load_autosave_scripts_styles__containers',
295
            $this->_autosave_containers,
296
            $this
297
        );
298
        $containers = apply_filters(
299
            'FHEE__EE_Admin_Page_CPT__' . get_class($this) . '___load_autosave_scripts_styles__containers',
300
            $containers,
301
            $this
302
        );
303
304
        wp_localize_script(
305
            'event_editor_js',
306
            'EE_AUTOSAVE_IDS',
307
            $containers
308
        ); // todo once we enable autosaves, this needs to be switched to localize with "cpt-autosave"
309
310
        $unsaved_data_msg = array(
311
            'eventmsg'     => sprintf(
312
                __(
313
                    "The changes you made to this %s will be lost if you navigate away from this page.",
314
                    'event_espresso'
315
                ),
316
                $this->_cpt_object->labels->singular_name
0 ignored issues
show
Bug introduced by
The property labels does not seem to exist in EE_CPT_Base.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
317
            ),
318
            'inputChanged' => 0,
319
        );
320
        wp_localize_script('event_editor_js', 'UNSAVED_DATA_MSG', $unsaved_data_msg);
321
    }
322
323
324
    public function load_page_dependencies()
325
    {
326
        try {
327
            $this->_load_page_dependencies();
328
        } catch (EE_Error $e) {
329
            $e->get_error();
330
        }
331
    }
332
333
334
    /**
335
     * overloading the EE_Admin_Page parent load_page_dependencies so we can get the cpt stuff added in appropriately
336
     *
337
     * @access protected
338
     * @return void
339
     */
340
    protected function _load_page_dependencies()
341
    {
342
        // we only add stuff if this is a cpt_route!
343
        if (! $this->_cpt_route) {
344
            parent::_load_page_dependencies();
345
            return;
346
        }
347
        // now let's do some automatic filters into the wp_system
348
        // and we'll check to make sure the CHILD class
349
        // automatically has the required methods in place.
350
        // the following filters are for setting all the redirects
351
        // on DEFAULT WP custom post type actions
352
        // let's add a hidden input to the post-edit form
353
        // so we know when we have to trigger our custom redirects!
354
        // Otherwise the redirects will happen on ALL post saves which wouldn't be good of course!
355
        add_action('edit_form_after_title', array($this, 'cpt_post_form_hidden_input'));
356
        // inject our Admin page nav tabs...
357
        // let's make sure the nav tabs are set if they aren't already
358
        // if ( empty( $this->_nav_tabs ) ) $this->_set_nav_tabs();
359
        add_action('post_edit_form_tag', array($this, 'inject_nav_tabs'));
360
        // modify the post_updated messages array
361
        add_action('post_updated_messages', array($this, 'post_update_messages'), 10);
362
        // add shortlink button to cpt edit screens.  We can do this as a universal thing BECAUSE,
363
        // cpts use the same format for shortlinks as posts!
364
        add_filter('pre_get_shortlink', array($this, 'add_shortlink_button_to_editor'), 10, 4);
365
        // This basically allows us to change the title of the "publish" metabox area
366
        // on CPT pages by setting a 'publishbox' value in the $_labels property array in the child class.
367
        if (! empty($this->_labels['publishbox'])) {
368
            $box_label = is_array($this->_labels['publishbox'])
369
                         && isset($this->_labels['publishbox'][ $this->_req_action ])
370
                ? $this->_labels['publishbox'][ $this->_req_action ]
371
                : $this->_labels['publishbox'];
372
            add_meta_box(
373
                'submitdiv',
374
                $box_label,
375
                'post_submit_meta_box',
376
                $this->_cpt_routes[ $this->_req_action ],
377
                'side',
378
                'core'
379
            );
380
        }
381
        // let's add page_templates metabox if this cpt added support for it.
382
        if ($this->_supports_page_templates($this->_cpt_object->name)) {
0 ignored issues
show
Bug introduced by
The property name does not seem to exist in EE_CPT_Base.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
383
            add_meta_box(
384
                'page_templates',
385
                __('Page Template', 'event_espresso'),
386
                array($this, 'page_template_meta_box'),
387
                $this->_cpt_routes[ $this->_req_action ],
388
                'side',
389
                'default'
390
            );
391
        }
392
        // this is a filter that allows the addition of extra html after the permalink field on the wp post edit-form
393
        if (method_exists($this, 'extra_permalink_field_buttons')) {
394
            add_filter('get_sample_permalink_html', array($this, 'extra_permalink_field_buttons'), 10, 4);
395
        }
396
        // add preview button
397
        add_filter('get_sample_permalink_html', array($this, 'preview_button_html'), 5, 4);
398
        // insert our own post_stati dropdown
399
        add_action('post_submitbox_misc_actions', array($this, 'custom_post_stati_dropdown'), 10);
400
        // This allows adding additional information to the publish post submitbox on the wp post edit form
401
        if (method_exists($this, 'extra_misc_actions_publish_box')) {
402
            add_action('post_submitbox_misc_actions', array($this, 'extra_misc_actions_publish_box'), 10);
403
        }
404
        // This allows for adding additional stuff after the title field on the wp post edit form.
405
        // This is also before the wp_editor for post description field.
406
        if (method_exists($this, 'edit_form_after_title')) {
407
            add_action('edit_form_after_title', array($this, 'edit_form_after_title'), 10);
408
        }
409
        /**
410
         * Filtering WP's esc_url to capture urls pointing to core wp routes so they point to our route.
411
         */
412
        add_filter('clean_url', array($this, 'switch_core_wp_urls_with_ours'), 10, 3);
413
        parent::_load_page_dependencies();
414
        // notice we are ALSO going to load the pagenow hook set for this route
415
        // (see _before_page_setup for the reset of the pagenow global ).
416
        // This is for any plugins that are doing things properly
417
        // and hooking into the load page hook for core wp cpt routes.
418
        global $pagenow;
419
        add_action('load-' . $pagenow, array($this, 'modify_current_screen'), 20);
420
        do_action('load-' . $pagenow);
421
        add_action('admin_enqueue_scripts', array($this, 'setup_autosave_hooks'), 30);
422
        // we route REALLY early.
423
        try {
424
            $this->_route_admin_request();
425
        } catch (EE_Error $e) {
426
            $e->get_error();
427
        }
428
    }
429
430
431
    /**
432
     * Since we don't want users going to default core wp routes, this will check any wp urls run through the
433
     * esc_url() method and if we see a url matching a pattern for our routes, we'll modify it to point to OUR
434
     * route instead.
435
     *
436
     * @param string $good_protocol_url The escaped url.
437
     * @param string $original_url      The original url.
438
     * @param string $_context          The context sent to the esc_url method.
439
     * @return string possibly a new url for our route.
440
     */
441
    public function switch_core_wp_urls_with_ours($good_protocol_url, $original_url, $_context)
442
    {
443
        $routes_to_match = array(
444
            0 => array(
445
                'edit.php?post_type=espresso_attendees',
446
                'admin.php?page=espresso_registrations&action=contact_list',
447
            ),
448
            1 => array(
449
                'edit.php?post_type=' . $this->_cpt_object->name,
0 ignored issues
show
Bug introduced by
The property name does not seem to exist in EE_CPT_Base.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
450
                'admin.php?page=' . $this->_cpt_object->name,
451
            ),
452
        );
453
        foreach ($routes_to_match as $route_matches) {
454
            if (strpos($good_protocol_url, $route_matches[0]) !== false) {
455
                return str_replace($route_matches[0], $route_matches[1], $good_protocol_url);
456
            }
457
        }
458
        return $good_protocol_url;
459
    }
460
461
462
    /**
463
     * Determine whether the current cpt supports page templates or not.
464
     *
465
     * @since %VER%
466
     * @param string $cpt_name The cpt slug we're checking on.
467
     * @return bool True supported, false not.
468
     * @throws InvalidArgumentException
469
     * @throws InvalidDataTypeException
470
     * @throws InvalidInterfaceException
471
     */
472
    private function _supports_page_templates($cpt_name)
473
    {
474
        /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
475
        $custom_post_types = $this->getLoader()->getShared(
476
            'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
477
        );
478
        $cpt_args = $custom_post_types->getDefinitions();
479
        $cpt_args = isset($cpt_args[ $cpt_name ]) ? $cpt_args[ $cpt_name ]['args'] : array();
480
        $cpt_has_support = ! empty($cpt_args['page_templates']);
481
482
        // if the installed version of WP is > 4.7 we do some additional checks.
483
        if (RecommendedVersions::compareWordPressVersion('4.7', '>=')) {
484
            $post_templates = wp_get_theme()->get_post_templates();
485
            // if there are $post_templates for this cpt, then we return false for this method because
486
            // that means we aren't going to load our page template manager and leave that up to the native
487
            // cpt template manager.
488
            $cpt_has_support = ! isset($post_templates[ $cpt_name ]) ? $cpt_has_support : false;
489
        }
490
491
        return $cpt_has_support;
492
    }
493
494
495
    /**
496
     * Callback for the page_templates metabox selector.
497
     *
498
     * @since %VER%
499
     * @return void
500
     */
501
    public function page_template_meta_box()
502
    {
503
        global $post;
504
        $template = '';
505
506
        if (RecommendedVersions::compareWordPressVersion('4.7', '>=')) {
507
            $page_template_count = count(get_page_templates());
508
        } else {
509
            $page_template_count = count(get_page_templates($post));
510
        };
511
512
        if ($page_template_count) {
513
            $page_template = get_post_meta($post->ID, '_wp_page_template', true);
514
            $template = ! empty($page_template) ? $page_template : '';
515
        }
516
        ?>
517
        <p><strong><?php _e('Template', 'event_espresso') ?></strong></p>
518
        <label class="screen-reader-text" for="page_template"><?php _e('Page Template', 'event_espresso') ?></label><select
519
        name="page_template" id="page_template">
520
        <option value='default'><?php _e('Default Template', 'event_espresso'); ?></option>
521
        <?php page_template_dropdown($template); ?>
522
    </select>
523
        <?php
524
    }
525
526
527
    /**
528
     * if this post is a draft or scheduled post then we provide a preview button for user to click
529
     * Method is called from parent and is hooked into the wp 'get_sample_permalink_html' filter.
530
     *
531
     * @param  string $return    the current html
532
     * @param  int    $id        the post id for the page
533
     * @param  string $new_title What the title is
534
     * @param  string $new_slug  what the slug is
535
     * @return string            The new html string for the permalink area
536
     */
537
    public function preview_button_html($return, $id, $new_title, $new_slug)
538
    {
539
        $post = get_post($id);
540
        if ('publish' !== get_post_status($post)) {
541
            $return .= '<span_id="view-post-btn"><a target="_blank" href="'
542
                       . get_preview_post_link($id)
543
                       . '" class="button button-small">'
544
                       . __('Preview', 'event_espresso')
545
                       . '</a></span>'
546
                       . "\n";
547
        }
548
        return $return;
549
    }
550
551
552
    /**
553
     * add our custom post stati dropdown on the wp post page for this cpt
554
     *
555
     * @return void
556
     */
557
    public function custom_post_stati_dropdown()
558
    {
559
560
        $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
561
        $cur_status_label = array_key_exists($this->_cpt_model_obj->status(), $statuses)
562
            ? $statuses[ $this->_cpt_model_obj->status() ]
563
            : '';
564
        $template_args = array(
565
            'cur_status'            => $this->_cpt_model_obj->status(),
566
            'statuses'              => $statuses,
567
            'cur_status_label'      => $cur_status_label,
568
            'localized_status_save' => sprintf(__('Save %s', 'event_espresso'), $cur_status_label),
569
        );
570
        // we'll add a trash post status (WP doesn't add one for some reason)
571
        if ($this->_cpt_model_obj->status() === 'trash') {
572
            $template_args['cur_status_label'] = __('Trashed', 'event_espresso');
573
            $statuses['trash'] = __('Trashed', 'event_espresso');
574
            $template_args['statuses'] = $statuses;
575
        }
576
577
        $template = EE_ADMIN_TEMPLATE . 'status_dropdown.template.php';
578
        EEH_Template::display_template($template, $template_args);
579
    }
580
581
582
    public function setup_autosave_hooks()
583
    {
584
        $this->_set_autosave_containers();
585
        $this->_load_autosave_scripts_styles();
586
    }
587
588
589
    /**
590
     * This is run on all WordPress autosaves AFTER the autosave is complete and sends along a $_POST object (available
591
     * in $this->_req_data) containing: post_ID of the saved post autosavenonce for the saved post We'll do the check
592
     * for the nonce in here, but then this method looks for two things:
593
     * 1. Execute a method (if exists) matching 'ee_autosave_' and appended with the given route. OR
594
     * 2. do_actions() for global or class specific actions that have been registered (for plugins/addons not in an
595
     * EE_Admin_Page class. PLEASE NOTE: Data will be returned using the _return_json() object and so the
596
     * $_template_args property should be used to hold the $data array.  We're expecting the following things set in
597
     * template args.
598
     *    1. $template_args['error'] = IF there is an error you can add the message in here.
599
     *    2. $template_args['data']['items'] = an array of items that are setup in key index pairs of 'where_values_go'
600
     *    => 'values_to_add'.  In other words, for the datetime metabox we'll have something like
601
     *    $this->_template_args['data']['items'] = array(
602
     *        'event-datetime-ids' => '1,2,3';
603
     *    );
604
     *    Keep in mind the following things:
605
     *    - "where" index is for the input with the id as that string.
606
     *    - "what" index is what will be used for the value of that input.
607
     *
608
     * @return void
609
     */
610
    public function do_extra_autosave_stuff()
611
    {
612
        // next let's check for the autosave nonce (we'll use _verify_nonce )
613
        $nonce = isset($this->_req_data['autosavenonce'])
614
            ? $this->_req_data['autosavenonce']
615
            : null;
616
        $this->_verify_nonce($nonce, 'autosave');
617
        // make sure we define doing autosave (cause WP isn't triggering this we want to make sure we define it)
618
        if (! defined('DOING_AUTOSAVE')) {
619
            define('DOING_AUTOSAVE', true);
620
        }
621
        // if we made it here then the nonce checked out.  Let's run our methods and actions
622
        $autosave = "_ee_autosave_{$this->_current_view}";
623
        if (method_exists($this, $autosave)) {
624
            $this->$autosave();
625
        } else {
626
            $this->_template_args['success'] = true;
627
        }
628
        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__global_after', $this);
629
        do_action('AHEE__EE_Admin_Page_CPT__do_extra_autosave_stuff__after_' . get_class($this), $this);
630
        // now let's return json
631
        $this->_return_json();
632
    }
633
634
635
    /**
636
     * This takes care of setting up default routes and pages that utilize the core WP admin pages.
637
     * Child classes can override the defaults (in cases for adding metaboxes etc.)
638
     * but take care that you include the defaults here otherwise your core WP admin pages for the cpt won't work!
639
     *
640
     * @access protected
641
     * @throws EE_Error
642
     * @return void
643
     */
644
    protected function _extend_page_config_for_cpt()
645
    {
646
        // before doing anything we need to make sure this runs ONLY when the loaded page matches the set page_slug
647
        if (isset($this->_req_data['page']) && $this->_req_data['page'] !== $this->page_slug) {
648
            return;
649
        }
650
        // set page routes and page config but ONLY if we're not viewing a custom setup cpt route as defined in _cpt_routes
651
        if (! empty($this->_cpt_object)) {
652
            $this->_page_routes = array_merge(
653
                array(
654
                    'create_new' => '_create_new_cpt_item',
655
                    'edit'       => '_edit_cpt_item',
656
                ),
657
                $this->_page_routes
658
            );
659
            $this->_page_config = array_merge(
660
                array(
661
                    'create_new' => array(
662
                        'nav'           => array(
663
                            'label' => $this->_cpt_object->labels->add_new_item,
0 ignored issues
show
Bug introduced by
The property labels does not seem to exist in EE_CPT_Base.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
664
                            'order' => 5,
665
                        ),
666
                        'require_nonce' => false,
667
                    ),
668
                    'edit'       => array(
669
                        'nav'           => array(
670
                            'label'      => $this->_cpt_object->labels->edit_item,
671
                            'order'      => 5,
672
                            'persistent' => false,
673
                            'url'        => '',
674
                        ),
675
                        'require_nonce' => false,
676
                    ),
677
                ),
678
                $this->_page_config
679
            );
680
        }
681
        // load the next section only if this is a matching cpt route as set in the cpt routes array.
682
        if (! isset($this->_cpt_routes[ $this->_req_action ])) {
683
            return;
684
        }
685
        $this->_cpt_route = isset($this->_cpt_routes[ $this->_req_action ]) ? true : false;
686
        // add_action('FHEE__EE_Admin_Page___load_page_dependencies__after_load', array( $this, 'modify_current_screen') );
687
        if (empty($this->_cpt_object)) {
688
            $msg = sprintf(
689
                __(
690
                    'This page has been set as being related to a registered custom post type, however, the custom post type object could not be retrieved. There are two possible reasons for this:  1. The "%s" does not match a registered post type. or 2. The custom post type is not registered for the "%s" action as indexed in the "$_cpt_routes" property on this class (%s).',
691
                    'event_espresso'
692
                ),
693
                $this->page_slug,
694
                $this->_req_action,
695
                get_class($this)
696
            );
697
            throw new EE_Error($msg);
698
        }
699
        if ($this->_cpt_route) {
700
            $id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
701
            $this->_set_model_object($id);
702
        }
703
    }
704
705
706
    /**
707
     * Sets the _cpt_model_object property using what has been set for the _cpt_model_name and a given id.
708
     *
709
     * @access protected
710
     * @param int    $id       The id to retrieve the model object for. If empty we set a default object.
711
     * @param bool   $ignore_route_check
712
     * @param string $req_type whether the current route is for inserting, updating, or deleting the CPT
713
     * @throws EE_Error
714
     * @throws InvalidArgumentException
715
     * @throws InvalidDataTypeException
716
     * @throws InvalidInterfaceException
717
     * @throws ReflectionException
718
     */
719
    protected function _set_model_object($id = null, $ignore_route_check = false, $req_type = '')
720
    {
721
        $model = null;
722
        if (empty($this->_cpt_model_names)
723
            || (
724
                ! $ignore_route_check
725
                && ! isset($this->_cpt_routes[ $this->_req_action ])
726
            ) || (
727
                $this->_cpt_model_obj instanceof EE_CPT_Base
728
                && $this->_cpt_model_obj->ID() === $id
729
            )
730
        ) {
731
            // get out cuz we either don't have a model name OR the object has already been set and it has the same id as what has been sent.
732
            return;
733
        }
734
        // if ignore_route_check is true, then get the model name via CustomPostTypeDefinitions
735
        if ($ignore_route_check) {
736
            $post_type = get_post_type($id);
737
            /** @var EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions $custom_post_types */
738
            $custom_post_types = $this->getLoader()->getShared(
739
                'EventEspresso\core\domain\entities\custom_post_types\CustomPostTypeDefinitions'
740
            );
741
            $model_names = $custom_post_types->getCustomPostTypeModelNames($post_type);
742
            if (isset($model_names[ $post_type ])) {
743
                $model = EE_Registry::instance()->load_model($model_names[ $post_type ]);
744
            }
745
        } else {
746
            $model = EE_Registry::instance()->load_model($this->_cpt_model_names[ $this->_req_action ]);
747
        }
748
        if ($model instanceof EEM_Base) {
749
            $this->_cpt_model_obj = ! empty($id) ? $model->get_one_by_ID($id) : $model->create_default_object();
750
        }
751
        do_action(
752
            'AHEE__EE_Admin_Page_CPT__set_model_object__after_set_object',
753
            $this->_cpt_model_obj,
754
            $req_type
755
        );
756
    }
757
758
759
    /**
760
     * admin_init_global
761
     * This runs all the code that we want executed within the WP admin_init hook.
762
     * This method executes for ALL EE Admin pages.
763
     *
764
     * @access public
765
     * @return void
766
     */
767
    public function admin_init_global()
768
    {
769
        $post = isset($this->_req_data['post']) ? get_post($this->_req_data['post']) : null;
770
        // its possible this is a new save so let's catch that instead
771
        $post = isset($this->_req_data['post_ID']) ? get_post($this->_req_data['post_ID']) : $post;
772
        $post_type = $post ? $post->post_type : false;
773
        $current_route = isset($this->_req_data['current_route'])
774
            ? $this->_req_data['current_route']
775
            : 'shouldneverwork';
776
        $route_to_check = $post_type && isset($this->_cpt_routes[ $current_route ])
777
            ? $this->_cpt_routes[ $current_route ]
778
            : '';
779
        add_filter('get_delete_post_link', array($this, 'modify_delete_post_link'), 10, 3);
780
        add_filter('get_edit_post_link', array($this, 'modify_edit_post_link'), 10, 3);
781
        if ($post_type === $route_to_check) {
782
            add_filter('redirect_post_location', array($this, 'cpt_post_location_redirect'), 10, 2);
783
        }
784
        // now let's filter redirect if we're on a revision page and the revision is for an event CPT.
785
        $revision = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
786
        if (! empty($revision)) {
787
            $action = isset($this->_req_data['action']) ? $this->_req_data['action'] : null;
788
            // doing a restore?
789
            if (! empty($action) && $action === 'restore') {
790
                // get post for revision
791
                $rev_post = get_post($revision);
792
                $rev_parent = get_post($rev_post->post_parent);
793
                // only do our redirect filter AND our restore revision action if the post_type for the parent is one of our cpts.
794
                if ($rev_parent && $rev_parent->post_type === $this->page_slug) {
795
                    add_filter('wp_redirect', array($this, 'revision_redirect'), 10, 2);
796
                    // restores of revisions
797
                    add_action('wp_restore_post_revision', array($this, 'restore_revision'), 10, 2);
798
                }
799
            }
800
        }
801
        // NOTE we ONLY want to run these hooks if we're on the right class for the given post type.  Otherwise we could see some really freaky things happen!
802
        if ($post_type && $post_type === $route_to_check) {
803
            // $post_id, $post
804
            add_action('save_post', array($this, 'insert_update'), 10, 3);
805
            // $post_id
806
            add_action('trashed_post', array($this, 'before_trash_cpt_item'), 10);
807
            add_action('trashed_post', array($this, 'dont_permanently_delete_ee_cpts'), 10);
808
            add_action('untrashed_post', array($this, 'before_restore_cpt_item'), 10);
809
            add_action('after_delete_post', array($this, 'before_delete_cpt_item'), 10);
810
        }
811
    }
812
813
814
    /**
815
     * Callback for the WordPress trashed_post hook.
816
     * Execute some basic checks before calling the trash_cpt_item declared in the child class.
817
     *
818
     * @param int $post_id
819
     * @throws \EE_Error
820
     */
821 View Code Duplication
    public function before_trash_cpt_item($post_id)
822
    {
823
        $this->_set_model_object($post_id, true, 'trash');
824
        // if our cpt object isn't existent then get out immediately.
825
        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
826
            return;
827
        }
828
        $this->trash_cpt_item($post_id);
829
    }
830
831
832
    /**
833
     * Callback for the WordPress untrashed_post hook.
834
     * Execute some basic checks before calling the restore_cpt_method in the child class.
835
     *
836
     * @param $post_id
837
     * @throws \EE_Error
838
     */
839 View Code Duplication
    public function before_restore_cpt_item($post_id)
840
    {
841
        $this->_set_model_object($post_id, true, 'restore');
842
        // if our cpt object isn't existent then get out immediately.
843
        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
844
            return;
845
        }
846
        $this->restore_cpt_item($post_id);
847
    }
848
849
850
    /**
851
     * Callback for the WordPress after_delete_post hook.
852
     * Execute some basic checks before calling the delete_cpt_item method in the child class.
853
     *
854
     * @param $post_id
855
     * @throws \EE_Error
856
     */
857 View Code Duplication
    public function before_delete_cpt_item($post_id)
858
    {
859
        $this->_set_model_object($post_id, true, 'delete');
860
        // if our cpt object isn't existent then get out immediately.
861
        if (! $this->_cpt_model_obj instanceof EE_CPT_Base || $this->_cpt_model_obj->ID() !== $post_id) {
862
            return;
863
        }
864
        $this->delete_cpt_item($post_id);
865
    }
866
867
868
    /**
869
     * This simply verifies if the cpt_model_object is instantiated for the given page and throws an error message
870
     * accordingly.
871
     *
872
     * @access public
873
     * @throws EE_Error
874
     * @return void
875
     */
876
    public function verify_cpt_object()
877
    {
878
        $label = ! empty($this->_cpt_object) ? $this->_cpt_object->labels->singular_name : $this->page_label;
0 ignored issues
show
Bug introduced by
The property labels does not seem to exist in EE_CPT_Base.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
879
        // verify event object
880
        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
881
            throw new EE_Error(
882
                sprintf(
883
                    __(
884
                        'Something has gone wrong with the page load because we are unable to set up the object for the %1$s.  This usually happens when the given id for the page route is NOT for the correct custom post type for this page',
885
                        'event_espresso'
886
                    ),
887
                    $label
888
                )
889
            );
890
        }
891
        // if auto-draft then throw an error
892 View Code Duplication
        if ($this->_cpt_model_obj->get('status') === 'auto-draft') {
893
            EE_Error::overwrite_errors();
894
            EE_Error::add_error(
895
                sprintf(
896
                    __(
897
                        'This %1$s was saved without a title, description, or excerpt which means that none of the extra details you added were saved properly.  All autodrafts will show up in the "draft" view of your event list table.  You can delete them from there. Please click the "Add %1$s" button to refresh and restart.',
898
                        'event_espresso'
899
                    ),
900
                    $label
901
                ),
902
                __FILE__,
903
                __FUNCTION__,
904
                __LINE__
905
            );
906
        }
907
    }
908
909
910
    /**
911
     * admin_footer_scripts_global
912
     * Anything triggered by the 'admin_print_footer_scripts' WP hook should be put in here. This particular method
913
     * will apply on ALL EE_Admin pages.
914
     *
915
     * @access public
916
     * @return void
917
     */
918
    public function admin_footer_scripts_global()
919
    {
920
        $this->_add_admin_page_ajax_loading_img();
921
        $this->_add_admin_page_overlay();
922
    }
923
924
925
    /**
926
     * add in any global scripts for cpt routes
927
     *
928
     * @return void
929
     */
930
    public function load_global_scripts_styles()
931
    {
932
        parent::load_global_scripts_styles();
933
        if ($this->_cpt_model_obj instanceof EE_CPT_Base) {
934
            // setup custom post status object for localize script but only if we've got a cpt object
935
            $statuses = $this->_cpt_model_obj->get_custom_post_statuses();
936
            if (! empty($statuses)) {
937
                // get ALL statuses!
938
                $statuses = $this->_cpt_model_obj->get_all_post_statuses();
939
                // setup object
940
                $ee_cpt_statuses = array();
941
                foreach ($statuses as $status => $label) {
942
                    $ee_cpt_statuses[ $status ] = array(
943
                        'label'      => $label,
944
                        'save_label' => sprintf(__('Save as %s', 'event_espresso'), $label),
945
                    );
946
                }
947
                wp_localize_script('ee_admin_js', 'eeCPTstatuses', $ee_cpt_statuses);
948
            }
949
        }
950
    }
951
952
953
    /**
954
     * This is a wrapper for the insert/update routes for cpt items so we can add things that are common to ALL
955
     * insert/updates
956
     *
957
     * @param  int     $post_id ID of post being updated
958
     * @param  WP_Post $post    Post object from WP
959
     * @param  bool    $update  Whether this is an update or a new save.
960
     * @return void
961
     * @throws \EE_Error
962
     */
963
    public function insert_update($post_id, $post, $update)
964
    {
965
        // make sure that if this is a revision OR trash action that we don't do any updates!
966
        if (isset($this->_req_data['action'])
967
            && (
968
                $this->_req_data['action'] === 'restore'
969
                || $this->_req_data['action'] === 'trash'
970
            )
971
        ) {
972
            return;
973
        }
974
        $this->_set_model_object($post_id, true, 'insert_update');
975
        // if our cpt object is not instantiated and its NOT the same post_id as what is triggering this callback, then exit.
976
        if ($update
977
            && (
978
                ! $this->_cpt_model_obj instanceof EE_CPT_Base
979
                || $this->_cpt_model_obj->ID() !== $post_id
980
            )
981
        ) {
982
            return;
983
        }
984
        // check for autosave and update our req_data property accordingly.
985
        /*if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && isset( $this->_req_data['ee_autosave_data'] ) ) {
986
            foreach( (array) $this->_req_data['ee_autosave_data'] as $id => $values ) {
987
988
                foreach ( (array) $values as $key => $value ) {
989
                    $this->_req_data[$key] = $value;
990
                }
991
            }
992
993
        }/**/ // TODO reactivate after autosave is implemented in 4.2
994
995
        // take care of updating any selected page_template IF this cpt supports it.
996
        if ($this->_supports_page_templates($post->post_type) && ! empty($this->_req_data['page_template'])) {
997
            // wp version aware.
998
            if (RecommendedVersions::compareWordPressVersion('4.7', '>=')) {
999
                $page_templates = wp_get_theme()->get_page_templates();
1000
            } else {
1001
                $post->page_template = $this->_req_data['page_template'];
1002
                $page_templates = wp_get_theme()->get_page_templates($post);
1003
            }
1004
            if ('default' != $this->_req_data['page_template'] && ! isset($page_templates[ $this->_req_data['page_template'] ])) {
1005
                EE_Error::add_error(__('Invalid Page Template.', 'event_espresso'), __FILE__, __FUNCTION__, __LINE__);
1006
            } else {
1007
                update_post_meta($post_id, '_wp_page_template', $this->_req_data['page_template']);
1008
            }
1009
        }
1010
        if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) {
1011
            return;
1012
        } //TODO we'll remove this after reimplementing autosave in 4.2
1013
        $this->_insert_update_cpt_item($post_id, $post);
1014
    }
1015
1016
1017
    /**
1018
     * This hooks into the wp_trash_post() function and removes the `_wp_trash_meta_status` and `_wp_trash_meta_time`
1019
     * post meta IF the trashed post is one of our CPT's - note this method should only be called with our cpt routes
1020
     * so we don't have to check for our CPT.
1021
     *
1022
     * @param  int $post_id ID of the post
1023
     * @return void
1024
     */
1025
    public function dont_permanently_delete_ee_cpts($post_id)
1026
    {
1027
        // only do this if we're actually processing one of our CPTs
1028
        // if our cpt object isn't existent then get out immediately.
1029
        if (! $this->_cpt_model_obj instanceof EE_CPT_Base) {
1030
            return;
1031
        }
1032
        delete_post_meta($post_id, '_wp_trash_meta_status');
1033
        delete_post_meta($post_id, '_wp_trash_meta_time');
1034
        // our cpts may have comments so let's take care of that too
1035
        delete_post_meta($post_id, '_wp_trash_meta_comments_status');
1036
    }
1037
1038
1039
    /**
1040
     * This is a wrapper for the restore_cpt_revision route for cpt items so we can make sure that when a revision is
1041
     * triggered that we restore related items.  In order to work cpt classes MUST have a restore_cpt_revision method
1042
     * in them. We also have our OWN action in here so addons can hook into the restore process easily.
1043
     *
1044
     * @param  int $post_id     ID of cpt item
1045
     * @param  int $revision_id ID of revision being restored
1046
     * @return void
1047
     */
1048
    public function restore_revision($post_id, $revision_id)
1049
    {
1050
        $this->_restore_cpt_item($post_id, $revision_id);
1051
        // global action
1052
        do_action('AHEE_EE_Admin_Page_CPT__restore_revision', $post_id, $revision_id);
1053
        // class specific action so you can limit hooking into a specific page.
1054
        do_action('AHEE_EE_Admin_Page_CPT_' . get_class($this) . '__restore_revision', $post_id, $revision_id);
1055
    }
1056
1057
1058
    /**
1059
     * @see restore_revision() for details
1060
     * @param  int $post_id     ID of cpt item
1061
     * @param  int $revision_id ID of revision for item
1062
     * @return void
1063
     */
1064
    abstract protected function _restore_cpt_item($post_id, $revision_id);
1065
1066
1067
    /**
1068
     * Execution of this method is added to the end of the load_page_dependencies method in the parent
1069
     * so that we can fix a bug where default core metaboxes were not being called in the sidebar.
1070
     * To fix we have to reset the current_screen using the page_slug
1071
     * (which is identical - or should be - to our registered_post_type id.)
1072
     * Also, since the core WP file loads the admin_header.php for WP
1073
     * (and there are a bunch of other things edit-form-advanced.php loads that need to happen really early)
1074
     * we need to load it NOW, hence our _route_admin_request in here. (Otherwise screen options won't be set).
1075
     *
1076
     * @return void
1077
     */
1078
    public function modify_current_screen()
1079
    {
1080
        // ONLY do this if the current page_route IS a cpt route
1081
        if (! $this->_cpt_route) {
1082
            return;
1083
        }
1084
        // routing things REALLY early b/c this is a cpt admin page
1085
        set_current_screen($this->_cpt_routes[ $this->_req_action ]);
1086
        $this->_current_screen = get_current_screen();
1087
        $this->_current_screen->base = 'event-espresso';
1088
        $this->_add_help_tabs(); // we make sure we add any help tabs back in!
1089
        /*try {
1090
            $this->_route_admin_request();
1091
        } catch ( EE_Error $e ) {
1092
            $e->get_error();
1093
        }/**/
1094
    }
1095
1096
1097
    /**
1098
     * This allows child classes to modify the default editor title that appears when people add a new or edit an
1099
     * existing CPT item.     * This uses the _labels property set by the child class via _define_page_props. Just make
1100
     * sure you have a key in _labels property that equals 'editor_title' and the value can be whatever you want the
1101
     * default to be.
1102
     *
1103
     * @param string $title The new title (or existing if there is no editor_title defined)
1104
     * @return string
1105
     */
1106
    public function add_custom_editor_default_title($title)
1107
    {
1108
        return isset($this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ])
1109
            ? $this->_labels['editor_title'][ $this->_cpt_routes[ $this->_req_action ] ]
1110
            : $title;
1111
    }
1112
1113
1114
    /**
1115
     * hooks into the wp_get_shortlink button and makes sure that the shortlink gets generated
1116
     *
1117
     * @param string $shortlink   The already generated shortlink
1118
     * @param int    $id          Post ID for this item
1119
     * @param string $context     The context for the link
1120
     * @param bool   $allow_slugs Whether to allow post slugs in the shortlink.
1121
     * @return string
1122
     */
1123
    public function add_shortlink_button_to_editor($shortlink, $id, $context, $allow_slugs)
1124
    {
1125
        if (! empty($id) && get_option('permalink_structure') !== '') {
1126
            $post = get_post($id);
1127
            if (isset($post->post_type) && $this->page_slug === $post->post_type) {
1128
                $shortlink = home_url('?p=' . $post->ID);
1129
            }
1130
        }
1131
        return $shortlink;
1132
    }
1133
1134
1135
    /**
1136
     * overriding the parent route_admin_request method so we DON'T run the route twice on cpt core page loads (it's
1137
     * already run in modify_current_screen())
1138
     *
1139
     * @return void
1140
     */
1141
    public function route_admin_request()
1142
    {
1143
        if ($this->_cpt_route) {
1144
            return;
1145
        }
1146
        try {
1147
            $this->_route_admin_request();
1148
        } catch (EE_Error $e) {
1149
            $e->get_error();
1150
        }
1151
    }
1152
1153
1154
    /**
1155
     * Add a hidden form input to cpt core pages so that we know to do redirects to our routes on saves
1156
     *
1157
     * @return void
1158
     */
1159
    public function cpt_post_form_hidden_input()
1160
    {
1161
        echo '<input type="hidden" name="ee_cpt_item_redirect_url" value="' . $this->_admin_base_url . '" />';
1162
        // we're also going to add the route value and the current page so we can direct autosave parsing correctly
1163
        echo '<div id="ee-cpt-hidden-inputs">';
1164
        echo '<input type="hidden" id="current_route" name="current_route" value="' . $this->_current_view . '" />';
1165
        echo '<input type="hidden" id="current_page" name="current_page" value="' . $this->page_slug . '" />';
1166
        echo '</div>';
1167
    }
1168
1169
1170
    /**
1171
     * This allows us to redirect the location of revision restores when they happen so it goes to our CPT routes.
1172
     *
1173
     * @param  string $location Original location url
1174
     * @param  int    $status   Status for http header
1175
     * @return string           new (or original) url to redirect to.
1176
     */
1177
    public function revision_redirect($location, $status)
1178
    {
1179
        // get revision
1180
        $rev_id = isset($this->_req_data['revision']) ? $this->_req_data['revision'] : null;
1181
        // can't do anything without revision so let's get out if not present
1182
        if (empty($rev_id)) {
1183
            return $location;
1184
        }
1185
        // get rev_post_data
1186
        $rev = get_post($rev_id);
1187
        $admin_url = $this->_admin_base_url;
1188
        $query_args = array(
1189
            'action'   => 'edit',
1190
            'post'     => $rev->post_parent,
1191
            'revision' => $rev_id,
1192
            'message'  => 5,
1193
        );
1194
        $this->_process_notices($query_args, true);
1195
        return self::add_query_args_and_nonce($query_args, $admin_url);
1196
    }
1197
1198
1199
    /**
1200
     * Modify the edit post link generated by wp core function so that EE CPTs get setup differently.
1201
     *
1202
     * @param  string $link    the original generated link
1203
     * @param  int    $id      post id
1204
     * @param  string $context optional, defaults to display.  How to write the '&'
1205
     * @return string          the link
1206
     */
1207
    public function modify_edit_post_link($link, $id, $context)
1208
    {
1209
        $post = get_post($id);
1210 View Code Duplication
        if (! isset($this->_req_data['action'])
1211
            || ! isset($this->_cpt_routes[ $this->_req_data['action'] ])
1212
            || $post->post_type !== $this->_cpt_routes[ $this->_req_data['action'] ]
1213
        ) {
1214
            return $link;
1215
        }
1216
        $query_args = array(
1217
            'action' => isset($this->_cpt_edit_routes[ $post->post_type ])
1218
                ? $this->_cpt_edit_routes[ $post->post_type ]
1219
                : 'edit',
1220
            'post'   => $id,
1221
        );
1222
        return self::add_query_args_and_nonce($query_args, $this->_admin_base_url);
1223
    }
1224
1225
1226
    /**
1227
     * Modify the trash link on our cpt edit pages so it has the required query var for triggering redirect properly on
1228
     * our routes.
1229
     *
1230
     * @param  string $delete_link  original delete link
1231
     * @param  int    $post_id      id of cpt object
1232
     * @param  bool   $force_delete whether this is forcing a hard delete instead of trash
1233
     * @return string new delete link
1234
     * @throws EE_Error
1235
     */
1236
    public function modify_delete_post_link($delete_link, $post_id, $force_delete)
1237
    {
1238
        $post = get_post($post_id);
1239
1240 View Code Duplication
        if (empty($this->_req_data['action'])
1241
            || ! isset($this->_cpt_routes[ $this->_req_data['action'] ])
1242
            || ! $post instanceof WP_Post
1243
            || $post->post_type !== $this->_cpt_routes[ $this->_req_data['action'] ]
1244
        ) {
1245
            return $delete_link;
1246
        }
1247
        $this->_set_model_object($post->ID, true);
1248
1249
        // returns something like `trash_event` or `trash_attendee` or `trash_venue`
1250
        $action = 'trash_' . str_replace('ee_', '', strtolower(get_class($this->_cpt_model_obj)));
1251
1252
        return EE_Admin_Page::add_query_args_and_nonce(
1253
            array(
1254
                'page'   => $this->_req_data['page'],
1255
                'action' => $action,
1256
                $this->_cpt_model_obj->get_model()->get_primary_key_field()->get_name()
1257
                         => $post->ID,
1258
            ),
1259
            admin_url()
1260
        );
1261
    }
1262
1263
1264
    /**
1265
     * This is the callback for the 'redirect_post_location' filter in wp-admin/post.php
1266
     * so that we can hijack the default redirect locations for wp custom post types
1267
     * that WE'RE using and send back to OUR routes.  This should only be hooked in on the right route.
1268
     *
1269
     * @param  string $location This is the incoming currently set redirect location
1270
     * @param  string $post_id  This is the 'ID' value of the wp_posts table
1271
     * @return string           the new location to redirect to
1272
     */
1273
    public function cpt_post_location_redirect($location, $post_id)
1274
    {
1275
        // we DO have a match so let's setup the url
1276
        // we have to get the post to determine our route
1277
        $post = get_post($post_id);
1278
        $edit_route = $this->_cpt_edit_routes[ $post->post_type ];
1279
        // shared query_args
1280
        $query_args = array('action' => $edit_route, 'post' => $post_id);
1281
        $admin_url = $this->_admin_base_url;
1282
        if (isset($this->_req_data['save']) || isset($this->_req_data['publish'])) {
1283
            $status = get_post_status($post_id);
1284
            if (isset($this->_req_data['publish'])) {
1285
                switch ($status) {
1286
                    case 'pending':
1287
                        $message = 8;
1288
                        break;
1289
                    case 'future':
1290
                        $message = 9;
1291
                        break;
1292
                    default:
1293
                        $message = 6;
1294
                }
1295
            } else {
1296
                $message = 'draft' === $status ? 10 : 1;
1297
            }
1298
        } elseif (isset($this->_req_data['addmeta']) && $this->_req_data['addmeta']) {
1299
            $message = 2;
1300
        } elseif (isset($this->_req_data['deletemeta']) && $this->_req_data['deletemeta']) {
1301
            $message = 3;
1302
        } elseif ($this->_req_data['action'] === 'post-quickpress-save-cont') {
1303
            $message = 7;
1304
        } else {
1305
            $message = 4;
1306
        }
1307
        // change the message if the post type is not viewable on the frontend
1308
        $this->_cpt_object = get_post_type_object($post->post_type);
1309
        $message = $message === 1 && ! $this->_cpt_object->publicly_queryable ? 4 : $message;
1310
        $query_args = array_merge(array('message' => $message), $query_args);
1311
        $this->_process_notices($query_args, true);
1312
        return self::add_query_args_and_nonce($query_args, $admin_url);
1313
    }
1314
1315
1316
    /**
1317
     * This method is called to inject nav tabs on core WP cpt pages
1318
     *
1319
     * @access public
1320
     * @return void
1321
     */
1322
    public function inject_nav_tabs()
1323
    {
1324
        // can we hijack and insert the nav_tabs?
1325
        $nav_tabs = $this->_get_main_nav_tabs();
1326
        // first close off existing form tag
1327
        $html = '>';
1328
        $html .= $nav_tabs;
1329
        // now let's handle the remaining tag ( missing ">" is CORRECT )
1330
        $html .= '<span></span';
1331
        echo $html;
1332
    }
1333
1334
1335
    /**
1336
     * This just sets up the post update messages when an update form is loaded
1337
     *
1338
     * @access public
1339
     * @param  array $messages the original messages array
1340
     * @return array           the new messages array
1341
     */
1342
    public function post_update_messages($messages)
1343
    {
1344
        global $post;
1345
        $id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1346
        $id = empty($id) && is_object($post) ? $post->ID : null;
1347
        /*$current_route = isset($this->_req_data['current_route']) ? $this->_req_data['current_route'] : 'shouldneverwork';
1348
1349
        $route_to_check = $post_type && isset( $this->_cpt_routes[$current_route]) ? $this->_cpt_routes[$current_route] : '';/**/
1350
        $messages[ $post->post_type ] = array(
1351
            0  => '', // Unused. Messages start at index 1.
1352
            1  => sprintf(
1353
                __('%1$s updated. %2$sView %1$s%3$s', 'event_espresso'),
1354
                $this->_cpt_object->labels->singular_name,
0 ignored issues
show
Bug introduced by
The property labels does not seem to exist in EE_CPT_Base.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1355
                '<a href="' . esc_url(get_permalink($id)) . '">',
1356
                '</a>'
1357
            ),
1358
            2  => __('Custom field updated', 'event_espresso'),
1359
            3  => __('Custom field deleted.', 'event_espresso'),
1360
            4  => sprintf(__('%1$s updated.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1361
            5  => isset($_GET['revision']) ? sprintf(
1362
                __('%s restored to revision from %s', 'event_espresso'),
1363
                $this->_cpt_object->labels->singular_name,
1364
                wp_post_revision_title((int) $_GET['revision'], false)
1365
            )
1366
                : false,
1367
            6  => sprintf(
1368
                __('%1$s published. %2$sView %1$s%3$s', 'event_espresso'),
1369
                $this->_cpt_object->labels->singular_name,
1370
                '<a href="' . esc_url(get_permalink($id)) . '">',
1371
                '</a>'
1372
            ),
1373
            7  => sprintf(__('%1$s saved.', 'event_espresso'), $this->_cpt_object->labels->singular_name),
1374
            8  => sprintf(
1375
                __('%1$s submitted. %2$sPreview %1$s%3$s', 'event_espresso'),
1376
                $this->_cpt_object->labels->singular_name,
1377
                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))) . '">',
1378
                '</a>'
1379
            ),
1380
            9  => sprintf(
1381
                __('%1$s scheduled for: %2$s. %3$s">Preview %1$s%3$s', 'event_espresso'),
1382
                $this->_cpt_object->labels->singular_name,
1383
                '<strong>' . date_i18n('M j, Y @ G:i', strtotime($post->post_date)) . '</strong>',
1384
                '<a target="_blank" href="' . esc_url(get_permalink($id)),
1385
                '</a>'
1386
            ),
1387
            10 => sprintf(
1388
                __('%1$s draft updated. %2$s">Preview page%3$s', 'event_espresso'),
1389
                $this->_cpt_object->labels->singular_name,
1390
                '<a target="_blank" href="' . esc_url(add_query_arg('preview', 'true', get_permalink($id))),
1391
                '</a>'
1392
            ),
1393
        );
1394
        return $messages;
1395
    }
1396
1397
1398
    /**
1399
     * default method for the 'create_new' route for cpt admin pages.
1400
     * For reference what to include in here, see wp-admin/post-new.php
1401
     *
1402
     * @access  protected
1403
     * @return void
1404
     */
1405
    protected function _create_new_cpt_item()
1406
    {
1407
        // gather template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1408
        global $post, $title, $is_IE, $post_type, $post_type_object;
1409
        $post_type = $this->_cpt_routes[ $this->_req_action ];
1410
        $post_type_object = $this->_cpt_object;
1411
        $title = $post_type_object->labels->add_new_item;
0 ignored issues
show
Bug introduced by
The property labels does not seem to exist in EE_CPT_Base.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1412
        $post = $post = get_default_post_to_edit($this->_cpt_routes[ $this->_req_action ], true);
1413
        add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1414
        // modify the default editor title field with default title.
1415
        add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1416
        $this->loadEditorTemplate(true);
1417
    }
1418
1419
1420
    /**
1421
     * Enqueues auto-save and loads the editor template
1422
     *
1423
     * @param bool $creating
1424
     */
1425
    private function loadEditorTemplate($creating = true)
1426
    {
1427
        global $post, $title, $is_IE, $post_type, $post_type_object;
1428
        // these vars are used by the template
1429
        $editing = true;
0 ignored issues
show
Unused Code introduced by
$editing is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1430
        $post_ID = $post->ID;
0 ignored issues
show
Unused Code introduced by
$post_ID is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1431
        if (apply_filters('FHEE__EE_Admin_Page_CPT___create_new_cpt_item__replace_editor', false, $post) === false) {
1432
            // only enqueue autosave when creating event (necessary to get permalink/url generated)
1433
            // otherwise EE doesn't support autosave fully, so to prevent user confusion we disable it in edit context.
1434
            if ($creating) {
1435
                wp_enqueue_script('autosave');
1436
            } else {
1437
                if (isset($this->_cpt_routes[ $this->_req_data['action'] ])
1438
                    && ! isset($this->_labels['hide_add_button_on_cpt_route'][ $this->_req_data['action'] ])
1439
                ) {
1440
                    $create_new_action = apply_filters(
1441
                        'FHEE__EE_Admin_Page_CPT___edit_cpt_item__create_new_action',
1442
                        'create_new',
1443
                        $this
1444
                    );
1445
                    $post_new_file = EE_Admin_Page::add_query_args_and_nonce(
0 ignored issues
show
Unused Code introduced by
$post_new_file is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1446
                        array(
1447
                            'action' => $create_new_action,
1448
                            'page'   => $this->page_slug,
1449
                        ),
1450
                        'admin.php'
1451
                    );
1452
                }
1453
            }
1454
            include_once WP_ADMIN_PATH . 'edit-form-advanced.php';
1455
        }
1456
    }
1457
1458
1459
    public function add_new_admin_page_global()
1460
    {
1461
        $admin_page = ! empty($this->_req_data['post']) ? 'post-php' : 'post-new-php';
1462
        ?>
1463
        <script type="text/javascript">
1464
            adminpage = '<?php echo $admin_page; ?>';
1465
        </script>
1466
        <?php
1467
    }
1468
1469
1470
    /**
1471
     * default method for the 'edit' route for cpt admin pages
1472
     * For reference on what to put in here, refer to wp-admin/post.php
1473
     *
1474
     * @access protected
1475
     * @return string   template for edit cpt form
1476
     */
1477
    protected function _edit_cpt_item()
1478
    {
1479
        global $post, $title, $is_IE, $post_type, $post_type_object;
1480
        $post_id = isset($this->_req_data['post']) ? $this->_req_data['post'] : null;
1481
        $post = ! empty($post_id) ? get_post($post_id, OBJECT, 'edit') : null;
1482
        if (empty($post)) {
1483
            wp_die(__('You attempted to edit an item that doesn&#8217;t exist. Perhaps it was deleted?', 'event_espresso'));
1484
        }
1485
        if (! empty($_GET['get-post-lock'])) {
1486
            wp_set_post_lock($post_id);
1487
            wp_redirect(get_edit_post_link($post_id, 'url'));
1488
            exit();
1489
        }
1490
1491
        // template vars for WP_ADMIN_PATH . 'edit-form-advanced.php'
1492
        $post_type = $this->_cpt_routes[ $this->_req_action ];
1493
        $post_type_object = $this->_cpt_object;
1494
1495
        if (! wp_check_post_lock($post->ID)) {
1496
            wp_set_post_lock($post->ID);
1497
        }
1498
        add_action('admin_footer', '_admin_notice_post_locked');
1499
        if (post_type_supports($this->_cpt_routes[ $this->_req_action ], 'comments')) {
1500
            wp_enqueue_script('admin-comments');
1501
            enqueue_comment_hotkeys_js();
1502
        }
1503
        add_action('admin_print_styles', array($this, 'add_new_admin_page_global'));
1504
        // modify the default editor title field with default title.
1505
        add_filter('enter_title_here', array($this, 'add_custom_editor_default_title'), 10);
1506
        $this->loadEditorTemplate(false);
1507
    }
1508
1509
1510
1511
    /**
1512
     * some getters
1513
     */
1514
    /**
1515
     * This returns the protected _cpt_model_obj property
1516
     *
1517
     * @return EE_CPT_Base
1518
     */
1519
    public function get_cpt_model_obj()
1520
    {
1521
        return $this->_cpt_model_obj;
1522
    }
1523
}
1524