PluginProbe
Custom Metadata Manager / 0.7
Custom Metadata Manager v0.7
trunk 0.1 0.2 0.3 0.5 0.5.1 0.5.2 0.5.3 0.5.4 0.5.5 0.5.6 0.5.7 0.6 0.7 0.7.1 0.8.0
custom-metadata / custom_metadata.php

custom_metadata.php in Custom Metadata Manager 0.7, at custom_metadata.php

1,035 lines 36.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Custom Metadata Manager
4 Plugin URI: http://wordpress.org/extend/plugins/custom-metadata/
5 Description: An easy way to add custom fields to your object types (post, pages, custom post types, users)
6 Author: Mohammad Jangda, Joachim Kudish & Colin Vernon
7 Version: 0.7
8 Author URI: http://digitalize.ca/wordpress-plugins/custom-metadata/
9
10 Copyright 2010-2012 Mohammad Jangda, Joachim Kudish, Colin Vernon
11
12 GNU General Public License, Free Software Foundation <http://creativecommons.org/licenses/GPL/2.0/>
13
14 This program is free software; you can redistribute it and/or modify
15 it under the terms of the GNU General Public License as published by
16 the Free Software Foundation; either version 2 of the License, or
17 (at your option) any later version.
18
19 This program is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with this program; if not, write to the Free Software
26 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27
28 */
29
30 /**
31 * set this to true in your wp-config.php file to enable debug/test mode
32 */
33 if ( ! defined( 'CUSTOM_METADATA_MANAGER_DEBUG' ) )
34 define( 'CUSTOM_METADATA_MANAGER_DEBUG', false );
35
36 if ( CUSTOM_METADATA_MANAGER_DEBUG )
37 include_once( 'custom_metadata_examples.php' );
38
39 /*
40 TODO:
41 - Additional Field types (multi-select, multi-checkboxes, taxonomy checkbox)
42 - Group description field
43 - Multiple display in the same order as saved
44
45 - Limit group based on caps?
46 - Limit view of custom column based on caps?
47 - Limit view and save to specific caps based on object type?
48 -- default for posts: edit_posts
49 -- default for custom posts: check custom post type object
50 -- user: edit_user
51
52 - validation (pass in array of validation types, or string that references function)
53 - quick edit
54 - Links support (?)
55
56 */
57
58 if (!class_exists('custom_metadata_manager')) :
59
60 class custom_metadata_manager {
61
62 var $errors = array();
63 var $metadata = array();
64 var $_non_post_types = array( 'user', 'comment');
65 // Object types that come "built-in" with WordPress
66 var $_builtin_object_types = array( 'post', 'page', 'user', 'comment' );
67 // Column filter names
68 var $_column_types = array( 'posts', 'pages', 'users', 'comments' );
69 // field types
70 var $_field_types = array( 'text', 'textarea', 'password', 'checkbox', 'radio', 'select', 'upload', 'wysiwyg', 'datepicker', 'taxonomy_select', 'taxonomy_radio' );
71 // field types that are cloneable
72 var $_cloneable_field_types = array( 'text', 'textarea', 'upload', 'password');
73 // Object types whose columns are generated through apply_filters instead of do_action
74 var $_column_filter_object_types = array( 'user' );
75 // Whitelisted pages that get stylesheets and scripts
76 var $_pages_whitelist = array( 'edit.php', 'post.php', 'post-new.php', 'users.php', 'profile.php', 'user-edit.php', 'edit-comments.php', 'comment.php');
77 // the default args used for the wp_editor function
78 var $default_editor_args = array();
79
80
81 function __construct( ) {
82
83 // filter our vars
84 $this->_non_post_types = apply_filters( 'custom_metadata_manager_non_post_types', $this->_non_post_types );
85 $this->_builtin_object_types = apply_filters( 'custom_metadata_manager_builtin_object_types', $this->_builtin_object_types );
86 $this->_column_types = apply_filters( 'custom_metadata_manager_column_types', $this->_column_types);
87 $this->_field_types = apply_filters( 'custom_metadata_manager_field_types', $this->_field_types);
88 $this->_cloneable_field_types = apply_filters( 'custom_metadata_manager_cloneable_field_types', $this->_cloneable_field_types);
89 $this->_column_filter_object_types = apply_filters( 'custom_metadata_manager_column_filter_object_types', $this->_column_filter_object_types);
90 $this->_pages_whitelist = apply_filters( 'custom_metadata_manager_pages_whitelist', $this->_pages_whitelist);
91 $this->default_editor_args = apply_filters( 'custom_metadata_manager_default_editor_args', $this->default_editor_args );
92
93
94 // We need to run these as late as possible!
95 add_action( 'init', array( &$this, 'init' ), 1000, 0 );
96 add_action( 'admin_init', array( &$this, 'admin_init' ), 1000, 0 );
97 }
98
99 function init() {
100 $this->init_object_types();
101 }
102
103 function admin_init() {
104 global $pagenow;
105
106 define( 'CUSTOM_METADATA_MANAGER_VERSION', '0.7' );
107 define( 'CUSTOM_METADATA_MANAGER_URL' , apply_filters('custom_metadata_manager_url', trailingslashit(plugins_url('', __FILE__))) );
108
109 // Hook into load to initialize custom columns
110 if( in_array( $pagenow, $this->_pages_whitelist ) ) {
111 add_action( 'load-' . $pagenow, array( &$this, 'init_metadata' ) );
112 }
113
114 // Hook into admin_notices to show errors
115 if( current_user_can( 'manage_options' ) )
116 add_action( 'admin_notices', array( &$this, '_display_registration_errors' ) );
117
118 }
119
120 function init_object_types() {
121 foreach( array_merge( get_post_types(), $this->_builtin_object_types ) as $object_type )
122 $this->metadata[$object_type] = array();
123 }
124
125 function init_metadata() {
126 $object_type = $this->_get_object_type_context();
127
128 add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_scripts' ) );
129 add_action( 'admin_enqueue_scripts', array( &$this, 'enqueue_styles' ) );
130
131 $this->init_columns();
132
133 // Handle actions related to users
134 if( $object_type == 'user' ) {
135 // Editing another user's profile
136 add_action( 'edit_user_profile', array( &$this, 'add_user_metadata_groups' ) );
137 add_action( 'edit_user_profile_update', array( &$this, 'save_user_metadata' ) );
138 // Allow user-editable fields on "Your Profile"
139 add_action( 'show_user_profile', array( &$this, 'add_user_metadata_groups' ) );
140 add_action( 'personal_options_update', array( &$this, 'save_user_metadata' ) );
141
142 } else {
143
144 // Hook in to metaboxes
145 add_action( 'add_meta_boxes', array( &$this, "add_post_metadata_groups" ) );
146
147 // Hook in to save
148 add_action( 'save_post', array( &$this, 'save_post_metadata' ) );
149 add_action( 'edit_comment', array( &$this, 'save_comment_metadata' ) );
150 }
151 }
152
153 function init_columns() {
154
155 $object_type = $this->_get_object_type_context();
156
157 // This is not really that clean, but it works. Damn inconsistencies!
158 if( post_type_exists( $object_type ) ) {
159 $column_header_name = sprintf( '%s_posts', $object_type );
160 $column_content_name = ( 'page' != $object_type ) ? 'posts' : 'pages';
161 } elseif( $object_type == 'comment' ) {
162 $column_header_name = 'edit-comments';
163 $column_content_name = 'comments';
164 } else {
165 // users
166 $column_header_name = $column_content_name = $object_type . 's';
167 }
168
169 // Hook into Column Headers
170 add_filter( "manage_{$column_header_name}_columns", array( &$this, 'add_metadata_column_headers' ) );
171
172 // User and Posts have different functions
173 $custom_column_content_function = array( &$this, "add_{$object_type}_metadata_column_content" );
174 if( ! is_callable( $custom_column_content_function ) )
175 $custom_column_content_function = array( &$this, 'add_metadata_column_content' );
176
177 // Hook into Column Content. Users get filtered, others get actioned.
178 if( ! in_array( $object_type, $this->_column_filter_object_types ) )
179 add_action( "manage_{$column_content_name}_custom_column", $custom_column_content_function, 10, 3 );
180 else
181 add_filter( "manage_{$column_content_name}_custom_column", $custom_column_content_function, 10, 3 );
182
183 }
184
185 function enqueue_scripts() {
186 wp_enqueue_script('jquery');
187 wp_enqueue_script('jquery-ui-datepicker');
188 wp_enqueue_script('custom-metadata-manager-js', apply_filters( 'custom-metadata-manager-default-js', CUSTOM_METADATA_MANAGER_URL .'js/custom-metadata-manager.js' ), array( 'jquery' ), CUSTOM_METADATA_MANAGER_VERSION, true);
189 // wp_enqueue_script('jquery-ui-datepicker', apply_filters('custom-metadata-manager-datepicker-js', CUSTOM_METADATA_MANAGER_URL .'js/jquery-ui-datepicker.min.js'), array('jquery', 'jquery-ui-core'));
190 }
191
192 function enqueue_styles() {
193 wp_enqueue_style( 'custom-metadata-manager-css', apply_filters( 'custom-metadata-manager-default-css', CUSTOM_METADATA_MANAGER_URL .'css/custom-metadata-manager.css' ), array(), CUSTOM_METADATA_MANAGER_VERSION );
194 wp_enqueue_style( 'jquery-ui-css', apply_filters( 'custom-metadata-manager-jquery-ui-css', CUSTOM_METADATA_MANAGER_URL .'css/jquery-ui-smoothness.css' ), array(), CUSTOM_METADATA_MANAGER_VERSION );
195 }
196
197 function add_metadata_column_headers( $columns ) {
198
199 $object_type = $this->_get_object_type_context();
200
201 if( $object_type ) {
202 $fields = $this->get_fields_in_object_type( $object_type );
203
204 foreach( $fields as $field_slug => $field ) {
205 if( $this->is_field_addable_to_columns( $field_slug, $field ) ) {
206 $columns[$field_slug] = is_string( $field->display_column ) ? $field->display_column : $field->label;
207 }
208 }
209 }
210 return $columns;
211 }
212
213 function add_user_metadata_column_content( $param, $name, $object_id ) {
214 return $this->add_metadata_column_content( $name, $object_id );
215 }
216
217 function add_metadata_column_content( $name, $object_id ) {
218
219 $object_type = $this->_get_object_type_context();
220 $field_slug = $name;
221
222 $column_content = '';
223
224 if( $this->is_registered_object_type( $object_type ) && $this->is_registered_field( $field_slug, null, $object_type ) ) {
225 $field = $this->get_field( $field_slug, null, $object_type );
226 $column_content = $this->_metadata_column_content( $field_slug, $field, $object_type, $object_id );
227 }
228
229 if( $column_content && ! in_array( $object_type, $this->_column_filter_object_types ) )
230 echo $column_content;
231 else
232 return $column_content;
233 }
234
235 function add_metadata_field( $field_slug, $object_types = array( 'post' ), $args = array() ) {
236
237 $defaults = array(
238 'group' => '', // To which meta_box the field should be added
239 'field_type' => 'text', // The type of field; possibly values: text, checkbox, radio, select, image
240 'label' => $field_slug, // Label for the field
241 'description' => '', // Description of the field, displayed below the input
242 'values' => array(), // values for select, checkbox, radio buttons
243 'display_callback' => '', // function to custom render the input
244 'sanitize_callback' => '',
245 'display_column' => false, // Add the field to the columns when viewing all posts
246 'display_column_callback' => '',
247 'add_to_quick_edit' => false, // (post only) Add the field to Quick edit
248 'required_cap' => '', // the cap required to view and edit the field
249 'multiple' => false, // can the field be duplicated with a click of a button
250 'readonly' => false, // makes the field be readonly
251 );
252
253 // Merge defaults with args
254 $field = wp_parse_args( $args, $defaults );
255 $field = (object) $field;
256
257 // Sanitize slug
258 $field_slug = sanitize_key( $field_slug );
259 $group_slug = sanitize_key( $field->group );
260
261 // Check to see if the user should see this field
262 if( $field->required_cap && ! current_user_can( $field->required_cap ) )
263 return;
264
265 if( ! $this->_validate_metadata_field( $field_slug, $field, $group_slug, $object_types ) )
266 return;
267
268 // Add to group
269 $this->add_field_to_group( $field_slug, $field, $group_slug, $object_types );
270
271 }
272
273 function add_metadata_group( $group_slug, $object_types, $args = array() ) {
274
275 $defaults = array(
276 'label' => $group_slug, // Label for the group
277 'description' => '', // Description of the group
278 'context' => 'normal', // (post only)
279 'priority' => 'default', // (post only)
280 'autosave' => false, // (post only) Should the group be saved in autosave?
281 );
282
283 // Merge defaults with args
284 $group = wp_parse_args( $args, $defaults );
285 $group = (object) $group;
286
287 // Sanitize slug
288 $group_slug = sanitize_key( $group_slug );
289
290 if( !$this->_validate_metadata_group( $group_slug, $group, $object_types ) )
291 return;
292
293 $this->add_group_to_object_type( $group_slug, $group, $object_types );
294 }
295
296
297 function add_field_to_group( $field_slug, $field, $group_slug, $object_types ) {
298 $object_types = (array) $object_types;
299
300 foreach( $object_types as $object_type ) {
301 if( ! $group_slug ) {
302 $group_slug = sprintf( 'single-group-%1$s-%2$s', $object_type, $field_slug );
303 }
304
305 // If group doesn't exist, create group
306 if( ! $this->is_registered_group( $group_slug, $object_type ) ) {
307 $this->add_metadata_group( $group_slug, $object_type, array( 'label' => ( ! empty( $field->label ) ) ? $field->label : $field_slug ) );
308 $field->group = $group_slug;
309 }
310
311 $this->_push_field( $field_slug, $field, $group_slug, $object_type );
312 }
313 }
314
315 function add_group_to_object_type( $group_slug, $group, $object_types ) {
316 $object_types = (array) $object_types;
317
318 foreach( $object_types as $object_type ) {
319 if( ($this->is_registered_object_type( $object_type ) && ! $this->is_group_in_object_type( $group_slug, $object_type )) ) {
320 $group->fields = array();
321 $this->_push_group( $group_slug, $group, $object_type );
322 }
323 }
324 }
325
326 function _validate_metadata_group( $group_slug, $group, $object_type ) {
327 $valid = true;
328
329 // TODO: only validate when DEBUG is on?
330 /*
331 if( ! $group_slug ) {
332
333 } elseif ( $this->is_registered_group( $group_slug, $object_type ) ) {
334 // TODO: check that it hasn't been registered already
335 } elseif ( $this->is_restricted_group( $group_slug, $object_type ) ) {
336 // TODO: check that it isn't restricted
337 }
338 */
339 return $valid;
340 }
341
342 function _validate_metadata_field( $field_slug, $field, $group_slug, $object_types ) {
343
344 // TODO: only validate when DEBUG is on?
345
346 $valid = true;
347 /*
348 if( !$field_slug ) {
349 // Check that
350 $this->_add_registration_error( $field_slug, __( 'You entered an empty slug name for this field!', 'custom-metadata-manager' ) );
351 $valid = false;
352 } else if( $this->is_registered_field( $field_slug, $group_slug, $object_type ) ) {
353 // does field name already exists
354 $this->_add_registration_error( $field_slug, __( 'This field already exists. Check to see that you\'re not registering the field twice, or use a different slug.', 'custom-metadata-manager' ) );
355 $valid = false;
356 } else if( $this->is_restricted_field( $field_slug, $object_type ) ) {
357 // is field restricted
358 $this->_add_registration_error( $field_slug, __( 'This field is restricted. Please use a different slug.', 'custom-metadata-manager' ) );
359 $valid = false;
360 }
361 // if display_callback not defined
362 // show admin_notices error
363 // show as text field (?)
364
365 */
366 // TODO: valid object_type?
367
368 return $valid;
369 }
370
371 function _add_registration_error( $field_slug, $error_message ) {
372 $this->errors[] = sprintf( __( '<strong>%1$s:</strong> %2$s', 'custom-metadata-manager' ), $field_slug, $error_message );
373 }
374
375 function add_post_metadata_groups() {
376 global $post, $comment;
377
378 $object_id = 0;
379
380 if( isset( $post ) ) {
381 $object_id = $post->ID;
382 } elseif( isset( $comment ) ) {
383 $object_id = $comment->comment_ID;
384 }
385 $object_type = $this->_get_object_type_context();
386
387 $groups = $this->get_groups_in_object_type( $object_type );
388
389 if( $object_id && !empty( $groups ) ) {
390 foreach( $groups as $group_slug => $group ) {
391 $this->add_post_metadata_group( $group_slug, $group, $object_type, $object_id );
392 }
393 }
394 }
395
396 function add_post_metadata_group( $group_slug, $group, $object_type, $object_id ) {
397
398 $fields = $this->get_fields_in_group( $group_slug, $object_type );
399
400 if( ! empty( $fields ) && $this->is_thing_added_to_object( $group_slug, $group, $object_type, $object_id ) ) {
401 add_meta_box( $group_slug, $group->label, array( &$this, '_display_post_metadata_box' ), $object_type, $group->context, $group->priority, array( 'group' => $group, 'fields' => $fields));
402 }
403 }
404
405 function add_user_metadata_groups() {
406 global $user_id;
407
408 if( !$user_id ) return;
409
410 $object_type = 'user';
411
412 $groups = $this->get_groups_in_object_type( $object_type );
413
414 if( !empty( $groups ) ) {
415 foreach( $groups as $group_slug => $group ) {
416 $this->add_user_metadata_group( $group_slug, $group, $object_type, $user_id );
417 }
418 }
419 }
420
421 function add_user_metadata_group( $group_slug, $group, $object_type, $user_id ) {
422 $fields = $this->get_fields_in_group( $group_slug, $object_type );
423
424 if( ! empty( $fields ) && $this->is_thing_added_to_object( $group_slug, $group, $object_type, $user_id ) )
425 $this->_display_user_metadata_box( $group_slug, $group, $object_type, $fields );
426 }
427
428
429 function _display_user_metadata_box( $group_slug, $group, $object_type, $fields ) {
430 global $user_id;
431 ?>
432 <h3><?php echo $group->label; ?></h3>
433
434 <table class="form-table user-metadata-group">
435 <?php foreach( $fields as $field_slug => $field ) : ?>
436 <?php if( $this->is_thing_added_to_object( $field_slug, $field, $object_type, $user_id ) ) : ?>
437 <tr valign="top">
438 <td scope="row">
439 <?php $this->_display_metadata_field( $field_slug, $field, $object_type, $user_id ); ?>
440 </td>
441 </tr>
442 <?php endif; ?>
443 <?php endforeach; ?>
444 </table>
445 <?php
446
447 $this->_display_group_nonce( $group_slug, $object_type );
448 }
449
450 function _display_post_metadata_box( $object, $meta_box ) {
451
452 $group_slug = $meta_box['id'];
453 $group = $meta_box['args']['group'];
454 $fields = $meta_box['args']['fields'];
455 $object_type = $this->_get_object_type_context();
456
457 // I really don't like using variable variables, but this is the path of least resistence.
458 if( isset( $object->{$object_type . '_ID'} ) ) {
459 $object_id = $object->{$object_type . '_ID'};
460 } elseif ( isset( $object->ID ) ) {
461 $object_id = $object->ID;
462 } else {
463 _e( 'Uh oh, something went wrong!', 'custom-metadata-manager' );
464 return;
465 }
466
467 foreach( $fields as $field_slug => $field ) {
468 if( $this->is_thing_added_to_object( $field_slug, $field, $object_type, $object_id ) ) {
469 $this->_display_metadata_field( $field_slug, $field, $object_type, $object_id );
470 }
471 }
472
473 // Each group gets its own nonce
474 $this->_display_group_nonce( $group_slug, $object_type );
475 }
476
477 function _display_group_nonce( $group_slug, $object_type ) {
478 $nonce_key = $this->build_nonce_key( $group_slug, $object_type );
479 wp_nonce_field( 'save-metadata', $nonce_key, false );
480 }
481
482 function verify_group_nonce( $group_slug, $object_type ) {
483 $nonce_key = $this->build_nonce_key( $group_slug, $object_type );
484 if( isset( $_POST[$nonce_key] ) )
485 return wp_verify_nonce( $_POST[$nonce_key], 'save-metadata' );
486 else
487 return false;
488 }
489
490 function build_nonce_key( $group_slug, $object_type ) {
491 return sprintf( 'metadata-%1$s-%2$s', $object_type, $group_slug );
492 }
493
494 function save_user_metadata( $user_id ) {
495 $object_type = 'user';
496 $groups = $this->get_groups_in_object_type( $object_type );
497
498 foreach( $groups as $group_slug => $group ) {
499 $this->save_metadata_group( $group_slug, $group, $object_type, $user_id );
500 }
501 }
502
503 function save_post_metadata( $post_id ) {
504 $post_type = $this->_get_object_type_context();
505 $groups = $this->get_groups_in_object_type( $post_type );
506
507 foreach( $groups as $group_slug => $group ) {
508 // TODO: Allow hook into autosave
509 if ( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE && !$group->autosave )
510 return $post_id;
511
512 $this->save_metadata_group( $group_slug, $group, $post_type, $post_id );
513 }
514 }
515
516 function save_comment_metadata( $comment_id ) {
517 $object_type = 'comment';
518 $groups = $this->get_groups_in_object_type( $object_type );
519
520 foreach( $groups as $group_slug => $group ) {
521 $this->save_metadata_group( $group_slug, $group, $object_type, $comment_id );
522 }
523 }
524
525 function save_metadata_group( $group_slug, $group, $object_type, $object_id ) {
526 if ( !$this->verify_group_nonce( $group_slug, $object_type ) ) {
527 return $object_id;
528 }
529
530 $fields = $this->get_fields_in_group( $group_slug, $object_type );
531
532 foreach( $fields as $field_slug => $field ) {
533 $this->save_metadata_field( $field_slug, $field, $object_type, $object_id );
534 }
535 }
536
537 function save_metadata_field( $field_slug, $field, $object_type, $object_id ) {
538 if( isset( $_POST[$field_slug] ) ) {
539 $value = $this->_sanitize_field_value( $field_slug, $field, $object_type, $object_id, $_POST[$field_slug] );
540 $this->_save_field_value( $field_slug, $field, $object_type, $object_id, $value );
541 } else {
542 $this->_delete_field_value( $field_slug, $field, $object_type, $object_id );
543 }
544 }
545
546 function get_metadata_field_value( $field_slug, $field, $object_type, $object_id ) {
547 return $this->_get_field_value( $field_slug, $field, $object_type, $object_id );
548 }
549
550 function is_registered_object_type( $object_type ) {
551 return array_key_exists( $object_type, $this->metadata ) /*&& is_array( $this->metadata[$object_type] )*/;
552 }
553
554 function is_registered_group( $group_slug, $object_type ) {
555 return $this->is_registered_object_type( $object_type ) && array_key_exists( $group_slug, $this->get_groups_in_object_type( $object_type ) );
556 }
557
558 function is_registered_field( $field_slug, $group_slug = '', $object_type ) {
559 if( $group_slug )
560 return $this->is_registered_group( $group_slug, $object_type ) && array_key_exists( $field_slug, $this->get_fields_in_group( $group_slug, $object_type ) );
561 else
562 return array_key_exists( $field_slug, $this->get_fields_in_object_type( $object_type ) );
563 }
564
565 function is_field_in_group( $field_slug, $group_slug, $object_type ) {
566 return in_array( $field_slug, $this->get_fields_in_group( $group_slug, $object_type ) );
567 }
568
569 function is_group_in_object_type( $group_slug, $object_type ) {
570 return array_key_exists( $group_slug, $this->get_groups_in_object_type( $object_type ) );
571 }
572
573 function is_field_addable_to_columns( $field_slug, $field ) {
574 return is_string( $field->display_column ) || ( is_bool( $field->display_column ) && $field->display_column );
575 }
576
577 function get_field( $field_slug, $group_slug, $object_type ) {
578 if( $this->is_registered_field( $field_slug, $group_slug, $object_type ) ) {
579 if( $group_slug ) {
580 return $this->get_single_field_in_group( $field_slug, $group_slug, $object_type );
581 } else {
582 return $this->get_single_field_in_object_type( $field_slug, $object_type );
583 }
584 }
585 return null;
586 }
587
588 function get_group( $group_slug, $object_type ) {
589 if( $this->is_registered_group( $group_slug, $object_type ) ) {
590 $groups = $this->get_groups_in_object_type( $object_type );
591 return $groups[$group_slug];
592 }
593 return null;
594 }
595
596 function get_object_types() {
597 return array_keys( $this->metadata );
598 }
599
600 function get_groups_in_object_type( $object_type ) {
601 if( $this->is_registered_object_type( $object_type ) )
602 return $this->metadata[$object_type];
603 return array();
604 }
605
606 function get_single_field_in_group( $field_slug, $group_slug, $object_slug ) {
607 $fields = $this->get_fields_in_group( $group_slug, $object_type );
608 return isset( $fields[$field_slug] ) ? $fields[$field_slug] : null;
609 }
610
611 function get_fields_in_group( $group_slug, $object_type ) {
612 $group = $this->get_group( $group_slug, $object_type );
613 if( $group ) return (array) $group->fields;
614 return array();
615 }
616
617 function get_single_field_in_object_type( $field_slug, $object_type ) {
618 $fields = $this->get_fields_in_object_type( $object_type );
619 return isset( $fields[$field_slug] ) ? $fields[$field_slug] : null;
620 }
621
622 function get_fields_in_object_type( $object_type ) {
623 $fields = array();
624 foreach( $this->get_groups_in_object_type( $object_type ) as $group_slug => $group ) {
625 $fields = array_merge( $fields, $this->get_fields_in_group( $group_slug, $object_type ) );
626 }
627 return $fields;
628 }
629
630 function _push_group( $group_slug, $group, $object_type ) {
631 $this->metadata[$object_type][$group_slug] = $group;
632 }
633
634 function _push_field( $field_slug, $field, $group_slug, $object_type ) {
635 $this->metadata[$object_type][$group_slug]->fields[$field_slug] = $field;
636 }
637
638 function is_thing_added_to_object( $thing_slug, $thing, $object_type, $object_id, $object_slug = '' ) {
639
640 if( isset( $thing->exclude ) ) {
641 return ! $this->does_id_array_match_object( $thing->exclude, $object_type, $object_id, $object_slug );
642 }
643
644 if( isset( $thing->include ) ) {
645 return $this->does_id_array_match_object( $thing->include, $object_type, $object_id, $object_slug );
646 }
647
648 return true;
649 }
650
651 function does_id_array_match_object( $id_array, $object_type, $object_id, $object_slug = '' ) {
652 if( is_array( $id_array ) ) {
653 if( isset( $id_array[$object_type] ) ) {
654 if( is_array( $id_array[$object_type] ) ) {
655 // array( 'user' => array( 123, 'postname' ) )
656 return $this->does_id_array_match_object( $id_array[$object_type], $object_type, $object_id, $object_slug );
657 } else {
658 // array( 'post' => 123 )
659 return $this->does_id_match_object( $id_array[$object_type], $object_id, $object_slug );
660 }
661 } else {
662 // array( 123, 456, 'postname' )
663 $match = false;
664 foreach( $id_array as $id ) {
665 if( $this->does_id_match_object( $id, $object_id, $object_slug ) ) {
666 $match = true;
667 break;
668 }
669 }
670 return $match;
671 }
672 } else {
673 // 123 || 'postname' || 'username' || 'comment-name'(?)
674 return $this->does_id_match_object( $id_array, $object_id, $object_slug );
675 }
676 }
677
678 function does_id_match_object( $id, $object_id, $object_slug = '' ) {
679 if( is_int( $id ) ) {
680 // 123
681 return $id == $object_id;
682 } else {
683 // 'postname' || 'username' || 'comment-name' ??
684 return $id == $object_slug;
685 }
686 }
687
688 function is_restricted_field( $field_slug, $object_type ) {
689 // TODO: Build this out
690 $post_restricted = array( 'post_title', 'post_author' );
691 $page_restricted = array( );
692 $user_restricted = array( );
693
694 switch( $object_type ) {
695 case 'user':
696 return in_array( $field_slug, $user_restricted );
697 case 'page':
698 return in_array( $field_slug, $page_restricted ) || in_array( $field_slug, $post_restricted );
699 case 'post':
700 default:
701 return in_array( $field_slug, $post_restricted );
702 }
703 return false;
704 }
705
706 function is_restricted_group( $group_slug, $object_type ) {
707 // TODO: Build this out
708 // Built-in metaboxes: title, custom-fields, revisions, author, etc.
709 return false;
710 }
711
712 function _get_object_type_context() {
713 global $current_screen, $pagenow;
714
715 $object_type = '';
716
717 if( $pagenow == 'profile.php' || $pagenow == 'user-edit.php' || $pagenow == 'users.php' ) {
718 return 'user';
719 }
720
721 if( isset( $current_screen->post_type ) ) {
722 $object_type = $current_screen->post_type;
723 } elseif( isset( $current_screen->base ) ) {
724 foreach( $this->_builtin_object_types as $builtin_type ) {
725 if( strpos( $current_screen->base, $builtin_type ) !== false ) {
726 $object_type = $builtin_type;
727 break;
728 }
729 }
730 }
731
732 return $object_type;
733 }
734
735 function _get_value_callback( $field ) {
736 $callback = isset( $field->value_callback ) ? $field->value_callback : '';
737 if( $callback && is_callable( $callback ) )
738 return $callback;
739 return '';
740 }
741
742 function _get_save_callback( $field ) {
743 $callback = isset( $field->save_callback ) ? $field->save_callback : '';
744 if( $callback && is_callable( $callback ) )
745 return $callback;
746 return '';
747 }
748
749 function get_sanitize_callback( $field ) {
750 $callback = $field->sanitize_callback;
751 if( $callback && is_callable( $callback ) )
752 return $callback;
753 return '';
754 }
755
756 function get_display_column_callback( $field ) {
757 $callback = $field->display_column_callback;
758 if( $callback && is_callable( $callback ) )
759 return $callback;
760 return '';
761 }
762
763 function _get_field_value( $field_slug, $field, $object_type, $object_id ) {
764
765 $get_value_callback = $this->_get_value_callback( $field );
766 echo $get_value_callback;
767 if( $get_value_callback )
768 return call_user_func( $get_value_callback, $object_type, $object_id, $field_slug );
769
770 if ( !in_array( $object_type, $this->_non_post_types ) )
771 $object_type = 'post';
772
773 $value = get_metadata( $object_type, $object_id, $field_slug, false );
774
775 return $value;
776 }
777
778 function _save_field_value( $field_slug, $field, $object_type, $object_id, $value ) {
779
780 $save_callback = $this->_get_save_callback( $field );
781
782 if( $save_callback )
783 return call_user_func( $save_callback, $object_type, $object_id, $field_slug, $value );
784
785 if( ! in_array( $object_type, $this->_non_post_types ) )
786 $object_type = 'post';
787
788 $field_slug = sanitize_key( $field_slug );
789
790 // save the taxonomy as a taxonomy [as well as a custom field]
791 if (($field->field_type == 'taxonomy_select' || $field->field_type == 'taxonomy_radio') && !in_array( $object_type, $this->_non_post_types )) {
792 wp_set_object_terms($object_id, $value, $field->taxonomy);
793 }
794
795 if (is_array($value)) {
796 // multiple values
797 delete_metadata( $object_type, $object_id, $field_slug ); // delete the old values and add the new ones
798 $value = array_reverse($value);
799 foreach ($value as $v) {
800 add_metadata( $object_type, $object_id, $field_slug, $v, false );
801 }
802 } else {
803 // single value
804 update_metadata( $object_type, $object_id, $field_slug, $value );
805 }
806
807 // delete metadata entries if empty
808 if (empty($value)) {
809 delete_metadata( $object_type, $object_id, $field_slug );
810 }
811
812
813 }
814
815 function _delete_field_value( $field_slug, $field, $object_type, $object_id, $value = false ) {
816 if( ! in_array( $object_type, $this->_non_post_types ) )
817 $object_type = 'post';
818
819 $field_slug = sanitize_key( $field_slug );
820
821 delete_metadata( $object_type, $object_id, $field_slug, $value );
822 }
823
824 function _sanitize_field_value( $field_slug, $field, $object_type, $object_id, $value ) {
825
826 $sanitize_callback = $this->get_sanitize_callback( $field );
827
828 if( $sanitize_callback )
829 return call_user_func( $sanitize_callback, $field_slug, $field, $object_type, $object_id, $value );
830
831 // convert date to unix timestamp
832 if ($field->field_type == 'datepicker') {
833 $value = strtotime($value);
834 }
835
836 return $value;
837 }
838
839 function _metadata_column_content( $field_slug, $field, $object_type, $object_id ) {
840 $value = $this->get_metadata_field_value( $field_slug, $field, $object_type, $object_id );
841
842 $display_column_callback = $this->get_display_column_callback( $field );
843
844 if( $display_column_callback )
845 return call_user_func( $display_column_callback, $field_slug, $field, $object_type, $object_id, $value );
846
847 if( is_array( $value ) )
848 return implode( ', ', $value );
849 return $value;
850 }
851
852 function _display_metadata_field( $field_slug, $field, $object_type, $object_id ) {
853
854 $value = $this->get_metadata_field_value( $field_slug, $field, $object_type, $object_id );
855
856 if (isset($field->display_callback) && function_exists($field->display_callback)) :
857
858 call_user_func($field->display_callback, $field_slug, $field, $object_type, $object_id, $value);
859
860 else :
861 ?>
862 <div class="custom-metadata-field <?php echo $field->field_type ?>">
863 <?php
864 if (!in_array($object_type, $this->_non_post_types)) global $post;
865 if (isset($field->multiple) && $field->multiple && @!in_array($field->field_type, $this->_cloneable_field_types)) {
866 $field->multiple = false;
867 echo '<p class="error"><strong>Note:</strong> this field type cannot be multiplied</p>';
868 }
869
870 if (isset($field->multiple) && $field->multiple) $field_id = $field_slug.'[]';
871 else $field_id = $field_slug;
872
873 $readonly_str = ($field->readonly) ? 'readonly="readonly" ' : '';
874
875 if (get_post_type()) $numb = $post->ID; else $numb = 1; ?>
876 <script>var numb = '<?php echo $numb ?>'; </script>
877
878 <label for="<?php echo $field_slug; ?>"><?php echo $field->label; ?></label>
879 <?php
880 // make sure $value is an array
881 if (!$value) $value = ''; // if empty, give it an empty string instead
882 $value = (array)$value;
883 $count = 1;
884 foreach( $value as $v ) : ?>
885
886 <div class="<?php echo $field_slug ?> cloneable" id="<?php echo $field_slug ?>-<?php echo $count;?>">
887
888 <?php switch ($field->field_type) :
889 case 'text': ?>
890 <input type="text" id="<?php echo $field_slug; ?>" name="<?php echo $field_id; ?>" value="<?php echo esc_attr( $v ); ?>" <?php echo $readonly_str ?>/>
891 <?php break; ?>
892
893 <?php case 'textarea': ?>
894 <textarea id="<?php echo $field_slug; ?>" name="<?php echo $field_id; ?>" <?php echo $readonly_str ?>><?php echo esc_attr($v); ?></textarea>
895 <?php break; ?>
896
897 <?php case 'password': ?>
898 <input type="password" id="<?php echo $field_slug; ?>" name="<?php echo $field_id; ?>" value="<?php echo esc_attr($v); ?>" <?php echo $readonly_str ?>/>
899 <?php break; ?>
900
901 <?php case 'checkbox': ?>
902 <?php $checked = $v ? ' checked="checked"' : ''; ?>
903 <input type="checkbox" id="<?php echo $field_slug; ?>" name="<?php echo $field_id; ?>" <?php echo $checked; ?> />
904 <?php break; ?>
905
906 <?php case 'radio': ?>
907 <?php foreach( $field->values as $value_slug => $value_label ) : ?>
908 <?php
909 $checked = ( $v == $value_slug ) ? ' checked="checked"' : '';
910 $value_id = sprintf( '%s_%s', $field_slug, $value_slug );
911 ?>
912 <label for="<?php echo $value_id; ?>" class="selectit">
913 <input type="radio" id="<?php echo $value_id; ?>" name="<?php echo $field_id; ?>" id="<?php echo $value_id; ?>" value="<?php echo $value_slug ?>" <?php echo $checked; ?> />
914 <?php echo $value_label; ?>
915 </label>
916 <?php endforeach; ?>
917 <?php break; ?>
918
919 <?php case 'select': ?>
920 <select id="<?php echo $field_slug; ?>" name="<?php echo $field_id; ?>">
921 <?php foreach( $field->values as $value_slug => $value_label ) : ?>
922 <?php
923 $selected = ( $v == $value_slug ) ? ' selected="selected"' : '';
924 $value_id = $field_slug . $value_slug;
925 ?>
926 <option value="<?php echo $value_slug ?>" <?php echo $selected; ?>>
927 <?php echo $value_label; ?>
928 </option>
929 <?php endforeach; ?>
930 </select>
931 <?php break; ?>
932
933 <?php case 'datepicker': ?>
934 <input type="text" name="<?php echo $field_id; ?>" value="<?php echo @date('m/d/Y', $v); ?>" <?php echo $readonly_str ?>/>
935 <?php break; ?>
936
937 <?php case 'wysiwyg': ?>
938 <?php
939 $args = apply_filters('custom_metadata_manager_wysiwyg_args_field_'.$field_id, $this->default_editor_args, $field_slug, $field, $object_type, $object_id );
940 wp_editor($v, $field_id, $args);
941 ?>
942 <?php break; ?>
943
944 <?php case 'upload': ?>
945 <input type="text" name="<?php echo $field_id; ?>" value="<?php echo $v; ?>" class="upload_field" <?php echo $readonly_str ?>/>
946 <input type="button" title="<?php echo $post->ID ?>" class="button upload_button" value="Upload" />
947 <?php break; ?>
948
949 <?php case 'taxonomy_select': ?>
950 <select name="<?php echo $field_id; ?>" id="<?php echo $field_slug; ?>">
951 <?php
952 $terms = get_terms( $field->taxonomy, array('hide_empty' => false));
953 foreach ( $terms as $term ) { ?>
954 <option value="<?php echo $term->slug ?>"<?php if ($term->slug == $v) echo ' selected' ?>><?php echo $term->name ?></option>
955 <?php }
956 ?>
957 </select>
958 <?php break; ?>
959
960 <?php case 'taxonomy_radio':
961 $terms = get_terms( $field->taxonomy, array('hide_empty' => false) );
962 foreach ( $terms as $term ) { ?>
963 <label for="<?php echo $term->slug; ?>" class="selectit">
964 <input type="radio" name="<?php echo $field_id ?>" value="<?php echo $term->slug ?>" id="<?php echo $term->slug ?>"<?php if ($term->slug == $v) echo ' checked' ?>>
965 <?php echo $term->name ?>
966 </label>
967 <?php } ?>
968 <?php break; ?>
969
970 <?php endswitch; ?>
971
972 <?php if ($count > 1) : ?>
973 <a href="#" class="del-multiple hide-if-no-js" style="color:red;">Delete</a>
974 <?php endif; $count++ ?>
975
976 </div>
977
978 <?php endforeach; ?>
979 <?php if (isset($field->multiple) && $field->multiple) : ?>
980 <p><a href="#" class="add-multiple hide-if-no-js" id="add-<?php echo $field_slug ?>">+ Add New</a></p>
981 <?php endif;?>
982
983 <?php $this->_display_field_description( $field_slug, $field, $object_type, $object_id, $value ); ?>
984
985 </div>
986
987 <?php
988 endif;
989 }
990
991 function _display_field_description( $field_slug, $field, $object_type, $object_id, $value ) {
992 ?>
993 <?php if( $field->description ) : ?>
994 <span class="description"><?php echo $field->description; ?></span>
995 <?php endif; ?>
996 <?php
997 }
998
999 function _display_registration_errors( ) {
1000 if( !empty( $this->errors ) ) {
1001 ?>
1002 <div class="message error">
1003 <?php foreach( $this->errors as $error => $error_message ) : ?>
1004 <li><?php echo $error_message; ?></li>
1005 <?php endforeach; ?>
1006 </div>
1007 <?php
1008 }
1009 }
1010
1011 function debug($msg, $object) {
1012 if( CUSTOM_METADATA_MANAGER_DEBUG ) {
1013 echo '<hr />';
1014 echo sprintf('<p>%s</p>', $msg);
1015 echo '<pre>';
1016 var_dump($object);
1017 echo '</pre>';
1018 }
1019 }
1020 }
1021
1022 global $custom_metadata_manager;
1023 $custom_metadata_manager = new custom_metadata_manager();
1024
1025 endif; // !class_exists
1026
1027 function x_add_metadata_field( $slug, $object_types = 'post', $args = array() ) {
1028 global $custom_metadata_manager;
1029 $custom_metadata_manager->add_metadata_field( $slug, $object_types, $args );
1030 }
1031
1032 function x_add_metadata_group( $slug, $object_types, $args = array() ) {
1033 global $custom_metadata_manager;
1034 $custom_metadata_manager->add_metadata_group( $slug, $object_types, $args );
1035 }