PluginProbe ʕ •ᴥ•ʔ
Meta Box / 4.18.0
Meta Box v4.18.0
trunk 4.1.10 4.1.11 4.10 4.10.1 4.10.2 4.10.3 4.10.4 4.11 4.11.1 4.11.2 4.12.1 4.12.4 4.12.5 4.12.6 4.13.0 4.13.1 4.13.2 4.13.3 4.13.4 4.14.0 4.14.1 4.14.10 4.14.11 4.14.2 4.14.4 4.14.5 4.14.6 4.14.7 4.14.8 4.14.9 4.15.0 4.15.1 4.15.2 4.15.3 4.15.4 4.15.5 4.15.6 4.15.7 4.15.8 4.15.9 4.16.0 4.16.1 4.16.2 4.16.3 4.17.0 4.17.1 4.17.2 4.17.3 4.18.0 4.18.1 4.18.2 4.18.3 4.18.4 4.2 4.2.1 4.2.2 4.2.3 4.2.4 4.3 4.3.1 4.3.10 4.3.11 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.3.7 4.3.8 4.3.9 4.4.0 4.4.1 4.4.3 4.5 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.5.6 4.5.7 4.6 4.7 4.7.1 4.7.2 4.7.3 4.8.0 4.8.1 4.8.2 4.8.3 4.8.4 4.8.5 4.8.6 4.8.7 4.9 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 4.9.6 4.9.7 4.9.8 5.0.0 5.0.1 5.1.0 5.1.1 5.1.2 5.10.0 5.10.1 5.10.10 5.10.11 5.10.12 5.10.13 5.10.14 5.10.15 5.10.16 5.10.17 5.10.18 5.10.19 5.10.2 5.10.3 5.10.4 5.10.5 5.10.6 5.10.7 5.10.8 5.10.9 5.11.0 5.11.1 5.11.2 5.11.3 5.11.4 5.12.0 5.2.0 5.2.1 5.2.10 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.10 5.3.2 5.3.3 5.3.4 5.3.5 5.3.6 5.3.7 5.3.8 5.3.9 5.4.0 5.4.1 5.4.2 5.4.3 5.4.4 5.4.5 5.4.6 5.4.7 5.4.8 5.5.0 5.5.1 5.6.0 5.6.1 5.6.10 5.6.11 5.6.12 5.6.13 5.6.14 5.6.15 5.6.16 5.6.17 5.6.18 5.6.2 5.6.3 5.6.4 5.6.5 5.6.6 5.6.7 5.6.8 5.6.9 5.7.0 5.7.1 5.7.2 5.7.3 5.7.4 5.7.5 5.8.0 5.8.1 5.8.2 5.9.0 5.9.1 5.9.10 5.9.11 5.9.2 5.9.3 5.9.4 5.9.5 5.9.6 5.9.7 5.9.8 5.9.9
meta-box / inc / meta-box.php
meta-box / inc Last commit date
about 7 years ago fields 7 years ago helpers 7 years ago interfaces 9 years ago storages 7 years ago templates 8 years ago walkers 7 years ago autoloader.php 7 years ago clone.php 7 years ago core.php 8 years ago field-registry.php 8 years ago field.php 7 years ago functions.php 7 years ago loader.php 7 years ago media-modal.php 8 years ago meta-box-registry.php 8 years ago meta-box.php 7 years ago sanitizer.php 7 years ago storage-registry.php 7 years ago validation.php 7 years ago wpml.php 8 years ago
meta-box.php
504 lines
1 <?php
2 /**
3 * A class to rapid develop meta boxes for custom & built in content types
4 * Piggybacks on WordPress
5 *
6 * @author Tran Ngoc Tuan Anh <rilwis@gmail.com>
7 * @license GNU GPL2+
8 * @package Meta Box
9 */
10
11 /**
12 * The main meta box class.
13 *
14 * @property string $id Meta Box ID.
15 * @property string $title Meta Box title.
16 * @property array $fields List of fields.
17 * @property array $post_types List of post types that the meta box is created for.
18 * @property string $style Meta Box style.
19 * @property bool $closed Whether to collapse the meta box when page loads.
20 * @property string $priority The meta box priority.
21 * @property string $context Where the meta box is displayed.
22 * @property bool $default_hidden Whether the meta box is hidden by default.
23 * @property bool $autosave Whether the meta box auto saves.
24 * @property bool $media_modal Add custom fields to media modal when viewing/editing an attachment.
25 *
26 * @package Meta Box
27 */
28 class RW_Meta_Box {
29 /**
30 * Meta box parameters.
31 *
32 * @var array
33 */
34 public $meta_box;
35
36 /**
37 * Detect whether the meta box is saved at least once.
38 * Used to prevent duplicated calls like revisions, manual hook to wp_insert_post, etc.
39 *
40 * @var bool
41 */
42 public $saved = false;
43
44 /**
45 * The object ID.
46 *
47 * @var int
48 */
49 protected $object_id = null;
50
51 /**
52 * The object type.
53 *
54 * @var string
55 */
56 protected $object_type = 'post';
57
58 /**
59 * Create meta box based on given data.
60 *
61 * @param array $meta_box Meta box definition.
62 */
63 public function __construct( $meta_box ) {
64 $meta_box = self::normalize( $meta_box );
65 $this->meta_box = $meta_box;
66
67 $this->meta_box['fields'] = self::normalize_fields( $meta_box['fields'], $this->get_storage() );
68
69 $this->meta_box = apply_filters( 'rwmb_meta_box_settings', $this->meta_box );
70
71 if ( $this->is_shown() ) {
72 $this->global_hooks();
73 $this->object_hooks();
74 }
75 }
76
77 /**
78 * Add fields to field registry.
79 */
80 public function register_fields() {
81 $field_registry = rwmb_get_registry( 'field' );
82
83 foreach ( $this->post_types as $post_type ) {
84 foreach ( $this->fields as $field ) {
85 $field_registry->add( $field, $post_type );
86 }
87 }
88 }
89
90 /**
91 * Conditional check for whether initializing meta box.
92 *
93 * - 1st filter applies to all meta boxes.
94 * - 2nd filter applies to only current meta box.
95 *
96 * @return bool
97 */
98 public function is_shown() {
99 $show = apply_filters( 'rwmb_show', true, $this->meta_box );
100
101 return apply_filters( "rwmb_show_{$this->id}", $show, $this->meta_box );
102 }
103
104 /**
105 * Add global hooks.
106 */
107 protected function global_hooks() {
108 // Enqueue common styles and scripts.
109 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue' ) );
110
111 // Add additional actions for fields.
112 foreach ( $this->fields as $field ) {
113 RWMB_Field::call( $field, 'add_actions' );
114 }
115 }
116
117 /**
118 * Specific hooks for meta box object. Default is 'post'.
119 * This should be extended in sub-classes to support meta fields for terms, user, settings pages, etc.
120 */
121 protected function object_hooks() {
122 // Add meta box.
123 add_action( 'add_meta_boxes', array( $this, 'add_meta_boxes' ) );
124
125 // Hide meta box if it's set 'default_hidden'.
126 add_filter( 'default_hidden_meta_boxes', array( $this, 'hide' ), 10, 2 );
127
128 // Save post meta.
129 foreach ( $this->post_types as $post_type ) {
130 if ( 'attachment' === $post_type ) {
131 // Attachment uses other hooks.
132 // @see wp_update_post(), wp_insert_attachment().
133 add_action( 'edit_attachment', array( $this, 'save_post' ) );
134 add_action( 'add_attachment', array( $this, 'save_post' ) );
135 } else {
136 add_action( "save_post_{$post_type}", array( $this, 'save_post' ) );
137 }
138 }
139 }
140
141 /**
142 * Enqueue common scripts and styles.
143 */
144 public function enqueue() {
145 if ( is_admin() && ! $this->is_edit_screen() ) {
146 return;
147 }
148
149 wp_enqueue_style( 'rwmb', RWMB_CSS_URL . 'style.css', array(), RWMB_VER );
150 if ( is_rtl() ) {
151 wp_enqueue_style( 'rwmb-rtl', RWMB_CSS_URL . 'style-rtl.css', array(), RWMB_VER );
152 }
153
154 // Load clone script conditionally.
155 foreach ( $this->fields as $field ) {
156 if ( $field['clone'] ) {
157 wp_enqueue_script( 'rwmb-clone', RWMB_JS_URL . 'clone.js', array( 'jquery-ui-sortable' ), RWMB_VER, true );
158 break;
159 }
160 }
161
162 // Enqueue scripts and styles for fields.
163 foreach ( $this->fields as $field ) {
164 RWMB_Field::call( $field, 'admin_enqueue_scripts' );
165 }
166
167 // Auto save.
168 if ( $this->autosave ) {
169 wp_enqueue_script( 'rwmb-autosave', RWMB_JS_URL . 'autosave.js', array( 'jquery' ), RWMB_VER, true );
170 }
171
172 /**
173 * Allow developers to enqueue more scripts and styles
174 *
175 * @param RW_Meta_Box $object Meta Box object
176 */
177 do_action( 'rwmb_enqueue_scripts', $this );
178 }
179
180 /**
181 * Add meta box for multiple post types
182 */
183 public function add_meta_boxes() {
184 $screen = get_current_screen();
185 add_filter( "postbox_classes_{$screen->id}_{$this->id}", array( $this, 'postbox_classes' ) );
186
187 foreach ( $this->post_types as $post_type ) {
188 add_meta_box(
189 $this->id,
190 $this->title,
191 array( $this, 'show' ),
192 $post_type,
193 $this->context,
194 $this->priority
195 );
196 }
197 }
198
199 /**
200 * Modify meta box postbox classes.
201 *
202 * @param array $classes Array of classes.
203 * @return array
204 */
205 public function postbox_classes( $classes ) {
206 if ( $this->closed ) {
207 $classes[] = 'closed';
208 }
209 $classes[] = "rwmb-{$this->style}";
210
211 return $classes;
212 }
213
214 /**
215 * Hide meta box if it's set 'default_hidden'
216 *
217 * @param array $hidden Array of default hidden meta boxes.
218 * @param object $screen Current screen information.
219 *
220 * @return array
221 */
222 public function hide( $hidden, $screen ) {
223 if ( $this->is_edit_screen( $screen ) && $this->default_hidden ) {
224 $hidden[] = $this->id;
225 }
226
227 return $hidden;
228 }
229
230 /**
231 * Callback function to show fields in meta box
232 */
233 public function show() {
234 if ( null === $this->object_id ) {
235 $this->set_object_id( $this->get_current_object_id() );
236 }
237 $saved = $this->is_saved();
238
239 // Container.
240 printf(
241 '<div class="rwmb-meta-box" data-autosave="%s" data-object-type="%s" data-object-id="%s">',
242 esc_attr( $this->autosave ? 'true' : 'false' ),
243 esc_attr( $this->object_type ),
244 esc_attr( $this->object_id )
245 );
246
247 wp_nonce_field( "rwmb-save-{$this->id}", "nonce_{$this->id}" );
248
249 // Allow users to add custom code before meta box content.
250 // 1st action applies to all meta boxes.
251 // 2nd action applies to only current meta box.
252 do_action( 'rwmb_before', $this );
253 do_action( "rwmb_before_{$this->id}", $this );
254
255 foreach ( $this->fields as $field ) {
256 RWMB_Field::call( 'show', $field, $saved, $this->object_id );
257 }
258
259 // Allow users to add custom code after meta box content.
260 // 1st action applies to all meta boxes.
261 // 2nd action applies to only current meta box.
262 do_action( 'rwmb_after', $this );
263 do_action( "rwmb_after_{$this->id}", $this );
264
265 // End container.
266 echo '</div>';
267 }
268
269 /**
270 * Save data from meta box
271 *
272 * @param int $object_id Object ID.
273 */
274 public function save_post( $object_id ) {
275 if ( ! $this->validate() ) {
276 return;
277 }
278 $this->saved = true;
279
280 $object_id = $this->get_real_object_id( $object_id );
281 $this->set_object_id( $object_id );
282
283 // Before save action.
284 do_action( 'rwmb_before_save_post', $object_id );
285 do_action( "rwmb_{$this->id}_before_save_post", $object_id );
286
287 array_map( array( $this, 'save_field' ), $this->fields );
288
289 // After save action.
290 do_action( 'rwmb_after_save_post', $object_id );
291 do_action( "rwmb_{$this->id}_after_save_post", $object_id );
292 }
293
294 /**
295 * Save field.
296 *
297 * @param array $field Field settings.
298 */
299 public function save_field( $field ) {
300 $single = $field['clone'] || ! $field['multiple'];
301 $old = RWMB_Field::call( $field, 'raw_meta', $this->object_id );
302 // @codingStandardsIgnoreLine
303 $new = isset( $_POST[ $field['id'] ] ) ? $_POST[ $field['id'] ] : ( $single ? '' : array() );
304
305 // Allow field class change the value.
306 if ( $field['clone'] ) {
307 $new = RWMB_Clone::value( $new, $old, $this->object_id, $field );
308 } else {
309 $new = RWMB_Field::call( $field, 'value', $new, $old, $this->object_id );
310 $new = RWMB_Field::filter( 'sanitize', $new, $field );
311 }
312 $new = RWMB_Field::filter( 'value', $new, $field, $old );
313
314 // Filter to allow the field to be modified.
315 $field = RWMB_Field::filter( 'field', $field, $field, $new, $old );
316
317 // Call defined method to save meta value, if there's no methods, call common one.
318 RWMB_Field::call( $field, 'save', $new, $old, $this->object_id );
319
320 RWMB_Field::filter( 'after_save_field', null, $field, $new, $old, $this->object_id, $field );
321 }
322
323 /**
324 * Validate form when submit. Check:
325 * - If this function is called to prevent duplicated calls like revisions, manual hook to wp_insert_post, etc.
326 * - Autosave
327 * - If form is submitted properly
328 *
329 * @return bool
330 */
331 public function validate() {
332 $nonce = filter_input( INPUT_POST, "nonce_{$this->id}", FILTER_SANITIZE_STRING );
333
334 return ! $this->saved
335 && ( ! defined( 'DOING_AUTOSAVE' ) || $this->autosave )
336 && wp_verify_nonce( $nonce, "rwmb-save-{$this->id}" );
337 }
338
339 /**
340 * Normalize parameters for meta box
341 *
342 * @param array $meta_box Meta box definition.
343 *
344 * @return array $meta_box Normalized meta box.
345 */
346 public static function normalize( $meta_box ) {
347 // Set default values for meta box.
348 $meta_box = wp_parse_args(
349 $meta_box,
350 array(
351 'id' => sanitize_title( $meta_box['title'] ),
352 'context' => 'normal',
353 'priority' => 'high',
354 'post_types' => 'post',
355 'autosave' => false,
356 'default_hidden' => false,
357 'style' => 'default',
358 )
359 );
360
361 /**
362 * Use 'post_types' for better understanding and fallback to 'pages' for previous versions.
363 *
364 * @since 4.4.1
365 */
366 RWMB_Helpers_Array::change_key( $meta_box, 'pages', 'post_types' );
367
368 // Make sure the post type is an array and is sanitized.
369 $meta_box['post_types'] = array_map( 'sanitize_key', RWMB_Helpers_Array::from_csv( $meta_box['post_types'] ) );
370
371 return $meta_box;
372 }
373
374 /**
375 * Normalize an array of fields
376 *
377 * @param array $fields Array of fields.
378 * @param RWMB_Storage_Interface $storage Storage object. Optional.
379 *
380 * @return array $fields Normalized fields.
381 */
382 public static function normalize_fields( $fields, $storage = null ) {
383 foreach ( $fields as $k => $field ) {
384 $field = RWMB_Field::call( 'normalize', $field );
385
386 // Allow to add default values for fields.
387 $field = apply_filters( 'rwmb_normalize_field', $field );
388 $field = apply_filters( "rwmb_normalize_{$field['type']}_field", $field );
389 $field = apply_filters( "rwmb_normalize_{$field['id']}_field", $field );
390
391 $field['storage'] = $storage;
392
393 $fields[ $k ] = $field;
394 }
395
396 return $fields;
397 }
398
399 /**
400 * Check if meta box is saved before.
401 * This helps saving empty value in meta fields (text, check box, etc.) and set the correct default values.
402 *
403 * @return bool
404 */
405 public function is_saved() {
406 foreach ( $this->fields as $field ) {
407 if ( empty( $field['id'] ) ) {
408 continue;
409 }
410
411 $value = RWMB_Field::call( $field, 'raw_meta', $this->object_id );
412 if ( false === $value ) {
413 continue;
414 }
415 if (
416 ( ! $field['multiple'] && '' !== $value )
417 || ( $field['multiple'] && is_array( $value ) && array() !== $value )
418 ) {
419 return true;
420 }
421 }
422
423 return false;
424 }
425
426 /**
427 * Check if we're on the right edit screen.
428 *
429 * @param WP_Screen $screen Screen object. Optional. Use current screen object by default.
430 *
431 * @return bool
432 */
433 public function is_edit_screen( $screen = null ) {
434 if ( ! ( $screen instanceof WP_Screen ) ) {
435 $screen = get_current_screen();
436 }
437
438 return 'post' === $screen->base && in_array( $screen->post_type, $this->post_types, true );
439 }
440
441 /**
442 * Magic function to get meta box property.
443 *
444 * @param string $key Meta box property name.
445 *
446 * @return mixed
447 */
448 public function __get( $key ) {
449 return isset( $this->meta_box[ $key ] ) ? $this->meta_box[ $key ] : false;
450 }
451
452 /**
453 * Set the object ID.
454 *
455 * @param mixed $id Object ID.
456 */
457 public function set_object_id( $id = null ) {
458 $this->object_id = $id;
459 }
460
461 /**
462 * Get object type.
463 *
464 * @return string
465 */
466 public function get_object_type() {
467 return $this->object_type;
468 }
469
470 /**
471 * Get storage object.
472 *
473 * @return RWMB_Storage_Interface
474 */
475 public function get_storage() {
476 return rwmb_get_storage( $this->object_type, $this );
477 }
478
479 /**
480 * Get current object id.
481 *
482 * @return int
483 */
484 protected function get_current_object_id() {
485 return get_the_ID();
486 }
487
488 /**
489 * Get real object ID when submitting.
490 *
491 * @param int $object_id Object ID.
492 * @return int
493 */
494 protected function get_real_object_id( $object_id ) {
495 // Make sure meta is added to the post, not a revision.
496 if ( 'post' !== $this->object_type ) {
497 return $object_id;
498 }
499 $parent = wp_is_post_revision( $object_id );
500
501 return $parent ? $parent : $object_id;
502 }
503 }
504