WordPress.org

Make WordPress Core

Ticket #18179: meta-box.2.php

File meta-box.2.php, 7.7 KB (added by hakre, 3 years ago)

Some example code making the registry a singleton and more fluent.

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         * @var WP_Meta_Box_Registry
38         */
39        private $registry;
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                $this->registry = WP_Meta_Box_Registry::instance();
75                $this->registry->add( $this );
76
77                // Construct the meta box's unique ID.
78                $this->unique_id = get_class( $this ) . "_{$this->screen}_{$this->slug}";
79
80                // Bind hooks
81                add_action( 'admin_init',            array( $this, '_register' ) );
82                add_action( 'admin_enqueue_scripts', array( $this, '_enqueue_scripts' ) );
83        }
84
85        /**
86         * Remove the meta box.
87         */
88        public function remove() {
89                // Make sure we've removed any data from the registry.
90                $this->registry->remove( $this );
91
92                // Unbind actions.
93                remove_action( 'admin_init',            array( $this, '_register' ) );
94                remove_action( 'admin_enqueue_scripts', array( $this, '_enqueue_scripts' ) );
95        }
96
97        /**
98         * Render the meta box. Implement in a subclass.
99         */
100        protected function render() {}
101
102        /**
103         * Save the meta box. Implement in a subclass.
104         */
105        protected function save() {}
106
107        /**
108         * Fires when the meta box is registered. Override in a subclass.
109         */
110        protected function register() {}
111
112        /**
113         * Enqueue meta box scripts. Override in a subclass.
114         */
115        protected function enqueue_scripts() {}
116
117        /**
118         * Determine whether the meta box will be saved. Override in a subclass.
119         *
120         * Return true to save, false to cancel.
121         */
122        protected function maybe_save() {
123                return true;
124        }
125
126        /**
127         * Add an action to trigger save.
128         *
129         * Due to the different types of meta boxes, save will not be triggered by default.
130         */
131        public function add_save_action( $name, $priority=10 ) {
132                add_action( $name, array( $this, '_save' ), $priority, 99 ); // Effectively infinite args.
133        }
134
135        /**
136         * Remove a save action.
137         */
138        public function remove_save_action( $name, $priority=10 ) {
139                remove_action( $name, array( $this, '_save' ), $priority, 99 ); // Effectively infinite args.
140        }
141
142
143        /* =====================================================================
144         * INTERNAL FUNCTIONS
145         * ===================================================================== */
146
147        /**
148         * Internal function. Registers the meta box.
149         */
150        public final function _register() {
151                $id = "{$this->unique_id}-meta-box";
152
153                add_meta_box( $id, $this->args['title'], array( $this, '_render' ),
154                        $this->screen, $this->args['context'], $this->args['priority'],
155                        $this->args );
156
157                $this->register();
158        }
159
160        /**
161         * Internal function. Ensures scripts are only loaded when necessary.
162         */
163        public final function _enqueue_scripts() {
164                $current_screen = get_current_screen();
165
166                if ( isset( $current_screen ) && $this->screen == $current_screen->id )
167                        $this->enqueue_scripts();
168        }
169
170        /**
171         * Internal function, initiates the rendering process.
172         */
173        public final function _render( $object, $box ) {
174                wp_nonce_field( "{$this->unique_id}_nonce", "_wpnonce_{$this->unique_id}", false );
175
176                $this->render();
177        }
178
179        /**
180         * Internal function, initiates the saving process.
181         */
182        public final function _save() {
183                // Nonce check (sorry, you don't have a choice about this one).
184                check_admin_referer( "{$this->unique_id}_nonce", "_wpnonce_{$this->unique_id}" );
185
186                $args = func_get_args();
187
188                // Only save if maybe_save returns true.
189                if ( call_user_func_array( array( $this, 'maybe_save' ), $args ) )
190                        call_user_func_array( array( $this, 'save' ), $args );
191        }
192}
193
194
195/**
196 * Meta Box Registry
197 *
198 * Implemented as singleton
199 *
200 */
201class WP_Meta_Box_Registry {
202
203        /**
204         * WP_Meta_Box_Registry singleton implementation
205         *
206         * @var WP_Meta_Box_Registry
207         */
208        private static $instance;
209
210        private $instances = array();
211
212        /**
213         * WP_Meta_Box_Registry singleton implementation
214         *
215         * @return WP_Meta_Box_Registry
216         */
217        public static function instance() {
218                if (null === self::$instance)
219                        self::$instance = new self();
220                return self::$instance;
221        }
222
223        /**
224         * Search registry for meta box instances
225         *
226         * @return array result
227         */
228        public function search($class_or_object, $screen = false, $slug = false) {
229                $instances = array();
230
231                foreach($this->instances as $instance) {
232
233                        if ( ! $instance instanceof $class_or_object )
234                                continue;
235
236                        if ( $screen !== false && $instance->screen !== $screen )
237                                continue;
238
239                        if ( $slug !== false && $instance->slug !== $slug )
240                                continue;
241
242                        $instances[] = $instance;
243                }
244
245                return $instances;
246        }
247
248        /**
249         * Adds a meta box instance.
250         *
251         * If $instance->slug is defined, will use $slug.
252         * If a meta box with the same slug exists, it will be overwritten.
253         *
254         * @param WP_Meta_Box $instance
255         */
256        public function add( WP_Meta_Box $instance ) {
257                static $counter = 0;
258
259                $hash = spl_object_hash( $instance );
260
261                if ( !empty( $instance->slug ) ) {
262                        // If slug is specified, remove existing instance
263                        $instances = $this->find($instance, $instance->screen, $instance->slug);
264
265                        if ($instances) 
266                                $this->remove($instances[0]);
267
268                } else {
269                        // If no slug is specified, get the numerical index.
270                        // alternatively get the number of this instances' class by screen and count plus one.
271                        $instance->slug = ++$counter;
272                }
273
274                $this->instances[ $hash ] = $instance;
275        }
276
277        // in case this is still needed.
278        public function get( WP_Meta_Box $instance ) {
279                $hash = spl_object_hash( $instance );
280
281                if ( isset( $this->instances[ $hash ] ) )
282                        return $instance;
283
284                return false;
285        }
286
287        /**
288         * Remove instance from registry
289         *
290         * @param WP_Meta_Box $instance
291         */
292        public function remove( WP_Meta_Box $instance ) {
293                $hash = spl_object_hash( $instance );
294
295                if ( isset( $this->instances[ $hash ] ) )
296                        unset( $this->instances[ $hash ] );
297        }
298}
299
300
301
302class WP_CPT_Meta_Box extends WP_Meta_Box {
303        function __construct( $screen, $args = array() ) {
304                // Call the WP_Meta_Box constructor.
305                parent::__construct( $screen, $args );
306
307                // Save the meta box contents when 'save_post' is triggered.
308                $this->add_save_action( 'save_post' );
309        }
310
311        function maybe_save( $post_id, $post ) {
312                // Bail if we're autosaving
313                if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
314                        return;
315
316                // @TODO Add revision check
317
318                // Cap check
319                if ( ! current_user_can( 'edit_post', $post_id ) )
320                        return;
321
322                return true;
323        }
324}