WordPress.org

Make WordPress Core

Ticket #18179: 18179.php

File 18179.php, 9.7 KB (added by ericlewis, 22 months ago)
Line 
1<?php
2/*
3Plugin Name: Custom Metaboxes for Core
4
5*/
6
7class WP_Meta_Box {
8        /**
9         * The screen where the meta box will be shown.
10         */
11        public $screen;
12
13        /**
14         * A unique slug to allow multiple meta boxes of the same class
15         * to exist on the same screen.
16         */
17        public $slug;
18
19        /**
20         * A unique identifier for the meta box.
21         *
22         * A string composed from the meta box class, screen, and slug.
23         */
24        public $unique_id;
25
26        /**
27         * Arguments passed to the meta box constructor.
28         */
29        public $args;
30
31        /**
32         * A registry that tracks each meta box instance.
33         */
34        public static $registry;
35
36        /**
37         * All registered meta
38         */
39        public $registered_meta;
40
41
42        /**
43         * Constructor.
44         *
45         * Any subclasses MUST call parent::_construct( $screen, $args ).
46         *
47         * @param string $screen - The ID (string) for the screen where the meta box will be shown.
48         * @param array  $args - An array of supplied arguments. Optional.
49         *
50         * $args recognized by default:
51         *    title    - The title of the meta box.
52         *    context  - The context within the page where the box should show ('normal', 'advanced').
53         *    priority - The priority within the context where the boxes should show ('high', 'low').
54         *    slug     - A unique string to identify the meta box.
55         */
56        public function __construct( $screen, $args = array() ) {
57                // Set variables
58                $this->screen = $screen;
59                $this->slug   = empty( $args['slug'] ) ? '' : $args['slug'];
60
61                // Remove slug from args
62                unset( $args['slug'] );
63
64                // Set default args
65                $defaults = array(
66                        'title'         => __( 'Untitled' , 'wordcampbase'),
67                        'context'       => 'side',
68                        'priority'      => 'default',
69                );
70                $this->args = wp_parse_args( $args, $defaults );
71
72                // Add the meta box to the registry.
73                // Generates a slug if one doesn't already exist.
74                WP_Meta_Box::registry()->add( $this );
75
76                // Construct the meta box's unique ID.
77                $this->unique_id = get_class( $this ) . "_{$this->screen}_{$this->slug}";
78
79                // Bind hooks
80                add_action( 'admin_init',            array( $this, '_register' ) );
81                add_action( 'admin_enqueue_scripts', array( $this, '_enqueue_scripts' ) );
82        }
83
84        /**
85         * Fetches the meta box registry.
86         */
87        public static function registry() {
88                if ( ! isset( WP_Meta_Box::$registry ) )
89                        WP_Meta_Box::$registry = new WP_Meta_Box_Registry();
90                return WP_Meta_Box::$registry;
91        }
92
93        /**
94         * Remove the meta box.
95         */
96        public function remove() {
97                // Make sure we've removed any data from the registry.
98                WP_Meta_Box::registry()->remove( get_class( $this ), $this->screen, $this->slug );
99
100                // Unbind actions.
101                remove_action( 'admin_init',            array( $this, '_register' ) );
102                remove_action( 'admin_enqueue_scripts', array( $this, '_enqueue_scripts' ) );
103        }
104
105        /**
106         * Render the meta box. Implement in a subclass.
107         */
108        protected function render() {}
109
110        /**
111         * Save the meta box. Implement in a subclass.
112         */
113        protected function save() {}
114
115        /**
116         * Fires when the meta box is registered. Override in a subclass.
117         */
118        protected function register() {}
119
120        /**
121         * Enqueue meta box scripts. Override in a subclass.
122         */
123        protected function enqueue_scripts() {}
124
125        /**
126         * Determine whether the meta box will be saved. Override in a subclass.
127         *
128         * Return true to save, false to cancel.
129         */
130        protected function maybe_save( $post_id, $post ) {
131                // Bail if we're autosaving
132                if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
133                        return;
134
135                // @TODO Add revision check
136
137                // Cap check
138                if ( ! current_user_can( 'edit_post', $post_id ) )
139                        return;
140
141                return true;
142        }
143
144        /**
145         * Add an action to trigger save.
146         *
147         * Due to the different types of meta boxes, save will not be triggered by default.
148         */
149        public function add_save_action( $name, $priority = 10 ) {
150                add_action( $name, array( $this, '_save' ), $priority, 99 ); // Effectively infinite args.
151        }
152
153        /**
154         * Remove a save action.
155         */
156        public function remove_save_action( $name, $priority = 10 ) {
157                remove_action( $name, array( $this, '_save' ), $priority, 99 ); // Effectively infinite args.
158        }
159
160        public function register_meta( $meta_key, $args_array = array() ) {
161                $defaults = array( 'field_type' => 'text', 'default_display' => true, 'title_label' => $meta_key, 'attrs' => array() );
162
163                $args_array = wp_parse_args($args_array, $defaults);
164               
165                // export array as variables
166                extract($args_array);
167                // var_dump($title_label);
168                $this->registered_meta[] = array( 'meta_key' => $meta_key, 'default_display' => $default_display, 'title_label' => $title_label, 'field_type' => $field_type, 'attrs' => $attrs );
169        }
170
171        /* =====================================================================
172         * INTERNAL FUNCTIONS
173         * ===================================================================== */
174
175        /**
176         * Internal function. Registers the meta box.
177         */
178        public final function _register() {
179                $id = "{$this->unique_id}-meta-box";
180
181                add_meta_box( $id, $this->args['title'], array( $this, '_render' ),
182                        $this->screen, $this->args['context'], $this->args['priority'],
183                        $this->args );
184
185                $this->register();
186        }
187
188        /**
189         * Internal function. Ensures scripts are only loaded when necessary.
190         */
191        public final function _enqueue_scripts() {
192                $current_screen = get_current_screen();
193
194                if ( isset( $current_screen ) && $this->screen == $current_screen->id )
195                        $this->enqueue_scripts();
196        }
197
198        /**
199         * Internal function, initiates the rendering process.
200         */
201        public final function _render( $object, $box ) {
202                wp_nonce_field( "{$this->unique_id}_nonce", "_wpnonce_{$this->unique_id}", false );
203               
204                // Render registered meta boxes with default_display set to true
205                foreach ( $this->registered_meta as $meta )
206                        if ( $meta['default_display'] )
207                                $this->_render_form_field( $meta );
208
209                $this->render();
210        }
211
212        /**
213         * Internal function, initiates the saving process.
214         */
215        public final function _save() {
216                global $post;
217                // Nonce check (sorry, you don't have a choice about this one).
218                check_admin_referer( "{$this->unique_id}_nonce", "_wpnonce_{$this->unique_id}" );
219
220                $args = func_get_args();
221
222                // Only save if maybe_save returns true.
223                if ( call_user_func_array( array( $this, 'maybe_save' ), $args ) ) {
224
225                        // Save registered meta boxes
226                        foreach ( $this->registered_meta as $meta )
227                                if ( $_REQUEST[$meta['meta_key']] )
228                                        update_post_meta( $post->ID, $meta['meta_key'], $_REQUEST[$meta['meta_key']] );
229
230                        call_user_func_array( array( $this, 'save' ), $args );
231                }
232                       
233        }
234
235        public function _render_form_field( $args ) {
236                global $post;
237                extract($args);
238                $value = get_post_meta($post->ID, $meta_key, true );
239
240                echo "<h4>$title_label</h4>";
241                switch( $field_type ) {
242                        case 'text' :
243                                $value = get_post_meta($post->ID, $meta_key, true);
244                                echo "<input type='text' name='$meta_key' value='$value'>";
245                        break;
246                }       
247        }
248
249}
250
251
252
253/**
254 * Meta Box Registry
255 *
256 * Implemented as singleton
257 *
258 */
259class WP_Meta_Box_Registry {
260
261        /**
262         * WP_Meta_Box_Registry singleton implementation
263         *
264         * @var WP_Meta_Box_Registry
265         */
266        private static $instance;
267
268        private $instances = array();
269
270        /**
271         * WP_Meta_Box_Registry singleton implementation
272         *
273         * @return WP_Meta_Box_Registry
274         */
275        public static function instance() {
276                if (null === self::$instance)
277                        self::$instance = new self();
278                return self::$instance;
279        }
280
281        /**
282         * Search registry for meta box instances
283         *
284         * @return array result
285         */
286        public function search($class_or_object, $screen = false, $slug = false) {
287                $instances = array();
288
289                foreach($this->instances as $instance) {
290
291                        if ( ! $instance instanceof $class_or_object )
292                                continue;
293
294                        if ( $screen !== false && $instance->screen !== $screen )
295                                continue;
296
297                        if ( $slug !== false && $instance->slug !== $slug )
298                                continue;
299
300                        $instances[] = $instance;
301                }
302
303                return $instances;
304        }
305
306        /**
307         * Adds a meta box instance.
308         *
309         * If $instance->slug is defined, will use $slug.
310         * If a meta box with the same slug exists, it will be overwritten.
311         *
312         * @param WP_Meta_Box $instance
313         */
314        public function add( WP_Meta_Box $instance ) {
315                static $counter = 0;
316
317                $hash = spl_object_hash( $instance );
318
319                if ( !empty( $instance->slug ) ) {
320                        // If slug is specified, remove existing instance
321                        $instances = $this->find($instance, $instance->screen, $instance->slug);
322
323                        if ($instances) 
324                                $this->remove($instances[0]);
325
326                } else {
327                        // If no slug is specified, get the numerical index.
328                        // alternatively get the number of this instances' class by screen and count plus one.
329                        $instance->slug = ++$counter;
330                }
331
332                $this->instances[ $hash ] = $instance;
333        }
334
335        // in case this is still needed.
336        public function get( WP_Meta_Box $instance ) {
337                $hash = spl_object_hash( $instance );
338
339                if ( isset( $this->instances[ $hash ] ) )
340                        return $instance;
341
342                return false;
343        }
344
345        /**
346         * Remove instance from registry
347         *
348         * @param WP_Meta_Box $instance
349         */
350        public function remove( WP_Meta_Box $instance ) {
351                $hash = spl_object_hash( $instance );
352
353                if ( isset( $this->instances[ $hash ] ) )
354                        unset( $this->instances[ $hash ] );
355        }
356}
357
358class WP_Basic_Meta_Box extends WP_Meta_Box {
359        function __construct( $screen, $args = array() ) {
360                // Call the WP_Meta_Box constructor.
361                parent::__construct( $screen, $args );
362
363                // Save the meta box contents when 'save_post' is triggered.
364                $this->add_save_action( 'save_post' );
365                $this->register_meta( 'test_form_field' );
366        }
367}
368
369$a = new WP_Basic_Meta_Box( 'post', array( 'title' => 'Basic Meta Box' ) );
370
371class WP_Basic_Meta_Box_with_Custom_Render extends WP_Meta_Box {
372        function __construct( $screen, $args = array() ) {
373                // Call the WP_Meta_Box constructor.
374                parent::__construct( $screen, $args );
375
376                // Save the meta box contents when 'save_post' is triggered.
377                $this->add_save_action( 'save_post' );
378                $this->register_meta( 'test_form_field', array( 'default_display' => false, 'title_label' => 'test' ) );
379        }
380
381        function render() {
382                echo 'custom meta box form rendering here';
383        }
384}
385
386$b = new WP_Basic_Meta_Box_with_Custom_Render( 'post', array( 'title' => 'Basic Meta Box with Custom Form rendering' ) );