Make WordPress Core

Ticket #18179: meta-box.php

File meta-box.php, 7.8 KB (added by koopersmith, 15 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}