WordPress.org

Make WordPress Core

Ticket #18179: meta-box.php

File meta-box.php, 7.8 KB (added by koopersmith, 3 years ago)
Line 
1<?php
2/*
3Plugin Name: WP_Meta_Box
4Plugin URI: http://wordpress.org/
5Description: The WP_Meta_Box class in development for 3.3.
6Version: 0.1
7Author: The WordPress Team
8*/
9
10class WP_Meta_Box {
11        /**
12         * The screen where the meta box will be shown.
13         */
14        public $screen;
15
16        /**
17         * A unique slug to allow multiple meta boxes of the same class
18         * to exist on the same screen.
19         */
20        public $slug;
21
22        /**
23         * A unique identifier for the meta box.
24         *
25         * A string composed from the meta box class, screen, and slug.
26         */
27        public $unique_id;
28
29        /**
30         * Arguments passed to the meta box constructor.
31         */
32        public $args;
33
34        /**
35         * A registry that tracks each meta box instance.
36         */
37        public static $registry;
38
39
40        /**
41         * Constructor.
42         *
43         * Any subclasses MUST call parent::_construct( $screen, $args ).
44         *
45         * @param string $screen - The ID (string) for the screen where the meta box will be shown.
46         * @param array  $args - An array of supplied arguments. Optional.
47         *
48         * $args recognized by default:
49         *    title    - The title of the meta box.
50         *    context  - The context within the page where the box should show ('normal', 'advanced').
51         *    priority - The priority within the context where the boxes should show ('high', 'low').
52         *    slug     - A unique string to identify the meta box.
53         */
54        public function __construct( $screen, $args = array() ) {
55                // Set variables
56                $this->screen = $screen;
57                $this->slug   = empty( $args['slug'] ) ? '' : $args['slug'];
58
59                // Remove slug from args
60                unset( $args['slug'] );
61
62                // Set default args
63                $defaults = array(
64                        'title'         => __( 'Untitled' , 'wordcampbase'),
65                        'context'       => 'side',
66                        'priority'      => 'default',
67                );
68                $this->args = wp_parse_args( $args, $defaults );
69
70                // Add the meta box to the registry.
71                // Generates a slug if one doesn't already exist.
72                WP_Meta_Box::registry()->add( $this );
73
74                // Construct the meta box's unique ID.
75                $this->unique_id = get_class( $this ) . "_{$this->screen}_{$this->slug}";
76
77                // Bind hooks
78                add_action( 'admin_init',            array( $this, '_register' ) );
79                add_action( 'admin_enqueue_scripts', array( $this, '_enqueue_scripts' ) );
80        }
81
82        /**
83         * Fetches the meta box registry.
84         */
85        public static function registry() {
86                if ( ! isset( WP_Meta_Box::$registry ) )
87                        WP_Meta_Box::$registry = new WP_Meta_Box_Registry();
88                return WP_Meta_Box::$registry;
89        }
90
91        /**
92         * Remove the meta box.
93         */
94        public function remove() {
95                // Make sure we've removed any data from the registry.
96                WP_Meta_Box::registry()->remove( get_class( $this ), $this->screen, $this->slug );
97
98                // Unbind actions.
99                remove_action( 'admin_init',            array( $this, '_register' ) );
100                remove_action( 'admin_enqueue_scripts', array( $this, '_enqueue_scripts' ) );
101        }
102
103        /**
104         * Render the meta box. Implement in a subclass.
105         */
106        protected function render() {}
107
108        /**
109         * Save the meta box. Implement in a subclass.
110         */
111        protected function save() {}
112
113        /**
114         * Fires when the meta box is registered. Override in a subclass.
115         */
116        protected function register() {}
117
118        /**
119         * Enqueue meta box scripts. Override in a subclass.
120         */
121        protected function enqueue_scripts() {}
122
123        /**
124         * Determine whether the meta box will be saved. Override in a subclass.
125         *
126         * Return true to save, false to cancel.
127         */
128        protected function maybe_save() {
129                return true;
130        }
131
132        /**
133         * Add an action to trigger save.
134         *
135         * Due to the different types of meta boxes, save will not be triggered by default.
136         */
137        public function add_save_action( $name, $priority=10 ) {
138                add_action( $name, array( $this, '_save' ), $priority, 99 ); // Effectively infinite args.
139        }
140
141        /**
142         * Remove a save action.
143         */
144        public function remove_save_action( $name, $priority=10 ) {
145                remove_action( $name, array( $this, '_save' ), $priority, 99 ); // Effectively infinite args.
146        }
147
148
149        /* =====================================================================
150         * INTERNAL FUNCTIONS
151         * ===================================================================== */
152
153        /**
154         * Internal function. Registers the meta box.
155         */
156        public final function _register() {
157                $id = "{$this->unique_id}-meta-box";
158
159                add_meta_box( $id, $this->args['title'], array( $this, '_render' ),
160                        $this->screen, $this->args['context'], $this->args['priority'],
161                        $this->args );
162
163                $this->register();
164        }
165
166        /**
167         * Internal function. Ensures scripts are only loaded when necessary.
168         */
169        public final function _enqueue_scripts() {
170                $current_screen = get_current_screen();
171
172                if ( isset( $current_screen ) && $this->screen == $current_screen->id )
173                        $this->enqueue_scripts();
174        }
175
176        /**
177         * Internal function, initiates the rendering process.
178         */
179        public final function _render( $object, $box ) {
180                wp_nonce_field( "{$this->unique_id}_nonce", "_wpnonce_{$this->unique_id}", false );
181
182                $this->render();
183        }
184
185        /**
186         * Internal function, initiates the saving process.
187         */
188        public final function _save() {
189                // Nonce check (sorry, you don't have a choice about this one).
190                check_admin_referer( "{$this->unique_id}_nonce", "_wpnonce_{$this->unique_id}" );
191
192                $args = func_get_args();
193
194                // Only save if maybe_save returns true.
195                if ( call_user_func_array( array( $this, 'maybe_save' ), $args ) )
196                        call_user_func_array( array( $this, 'save' ), $args );
197        }
198}
199
200
201
202class WP_Meta_Box_Registry {
203        public $instances = array();
204
205        /**
206         * Adds a meta box instance.
207         *
208         * If $instance->slug is defined, will use $slug.
209         * If a meta box with the same slug exists, it will be overwritten.
210         */
211        public function add( $instance ) {
212                $class = get_class( $instance );
213
214                if ( ! isset( $this->instances[ $class ] ) )
215                        $this->instances[ $class ] = array();
216
217                if ( ! isset( $this->instances[ $class ][ $instance->screen ] ) )
218                        $this->instances[ $class ][ $instance->screen ] = array();
219
220                $screen_instances = $this->instances[ $class ][ $instance->screen ];
221
222                // If no slug is specified, get the numerical index.
223                if ( empty( $instance->slug ) ) {
224                        $this->instances[ $class ][ $instance->screen ][] = $instance;
225
226                        // Get the index of the instance. I feel dirty.
227                        $instance->slug = array_pop( array_keys( $this->instances[ $class ][ $instance->screen ] ) );
228
229                } else {
230                        $this->instances[ $class ][ $instance->screen ][ $instance->slug ] = $instance;
231                }
232        }
233
234        public function get( $class, $screen = false, $slug = false ) {
235                if ( ! isset( $this->instances[ $class ] ) )
236                        return array();
237
238                if ( empty( $screen ) )
239                        return $this->instances[ $class ];
240
241                if ( ! isset( $this->instances[ $class ][ $screen ] ) )
242                        return array();
243
244                $screen_instances = $this->instances[ $class ][ $screen ];
245
246                if ( empty( $slug ) )
247                        return $screen_instances;
248
249                if ( isset( $screen_instances[ $slug ] ) )
250                        return $screen_instances[ $slug ];
251                else
252                        return false;
253        }
254
255        public function remove( $class, $screen = false, $slug = false ) {
256                // Ensure that the instance(s) we want to remove exist.
257                $instances = $this->get( $class, $screen, $slug );
258                if ( empty( $instances ) )
259                        return;
260
261                // Remove the appropriate instance(s) from the registry.
262                if ( empty( $screen ) )
263                        unset( $this->instances[ $class ] );
264                elseif ( empty( $slug ) )
265                        unset( $this->instances[ $class ][ $screen ] );
266                else
267                        unset( $this->instances[ $class ][ $screen ][ $slug ] );
268
269                // Call remove on each instance.
270                if ( ! empty( $slug ) )
271                        $instances = array( $instances );
272
273                foreach ( $instances as $instance ) {
274                        $instance->remove();
275                }
276        }
277}
278
279
280
281class WP_CPT_Meta_Box extends WP_Meta_Box {
282        function __construct( $screen, $args = array() ) {
283                // Call the WP_Meta_Box constructor.
284                parent::__construct( $screen, $args );
285
286                // Save the meta box contents when 'save_post' is triggered.
287                $this->add_save_action( 'save_post' );
288        }
289
290        function maybe_save( $post_id, $post ) {
291                // Bail if we're autosaving
292                if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
293                        return;
294
295                // @TODO Add revision check
296
297                // Cap check
298                if ( ! current_user_can( 'edit_post', $post_id ) )
299                        return;
300
301                return true;
302        }
303}