PluginProbe
WP Ultimate Post Grid / 2.8.0
WP Ultimate Post Grid v2.8.0
4.1.1 trunk 1.5 1.6 1.7 1.7.1 1.7.2 1.8 1.9 2.0 2.1 2.2 2.3 2.4.0 2.5.0 2.6.0 2.7.0 2.8.0 2.8.2 3.0.0 3.3.0 3.4.0 3.5.0 3.6.0 3.7.0 All 32 releases
wp-ultimate-post-grid / vendor / vafpress / includes / wpalchemy / MetaBox.php

MetaBox.php in WP Ultimate Post Grid 2.8.0, at vendor/vafpress/includes/wpalchemy/MetaBox.php

2,764 lines 58.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * @author Dimas Begunoff
5 * @copyright Copyright (c) 2009, Dimas Begunoff, http://farinspace.com
6 * @license http://en.wikipedia.org/wiki/MIT_License The MIT License
7 * @package WPAlchemy
8 * @version 1.5.2
9 * @link http://github.com/farinspace/wpalchemy
10 * @link http://farinspace.com
11 */
12
13 /**
14 * This is modified version so that WPAlchemy supports nested repeatable group
15 * Vafpress (http://vafpress.com)
16 * 2013
17 */
18
19 // todo: perhaps move _global_head and _global_foot locally, when first run
20 // define a constant to prevent other instances from running again ...
21
22 add_action('admin_head', array('WPAlchemy_MetaBox', '_global_head'));
23
24 add_action('admin_footer', array('WPAlchemy_MetaBox', '_global_foot'));
25
26 define('WPALCHEMY_MODE_ARRAY', 'array');
27
28 define('WPALCHEMY_MODE_EXTRACT', 'extract');
29
30 define('WPALCHEMY_FIELD_HINT_TEXT', 'text');
31
32 define('WPALCHEMY_FIELD_HINT_TEXTAREA', 'textarea');
33
34 define('WPALCHEMY_FIELD_HINT_CHECKBOX', 'checkbox');
35
36 define('WPALCHEMY_FIELD_HINT_CHECKBOX_MULTI', 'checkbox_multi');
37
38 define('WPALCHEMY_FIELD_HINT_RADIO', 'radio');
39
40 define('WPALCHEMY_FIELD_HINT_SELECT', 'select');
41
42 define('WPALCHEMY_FIELD_HINT_SELECT_MULTI', 'select_multi');
43
44 // depreciated, use WPALCHEMY_FIELD_HINT_SELECT_MULTI instead
45 define('WPALCHEMY_FIELD_HINT_SELECT_MULTIPLE', 'select_multiple');
46
47 define('WPALCHEMY_LOCK_TOP', 'top');
48
49 define('WPALCHEMY_LOCK_BOTTOM', 'bottom');
50
51 define('WPALCHEMY_LOCK_BEFORE_POST_TITLE', 'before_post_title');
52
53 define('WPALCHEMY_LOCK_AFTER_POST_TITLE', 'after_post_title');
54
55 define('WPALCHEMY_VIEW_START_OPENED', 'opened');
56
57 define('WPALCHEMY_VIEW_START_CLOSED', 'closed');
58
59 define('WPALCHEMY_VIEW_ALWAYS_OPENED', 'always_opened');
60
61 class WPAlchemy_MetaBox
62 {
63 /**
64 * User defined identifier for the meta box, prefix with an underscore to
65 * prevent option(s) form showing up in the custom fields meta box, this
66 * option should be used when instantiating the class.
67 *
68 * @since 1.0
69 * @access public
70 * @var string required
71 */
72 var $id;
73
74 /**
75 * Used to set the title of the meta box, this option should be used when
76 * instantiating the class.
77 *
78 * @since 1.0
79 * @access public
80 * @var string required
81 * @see $hide_title
82 */
83 var $title = 'Custom Meta';
84
85 /**
86 * Used to set the meta box content, the contents of your meta box should be
87 * defined within this file, this option should be used when instantiating
88 * the class.
89 *
90 * @since 1.0
91 * @access public
92 * @var string required
93 */
94 var $template;
95
96 /**
97 * Used to set the post types that the meta box can appear in, this option
98 * should be used when instantiating the class.
99 *
100 * @since 1.0
101 * @access public
102 * @var array
103 */
104 var $types;
105
106 /**
107 * @since 1.0
108 * @access public
109 * @var bool
110 */
111 var $context = 'normal';
112
113 /**
114 * @since 1.0
115 * @access public
116 * @var bool
117 */
118 var $priority = 'high';
119
120 /**
121 * @since 1.0
122 * @access public
123 * @var bool
124 */
125 var $autosave = TRUE;
126
127 /**
128 * Used to set how the class does its data storage, data will be stored as
129 * an associative array in a single meta entry in the wp_postmeta table or
130 * data can be set and individual entries in the wp_postmeta table, the
131 * following constants should be used when setting this option,
132 * WPALCHEMY_MODE_ARRAY (default) and WPALCHEMY_MODE_EXTRACT, this option
133 * should be used when instantiating the class.
134 *
135 * @since 1.2
136 * @access public
137 * @var string
138 */
139 var $mode = WPALCHEMY_MODE_ARRAY;
140
141 /**
142 * When the mode option is set to WPALCHEMY_MODE_EXTRACT, you have to take
143 * care to avoid name collisions with other meta entries. Use this option to
144 * automatically add a prefix to your variables, this option should be used
145 * when instantiating the class.
146 *
147 * @since 1.2
148 * @access public
149 * @var array
150 */
151 var $prefix;
152
153 /**
154 * @since 1.0
155 * @access public
156 * @var bool
157 */
158 var $exclude_template;
159
160 /**
161 * @since 1.0
162 * @access public
163 * @var bool
164 */
165 var $exclude_category_id;
166
167 /**
168 * @since 1.0
169 * @access public
170 * @var bool
171 */
172 var $exclude_category;
173
174 /**
175 * @since 1.0
176 * @access public
177 * @var bool
178 */
179 var $exclude_tag_id;
180
181 /**
182 * @since 1.0
183 * @access public
184 * @var bool
185 */
186 var $exclude_tag;
187
188 /**
189 * @since 1.0
190 * @access public
191 * @var bool
192 */
193 var $exclude_post_id;
194
195 /**
196 * @since 1.0
197 * @access public
198 * @var bool
199 */
200 var $include_template;
201
202 /**
203 * @since 1.0
204 * @access public
205 * @var bool
206 */
207 var $include_category_id;
208
209 /**
210 * @since 1.0
211 * @access public
212 * @var bool
213 */
214 var $include_category;
215
216 /**
217 * @since 1.0
218 * @access public
219 * @var bool
220 */
221 var $include_tag_id;
222
223 /**
224 * @since 1.0
225 * @access public
226 * @var bool
227 */
228 var $include_tag;
229
230 /**
231 * @since 1.0
232 * @access public
233 * @var bool
234 */
235 var $include_post_id;
236
237 /**
238 * Callback used on the WordPress "admin_init" action, the main benefit is
239 * that this callback is executed only when the meta box is present, this
240 * option should be used when instantiating the class.
241 *
242 * @since 1.3.4
243 * @access public
244 * @var string|array optional
245 */
246 var $init_action;
247
248 /**
249 * Callback used to override when the meta box gets displayed, must return
250 * true or false to determine if the meta box should or should not be
251 * displayed, this option should be used when instantiating the class.
252 *
253 * @since 1.3
254 * @access public
255 * @var string|array optional
256 * @param array $post_id first variable passed to the callback function
257 * @see can_output()
258 */
259 var $output_filter;
260
261 /**
262 * Callback used to override or insert meta data before saving, you can halt
263 * saving by passing back FALSE (return FALSE), this option should be used
264 * when instantiating the class.
265 *
266 * @since 1.3
267 * @access public
268 * @var string|array optional
269 * @param array $meta meta box data, first variable passed to the callback function
270 * @param string $post_id second variable passed to the callback function
271 * @see $save_action, add_filter()
272 */
273 var $save_filter;
274
275 /**
276 * Callback used to execute custom code after saving, this option should be
277 * used when instantiating the class.
278 *
279 * @since 1.3
280 * @access public
281 * @var string|array optional
282 * @param array $meta meta box data, first variable passed to the callback function
283 * @param string $post_id second variable passed to the callback function
284 * @see $save_filter, add_filter()
285 */
286 var $save_action;
287
288 /**
289 * Callback used to override or insert STYLE or SCRIPT tags into the head,
290 * this option should be used when instantiating the class.
291 *
292 * @since 1.3
293 * @access public
294 * @var string|array optional
295 * @param array $content current head content, first variable passed to the callback function
296 * @see $head_action, add_filter()
297 */
298 var $head_filter;
299
300 /**
301 * Callback used to insert STYLE or SCRIPT tags into the head,
302 * this option should be used when instantiating the class.
303 *
304 * @since 1.3
305 * @access public
306 * @var string|array optional
307 * @see $head_filter, add_action()
308 */
309 var $head_action;
310
311 /**
312 * Callback used to override or insert SCRIPT tags into the footer, this
313 * option should be used when instantiating the class.
314 *
315 * @since 1.3
316 * @access public
317 * @var string|array optional
318 * @param array $content current foot content, first variable passed to the callback function
319 * @see $foot_action, add_filter()
320 */
321 var $foot_filter;
322
323 /**
324 * Callback used to insert SCRIPT tags into the footer, this option should
325 * be used when instantiating the class.
326 *
327 * @since 1.3
328 * @access public
329 * @var string|array optional
330 * @see $foot_filter, add_action()
331 */
332 var $foot_action;
333
334 /**
335 * Used to hide the default content editor in a page or post, this option
336 * should be used when instantiating the class.
337 *
338 * @since 1.3
339 * @access public
340 * @var bool optional
341 */
342 var $hide_editor = FALSE;
343
344 /**
345 * Used in conjunction with the "hide_editor" option, prevents the media
346 * buttons from also being hidden.
347 *
348 * @since 1.5
349 * @access public
350 * @var bool optional
351 */
352 var $use_media_buttons = FALSE;
353
354 /**
355 * Used to hide the meta box title, this option should be used when
356 * instantiating the class.
357 *
358 * @since 1.3
359 * @access public
360 * @var bool optional
361 * @see $title
362 */
363 var $hide_title = FALSE;
364
365 /**
366 * Used to lock a meta box in place, possible values are: top, bottom,
367 * before_post_title, after_post_title, this option should be used when
368 * instantiating the class.
369 *
370 * @since 1.3.3
371 * @access public
372 * @var string optional possible values are: top, bottom, before_post_title, after_post_title
373 */
374 var $lock;
375
376 /**
377 * Used to lock a meta box at top (below the default content editor), this
378 * option should be used when instantiating the class.
379 *
380 * @deprecated deprecated since version 1.3.3
381 * @since 1.3
382 * @access public
383 * @var bool optional
384 * @see $lock
385 */
386 var $lock_on_top = FALSE;
387
388 /**
389 * Used to lock a meta box at bottom, this option should be used when
390 * instantiating the class.
391 *
392 * @deprecated deprecated since version 1.3.3
393 * @since 1.3
394 * @access public
395 * @var bool optional
396 * @see $lock
397 */
398 var $lock_on_bottom = FALSE;
399
400 /**
401 * Used to set the initial view state of the meta box, possible values are:
402 * opened, closed, always_opened, this option should be used when
403 * instantiating the class.
404 *
405 * @since 1.3.3
406 * @access public
407 * @var string optional possible values are: opened, closed, always_opened
408 */
409 var $view;
410
411 /**
412 * Used to hide the show/hide checkbox option from the screen options area,
413 * this option should be used when instantiating the class.
414 *
415 * @since 1.3.4
416 * @access public
417 * @var bool optional
418 */
419 var $hide_screen_option = FALSE;
420
421 // private
422
423 var $meta;
424 var $name;
425
426 /**
427 * Used to provide field type hinting
428 *
429 * @since 1.3
430 * @access private
431 * @var string
432 * @see the_field()
433 */
434 var $hint;
435
436 var $length = 0;
437 var $current = -1;
438 var $in_loop = FALSE;
439 var $in_template = FALSE;
440 var $group_tag;
441 var $current_post_id;
442
443 /**
444 * Used to store current loop details, cleared after loop ends
445 *
446 * @since 1.4
447 * @access private
448 * @var stdClass
449 * @see have_fields_and_multi(), have_fields()
450 */
451 var $_loop_data;
452
453 function __construct($arr)
454 {
455 $this->_loop_data = new stdClass;
456
457 $this->meta = array();
458
459 $this->types = array('post', 'page');
460
461 if (isset($arr) and is_array($arr))
462 {
463 foreach ($arr as $n => $v)
464 {
465 $this->$n = $v;
466 }
467
468 if (empty($this->id)) die('Meta box ID required');
469
470 if (is_numeric($this->id)) die('Meta box ID must be a string');
471
472 if (empty($this->template)) die('Meta box template file required');
473
474 // check for nonarray values
475
476 $exc_inc = array
477 (
478 'exclude_template',
479 'exclude_category_id',
480 'exclude_category',
481 'exclude_tag_id',
482 'exclude_tag',
483 'exclude_post_id',
484
485 'include_template',
486 'include_category_id',
487 'include_category',
488 'include_tag_id',
489 'include_tag',
490 'include_post_id'
491 );
492
493 foreach ($exc_inc as $v)
494 {
495 // ideally the exclude and include values should be in array form, convert to array otherwise
496 if (!empty($this->$v) AND !is_array($this->$v))
497 {
498 $this->$v = array_map('trim',explode(',',$this->$v));
499 }
500 }
501
502 // convert depreciated variables
503 if ($this->lock_on_top) $this->lock = WPALCHEMY_LOCK_TOP;
504 elseif ($this->lock_on_bottom) $this->lock = WPALCHEMY_LOCK_BOTTOM;
505
506 add_action('admin_init', array($this,'_init'));
507
508 // uses the default wordpress-importer plugin hook
509 add_action('import_post_meta', array($this, '_import'), 10, 3);
510 }
511 else
512 {
513 die('Associative array parameters required');
514 }
515 }
516
517 /**
518 * Used to correct double serialized data during post/page export/import,
519 * additionally will try to fix corrupted serialized data by recalculating
520 * string length values
521 *
522 * @since 1.3.16
523 * @access private
524 */
525 function _import($post_id, $key, $value)
526 {
527 if (WPALCHEMY_MODE_ARRAY == $this->mode AND $key == $this->id)
528 {
529 // using $wp_import to get access to the raw postmeta data prior to it getting passed
530 // through "maybe_unserialize()" in "plugins/wordpress-importer/wordpress-importer.php"
531 // the "import_post_meta" action is called after "maybe_unserialize()"
532
533 global $wp_import;
534
535 foreach ( $wp_import->posts as $post )
536 {
537 if ( $post_id == $post['post_id'] )
538 {
539 foreach( $post['postmeta'] as $meta )
540 {
541 if ( $key == $meta['key'] )
542 {
543 // try to fix corrupted serialized data, specifically "\r\n" being converted to "\n" during wordpress XML export (WXR)
544 // "maybe_unserialize()" fixes a wordpress bug which double serializes already serialized data during export/import
545 //preg_replace( '!s:(\d+):"(.*?)";!es', "'s:'.strlen('$2').':\"$2\";'", stripslashes( $meta['value'] ) );
546 $value = maybe_unserialize( preg_replace_callback(
547 '!s:(\d+):"(.*?)";!es',
548 array( $this, 'preg_replace_fix' ),
549 stripslashes( $meta['value'] )
550 ) );
551
552 update_post_meta( $post_id, $key, $value );
553 }
554 }
555 }
556 }
557 }
558 }
559
560 function preg_replace_fix($m) {
561 return 's:'.strlen($m[2]).':' . $m[2] . ';';
562 }
563
564 /**
565 * Used to initialize the meta box, runs on WordPress admin_init action,
566 * properly calls internal WordPress methods
567 *
568 * @since 1.0
569 * @access private
570 */
571 function _init()
572 {
573 // must be creating or editing a post or page
574 if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
575
576 if ( ! empty($this->output_filter))
577 {
578 $this->add_filter('output', $this->output_filter);
579 }
580
581 if ($this->can_output())
582 {
583 foreach ($this->types as $type)
584 {
585 add_meta_box($this->id . '_metabox', $this->title, array($this, '_setup'), $type, $this->context, $this->priority);
586 }
587
588 add_action('save_post', array($this,'_save'));
589
590 $filters = array('save', 'head', 'foot');
591
592 foreach ($filters as $filter)
593 {
594 $var = $filter . '_filter';
595
596 if (!empty($this->$var))
597 {
598 if ('save' == $filter)
599 {
600 $this->add_filter($filter, $this->$var, 10, 2);
601 }
602 else
603 {
604 $this->add_filter($filter, $this->$var);
605 }
606 }
607 }
608
609 $actions = array('save', 'head', 'foot', 'init');
610
611 foreach ($actions as $action)
612 {
613 $var = $action . '_action';
614
615 if (!empty($this->$var))
616 {
617 if ('save' == $action)
618 {
619 $this->add_action($action, $this->$var, 10, 2);
620 }
621 else
622 {
623 $this->add_action($action, $this->$var);
624 }
625 }
626 }
627
628 add_action('admin_head', array($this,'_head'), 11);
629
630 add_action('admin_footer', array($this,'_foot'), 11);
631
632 // action: init
633 if ($this->has_action('init'))
634 {
635 $this->do_action('init');
636 }
637 }
638 }
639
640 /**
641 * Used to insert STYLE or SCRIPT tags into the head, called on WordPress
642 * admin_head action.
643 *
644 * @since 1.3
645 * @access private
646 * @see _foot()
647 */
648 function _head()
649 {
650 $content = NULL;
651
652 ob_start();
653
654 ?>
655 <style type="text/css">
656 <?php if ($this->hide_editor) { ?> #wp-content-editor-container, #post-status-info, <?php if ($this->use_media_buttons) { ?> #content-html, #content-tmce<?php } else { ?> #wp-content-wrap<?php } ?> { display:none; } <?php } ?>
657 </style>
658 <?php
659
660 $content = ob_get_contents();
661
662 ob_end_clean();
663
664 // filter: head
665 if ($this->has_filter('head'))
666 {
667 $content = $this->apply_filters('head', $content);
668 }
669
670 echo $content;
671
672 // action: head
673 if ($this->has_action('head'))
674 {
675 $this->do_action('head');
676 }
677 }
678
679 /**
680 * Used to insert SCRIPT tags into the footer, called on WordPress
681 * admin_footer action.
682 *
683 * @since 1.3
684 * @access private
685 * @see _head()
686 */
687 function _foot()
688 {
689 $content = NULL;
690
691 if
692 (
693 $this->lock OR
694 $this->hide_title OR
695 $this->view OR
696 $this->hide_screen_option
697 )
698 {
699 ob_start();
700
701 ?>
702 <script type="text/javascript">
703 /* <![CDATA[ */
704 (function($){ /* not using jQuery ondomready, code runs right away in footer */
705
706 var mb_id = '<?php echo $this->id; ?>';
707 var mb = $('#' + mb_id + '_metabox');
708
709 <?php if (WPALCHEMY_LOCK_TOP == $this->lock): ?>
710 <?php if ('side' == $this->context): ?>
711 var id = 'wpalchemy-side-top';
712 if ( ! $('#'+id).length)
713 {
714 $('<div></div>').attr('id',id).prependTo('#side-info-column');
715 }
716 <?php else: ?>
717 var id = 'wpalchemy-content-top';
718 if ( ! $('#'+id).length)
719 {
720 $('<div></div>').attr('id',id).insertAfter('#postdiv, #postdivrich');
721 }
722 <?php endif; ?>
723 $('#'+id).append(mb);
724 <?php elseif (WPALCHEMY_LOCK_BOTTOM == $this->lock): ?>
725 <?php if ('side' == $this->context): ?>
726 var id = 'wpalchemy-side-bottom';
727 if ( ! $('#'+id).length)
728 {
729 $('<div></div>').attr('id',id).appendTo('#side-info-column');
730 }
731 <?php else: ?>
732 if ( ! $('#advanced-sortables').children().length)
733 {
734 $('#advanced-sortables').css('display','none');
735 }
736
737 var id = 'wpalchemy-content-bottom';
738 if ( ! $('#'+id).length)
739 {
740 $('<div></div>').attr('id',id).insertAfter('#advanced-sortables');
741 }
742 <?php endif; ?>
743 $('#'+id).append(mb);
744 <?php elseif (WPALCHEMY_LOCK_BEFORE_POST_TITLE == $this->lock): ?>
745 <?php if ('side' != $this->context): ?>
746 var id = 'wpalchemy-content-bpt';
747 if ( ! $('#'+id).length)
748 {
749 $('<div></div>').attr('id',id).prependTo('#post-body-content');
750 }
751 $('#'+id).append(mb);
752 <?php endif; ?>
753 <?php elseif (WPALCHEMY_LOCK_AFTER_POST_TITLE == $this->lock): ?>
754 <?php if ('side' != $this->context): ?>
755 var id = 'wpalchemy-content-apt';
756 if ( ! $('#'+id).length)
757 {
758 $('<div></div>').attr('id',id).insertAfter('#titlediv');
759 }
760 $('#'+id).append(mb);
761 <?php endif; ?>
762 <?php endif; ?>
763
764 <?php if ( ! empty($this->lock)): ?>
765 $('.hndle', mb).css('cursor','pointer');
766 $('.handlediv', mb).remove();
767 <?php endif; ?>
768
769 <?php if ($this->hide_title): ?>
770 $('.hndle', mb).remove();
771 $('.handlediv', mb).remove();
772 mb.removeClass('closed'); /* start opened */
773 <?php endif; ?>
774
775 <?php if (WPALCHEMY_VIEW_START_OPENED == $this->view): ?>
776 mb.removeClass('closed');
777 <?php elseif (WPALCHEMY_VIEW_START_CLOSED == $this->view): ?>
778 mb.addClass('closed');
779 <?php elseif (WPALCHEMY_VIEW_ALWAYS_OPENED == $this->view): ?>
780 /* todo: need to find a way to add this script block below, load-scripts.php?... */
781 var h3 = mb.children('h3');
782 setTimeout(function(){ h3.unbind('click'); }, 1000);
783 $('.handlediv', mb).remove();
784 mb.removeClass('closed'); /* start opened */
785 $('.hndle', mb).css('cursor','auto');
786 <?php endif; ?>
787
788 <?php if ($this->hide_screen_option): ?>
789 $('.metabox-prefs label[for='+ mb_id +'_metabox-hide]').remove();
790 <?php endif; ?>
791
792 mb = null;
793
794 })(jQuery);
795 /* ]]> */
796 </script>
797 <?php
798
799 $content = ob_get_contents();
800
801 ob_end_clean();
802 }
803
804 // filter: foot
805 if ($this->has_filter('foot'))
806 {
807 $content = $this->apply_filters('foot', $content);
808 }
809
810 echo $content;
811
812 // action: foot
813 if ($this->has_action('foot'))
814 {
815 $this->do_action('foot');
816 }
817 }
818
819 /**
820 * Used to setup the meta box content template
821 *
822 * @since 1.0
823 * @access private
824 * @see _init()
825 */
826 function _setup()
827 {
828 $this->in_template = TRUE;
829
830 // also make current post data available
831 global $post;
832
833 // shortcuts
834 $mb =& $this;
835 $metabox =& $this;
836 $id = $this->id;
837 $meta = $this->_meta(NULL, TRUE);
838
839 // use include because users may want to use one templete for multiple meta boxes
840 include $this->template;
841
842 // create a nonce for verification
843 echo '<input type="hidden" name="'. $this->id .'_nonce" value="' . wp_create_nonce($this->id) . '" />';
844
845 $this->in_template = FALSE;
846 }
847
848 /**
849 * Used to properly prefix the filter tag, the tag is unique to the meta
850 * box instance
851 *
852 * @since 1.3
853 * @access private
854 * @param string $tag name of the filter
855 * @return string uniquely prefixed tag name
856 */
857 function _get_filter_tag($tag)
858 {
859 $prefix = 'wpalchemy_filter_' . $this->id . '_';
860 $prefix = preg_replace('/_+/', '_', $prefix);
861
862 $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
863 return $prefix . $tag;
864 }
865
866 /**
867 * Uses WordPress add_filter() function, see WordPress add_filter()
868 *
869 * @since 1.3
870 * @access public
871 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L65
872 */
873 function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1)
874 {
875 $tag = $this->_get_filter_tag($tag);;
876 add_filter($tag, $function_to_add, $priority, $accepted_args);
877 }
878
879 /**
880 * Uses WordPress has_filter() function, see WordPress has_filter()
881 *
882 * @since 1.3
883 * @access public
884 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L86
885 */
886 function has_filter($tag, $function_to_check = FALSE)
887 {
888 $tag = $this->_get_filter_tag($tag);
889 return has_filter($tag, $function_to_check);
890 }
891
892 /**
893 * Uses WordPress apply_filters() function, see WordPress apply_filters()
894 *
895 * @since 1.3
896 * @access public
897 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L134
898 */
899 function apply_filters($tag, $value)
900 {
901 $args = func_get_args();
902 $args[0] = $this->_get_filter_tag($tag);
903 return call_user_func_array('apply_filters', $args);
904 }
905
906 /**
907 * Uses WordPress remove_filter() function, see WordPress remove_filter()
908 *
909 * @since 1.3
910 * @access public
911 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L250
912 */
913 function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
914 {
915 $tag = $this->_get_filter_tag($tag);
916 return remove_filter($tag, $function_to_remove, $priority, $accepted_args);
917 }
918
919 /**
920 * Used to properly prefix the action tag, the tag is unique to the meta
921 * box instance
922 *
923 * @since 1.3
924 * @access private
925 * @param string $tag name of the action
926 * @return string uniquely prefixed tag name
927 */
928 function _get_action_tag($tag)
929 {
930 $prefix = 'wpalchemy_action_' . $this->id . '_';
931 $prefix = preg_replace('/_+/', '_', $prefix);
932
933 $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
934 return $prefix . $tag;
935 }
936
937 /**
938 * Uses WordPress add_action() function, see WordPress add_action()
939 *
940 * @since 1.3
941 * @access public
942 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L324
943 */
944 function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1)
945 {
946 $tag = $this->_get_action_tag($tag);
947 add_action($tag, $function_to_add, $priority, $accepted_args);
948 }
949
950 /**
951 * Uses WordPress has_action() function, see WordPress has_action()
952 *
953 * @since 1.3
954 * @access public
955 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L492
956 */
957 function has_action($tag, $function_to_check = FALSE)
958 {
959 $tag = $this->_get_action_tag($tag);
960 return has_action($tag, $function_to_check);
961 }
962
963 /**
964 * Uses WordPress remove_action() function, see WordPress remove_action()
965 *
966 * @since 1.3
967 * @access public
968 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L513
969 */
970 function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
971 {
972 $tag = $this->_get_action_tag($tag);
973 return remove_action($tag, $function_to_remove, $priority, $accepted_args);
974 }
975
976 /**
977 * Uses WordPress do_action() function, see WordPress do_action()
978 * @since 1.3
979 * @access public
980 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L352
981 */
982 function do_action($tag, $arg = '')
983 {
984 $args = func_get_args();
985 $args[0] = $this->_get_action_tag($tag);
986 return call_user_func_array('do_action', $args);
987 }
988
989 /**
990 * Used to check if creating a new post or editing one
991 *
992 * @static
993 * @since 1.3.7
994 * @access private
995 * @return bool
996 * @see _is_page()
997 */
998 static function _is_post()
999 {
1000 if ('post' == WPAlchemy_MetaBox::_is_post_or_page())
1001 {
1002 return TRUE;
1003 }
1004
1005 return FALSE;
1006 }
1007
1008 /**
1009 * Used to check if creating a new page or editing one
1010 *
1011 * @static
1012 * @since 1.3.7
1013 * @access private
1014 * @return bool
1015 * @see _is_post()
1016 */
1017 static function _is_page()
1018 {
1019 if ('page' == WPAlchemy_MetaBox::_is_post_or_page())
1020 {
1021 return TRUE;
1022 }
1023
1024 return FALSE;
1025 }
1026
1027 /**
1028 * Used to check if creating or editing a post or page
1029 *
1030 * @static
1031 * @since 1.3.8
1032 * @access private
1033 * @return string "post" or "page"
1034 * @see _is_post(), _is_page()
1035 */
1036 static function _is_post_or_page()
1037 {
1038 $post_type = WPAlchemy_MetaBox::_get_current_post_type();
1039
1040 if (isset($post_type))
1041 {
1042 if ('page' == $post_type)
1043 {
1044 return 'page';
1045 }
1046 else
1047 {
1048 return 'post';
1049 }
1050 }
1051
1052 return NULL;
1053 }
1054
1055 /**
1056 * Used to check for the current post type, works when creating or editing a
1057 * new post, page or custom post type.
1058 *
1059 * @static
1060 * @since 1.4.6
1061 * @return string [custom_post_type], page or post
1062 */
1063 static function _get_current_post_type()
1064 {
1065 $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : NULL ;
1066
1067 if ( isset( $uri ) )
1068 {
1069 $uri_parts = parse_url($uri);
1070
1071 $file = basename($uri_parts['path']);
1072
1073 if ($uri AND in_array($file, array('post.php', 'post-new.php')))
1074 {
1075 $post_id = WPAlchemy_MetaBox::_get_post_id();
1076
1077 $post_type = isset($_GET['post_type']) ? $_GET['post_type'] : NULL ;
1078
1079 $post_type = $post_id ? get_post_type($post_id) : $post_type ;
1080
1081 if (isset($post_type))
1082 {
1083 return $post_type;
1084 }
1085 else
1086 {
1087 // because of the 'post.php' and 'post-new.php' checks above, we can default to 'post'
1088 return 'post';
1089 }
1090 }
1091 }
1092
1093 return NULL;
1094 }
1095
1096 /**
1097 * Used to get the current post id.
1098 *
1099 * @static
1100 * @since 1.4.8
1101 * @return int post ID
1102 */
1103 static function _get_post_id()
1104 {
1105 global $post;
1106
1107 $p_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : null ;
1108
1109 $g_post_id = isset($_GET['post']) ? $_GET['post'] : null ;
1110
1111 $post_id = $g_post_id ? $g_post_id : $p_post_id ;
1112
1113 $post_id = isset($post->ID) ? $post->ID : $post_id ;
1114
1115 if (isset($post_id))
1116 {
1117 return (integer) $post_id;
1118 }
1119
1120 return null;
1121 }
1122
1123 /**
1124 * @since 1.0
1125 */
1126 function can_output()
1127 {
1128 $post_id = WPAlchemy_MetaBox::_get_post_id();
1129
1130 if (!empty($this->exclude_template) OR !empty($this->include_template))
1131 {
1132 $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
1133 }
1134
1135 if
1136 (
1137 !empty($this->exclude_category) OR
1138 !empty($this->exclude_category_id) OR
1139 !empty($this->include_category) OR
1140 !empty($this->include_category_id)
1141 )
1142 {
1143 $categories = wp_get_post_categories($post_id,'fields=all');
1144 }
1145
1146 if
1147 (
1148 !empty($this->exclude_tag) OR
1149 !empty($this->exclude_tag_id) OR
1150 !empty($this->include_tag) OR
1151 !empty($this->include_tag_id)
1152 )
1153 {
1154 $tags = wp_get_post_tags($post_id);
1155 }
1156
1157 // processing order: "exclude" then "include"
1158 // processing order: "template" then "category" then "post"
1159
1160 $can_output = TRUE; // include all
1161
1162 if
1163 (
1164 !empty($this->exclude_template) OR
1165 !empty($this->exclude_category_id) OR
1166 !empty($this->exclude_category) OR
1167 !empty($this->exclude_tag_id) OR
1168 !empty($this->exclude_tag) OR
1169 !empty($this->exclude_post_id) OR
1170 !empty($this->include_template) OR
1171 !empty($this->include_category_id) OR
1172 !empty($this->include_category) OR
1173 !empty($this->include_tag_id) OR
1174 !empty($this->include_tag) OR
1175 !empty($this->include_post_id)
1176 )
1177 {
1178 if (!empty($this->exclude_template))
1179 {
1180 if (in_array($template_file,$this->exclude_template))
1181 {
1182 $can_output = FALSE;
1183 }
1184 }
1185
1186 if (!empty($this->exclude_category_id))
1187 {
1188 foreach ($categories as $cat)
1189 {
1190 if (in_array($cat->term_id,$this->exclude_category_id))
1191 {
1192 $can_output = FALSE;
1193 break;
1194 }
1195 }
1196 }
1197
1198 if (!empty($this->exclude_category))
1199 {
1200 foreach ($categories as $cat)
1201 {
1202 if
1203 (
1204 in_array($cat->slug,$this->exclude_category) OR
1205 in_array($cat->name,$this->exclude_category)
1206 )
1207 {
1208 $can_output = FALSE;
1209 break;
1210 }
1211 }
1212 }
1213
1214 if (!empty($this->exclude_tag_id))
1215 {
1216 foreach ($tags as $tag)
1217 {
1218 if (in_array($tag->term_id,$this->exclude_tag_id))
1219 {
1220 $can_output = FALSE;
1221 break;
1222 }
1223 }
1224 }
1225
1226 if (!empty($this->exclude_tag))
1227 {
1228 foreach ($tags as $tag)
1229 {
1230 if
1231 (
1232 in_array($tag->slug,$this->exclude_tag) OR
1233 in_array($tag->name,$this->exclude_tag)
1234 )
1235 {
1236 $can_output = FALSE;
1237 break;
1238 }
1239 }
1240 }
1241
1242 if (!empty($this->exclude_post_id))
1243 {
1244 if (in_array($post_id,$this->exclude_post_id))
1245 {
1246 $can_output = FALSE;
1247 }
1248 }
1249
1250 // excludes are not set use "include only" mode
1251
1252 if
1253 (
1254 empty($this->exclude_template) AND
1255 empty($this->exclude_category_id) AND
1256 empty($this->exclude_category) AND
1257 empty($this->exclude_tag_id) AND
1258 empty($this->exclude_tag) AND
1259 empty($this->exclude_post_id)
1260 )
1261 {
1262 $can_output = FALSE;
1263 }
1264
1265 if (!empty($this->include_template))
1266 {
1267 if (in_array($template_file,$this->include_template))
1268 {
1269 $can_output = TRUE;
1270 }
1271 }
1272
1273 if (!empty($this->include_category_id))
1274 {
1275 foreach ($categories as $cat)
1276 {
1277 if (in_array($cat->term_id,$this->include_category_id))
1278 {
1279 $can_output = TRUE;
1280 break;
1281 }
1282 }
1283 }
1284
1285 if (!empty($this->include_category))
1286 {
1287 foreach ($categories as $cat)
1288 {
1289 if
1290 (
1291 in_array($cat->slug,$this->include_category) OR
1292 in_array($cat->name,$this->include_category)
1293 )
1294 {
1295 $can_output = TRUE;
1296 break;
1297 }
1298 }
1299 }
1300
1301 if (!empty($this->include_tag_id))
1302 {
1303 foreach ($tags as $tag)
1304 {
1305 if (in_array($tag->term_id,$this->include_tag_id))
1306 {
1307 $can_output = TRUE;
1308 break;
1309 }
1310 }
1311 }
1312
1313 if (!empty($this->include_tag))
1314 {
1315 foreach ($tags as $tag)
1316 {
1317 if
1318 (
1319 in_array($tag->slug,$this->include_tag) OR
1320 in_array($tag->name,$this->include_tag)
1321 )
1322 {
1323 $can_output = TRUE;
1324 break;
1325 }
1326 }
1327 }
1328
1329 if (!empty($this->include_post_id))
1330 {
1331 if (in_array($post_id,$this->include_post_id))
1332 {
1333 $can_output = TRUE;
1334 }
1335 }
1336 }
1337
1338 $post_type = WPAlchemy_MetaBox::_get_current_post_type();
1339
1340 if (isset($post_type) AND ! in_array($post_type, $this->types))
1341 {
1342 $can_output = FALSE;
1343 }
1344
1345 // filter: output (can_output)
1346 if ($this->has_filter('output'))
1347 {
1348 $can_output = $this->apply_filters('output', $post_id);
1349 }
1350
1351 return $can_output;
1352 }
1353
1354 /**
1355 * Used to insert global STYLE or SCRIPT tags into the head, called on
1356 * WordPress admin_footer action.
1357 *
1358 * @static
1359 * @since 1.3
1360 * @access private
1361 * @see _global_foot()
1362 */
1363 static function _global_head()
1364 {
1365 // must be creating or editing a post or page
1366 if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
1367
1368 // todo: you're assuming people will want to use this exact functionality
1369 // consider giving a developer access to change this via hooks/callbacks
1370
1371 // include javascript for special functionality
1372 ?><style type="text/css"> .wpa_group.tocopy { display:none; } </style>
1373 <script type="text/javascript">
1374 /* <![CDATA[ */
1375 jQuery(function($)
1376 {
1377 $(document).click(function(e)
1378 {
1379 var elem = $(e.target);
1380
1381 if (elem.attr('class') && elem.filter('[class*=dodelete]').length)
1382 {
1383 e.preventDefault();
1384
1385 var p = elem.parents('.wpa_group:first');
1386
1387 if(p.length <= 0)
1388 p = elem.parents('.postbox'); /*wp*/
1389
1390 var the_name = elem.attr('class').match(/dodelete-([a-zA-Z0-9_-]*)/i);
1391
1392 the_name = (the_name && the_name[1]) ? the_name[1] : null ;
1393
1394 /* todo: expose and allow editing of this message */
1395 if (confirm('This action can not be undone, are you sure?'))
1396 {
1397 if (the_name)
1398 {
1399 $('.wpa_group-'+ the_name, p).not('.tocopy').remove();
1400 }
1401 else
1402 {
1403 elem.parents('.wpa_group:first').remove();
1404 }
1405
1406 if(!the_name)
1407 {
1408 var the_group = elem.parents('.wpa_group');
1409 if(the_group && the_group.attr('class'))
1410 {
1411 the_name = the_group.attr('class').match(/wpa_group-([a-zA-Z0-9_-]*)/i);
1412 the_name = (the_name && the_name[1]) ? the_name[1] : null ;
1413 }
1414 }
1415 checkLoopLimit(the_name);
1416
1417 $.wpalchemy.trigger('wpa_delete');
1418 }
1419 }
1420 });
1421
1422 $(document).on('click', '[class*=docopy-]', function(e)
1423 {
1424 e.preventDefault();
1425
1426 var p = $(this).parents('.wpa_group:first');
1427
1428 if(p.length <= 0)
1429 p = $(this).parents('.postbox'); /*wp*/
1430
1431 var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
1432
1433 var the_group = $('.wpa_group-'+ the_name +'.tocopy', p).first();
1434
1435 var the_clone = the_group.clone().removeClass('tocopy last');
1436
1437 var the_props = ['name', 'id', 'for', 'class'];
1438
1439 the_group.find('*').each(function(i, elem)
1440 {
1441 for (var j = 0; j < the_props.length; j++)
1442 {
1443 var the_prop = $(elem).attr(the_props[j]);
1444
1445 if (the_prop)
1446 {
1447 var reg = new RegExp('\\['+the_name+'\\]\\[(\\d+)\\]', 'i');
1448 var the_match = the_prop.match(reg);
1449
1450 if (the_match)
1451 {
1452 the_prop = the_prop.replace(the_match[0], '['+ the_name + ']' + '['+ (+the_match[1]+1) +']');
1453
1454 $(elem).attr(the_props[j], the_prop);
1455 }
1456
1457 the_match = null;
1458
1459 // todo: this may prove to be too broad of a search
1460 the_match = the_prop.match(/n(\d+)/i);
1461
1462 if (the_match)
1463 {
1464 the_prop = the_prop.replace(the_match[0], 'n' + (+the_match[1]+1));
1465
1466 $(elem).attr(the_props[j], the_prop);
1467 }
1468 }
1469 }
1470 });
1471
1472 // increment the group id
1473 var reg = new RegExp('\\[(\\d+)\\]$', 'i');
1474 var the_id = the_group.attr("id");
1475 var the_match = the_id.match(reg);
1476 if (the_match)
1477 {
1478 the_group.attr("id", the_id.replace(the_match[0], '['+ (+the_match[1]+1) +']'));
1479 }
1480
1481 if ($(this).hasClass('ontop'))
1482 {
1483 $('.wpa_group-'+ the_name, p).first().before(the_clone);
1484 }
1485 else
1486 {
1487 the_group.before(the_clone);
1488 }
1489
1490 checkLoopLimit(the_name);
1491
1492 $.wpalchemy.trigger('wpa_copy', [the_clone]);
1493 });
1494
1495 function checkLoopLimit(name)
1496 {
1497 var elems = $('.docopy-' + name);
1498
1499 $.each(elems, function(idx, elem){
1500
1501 var p = $(this).parents('.wpa_group:first');
1502
1503 if(p.length <= 0)
1504 p = $(this).parents('.postbox'); /*wp*/
1505
1506 var the_class = $('.wpa_loop-' + name, p).attr('class');
1507
1508 if (the_class)
1509 {
1510 var the_match = the_class.match(/wpa_loop_limit-([0-9]*)/i);
1511
1512 if (the_match)
1513 {
1514 var the_limit = the_match[1];
1515
1516 if ($('.wpa_group-' + name, p).not('.wpa_group.tocopy').length >= the_limit)
1517 {
1518 $(this).hide();
1519 }
1520 else
1521 {
1522 $(this).show();
1523 }
1524 }
1525 }
1526
1527 });
1528
1529 }
1530
1531 /* do an initial limit check, show or hide buttons */
1532 $('[class*=docopy-]').each(function()
1533 {
1534 var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
1535
1536 checkLoopLimit(the_name);
1537 });
1538 });
1539 /* ]]> */
1540 </script>
1541 <?php
1542 }
1543
1544 /**
1545 * Used to insert global SCRIPT tags into the footer, called on WordPress
1546 * admin_footer action.
1547 *
1548 * @static
1549 * @since 1.3
1550 * @access private
1551 * @see _global_head()
1552 */
1553 static function _global_foot()
1554 {
1555 // must be creating or editing a post or page
1556 if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
1557
1558 ?>
1559 <script type="text/javascript">
1560 /* <![CDATA[ */
1561 (function($){ /* not using jQuery ondomready, code runs right away in footer */
1562
1563 /* use a global dom element to attach events to */
1564 $.wpalchemy = $('<div></div>').attr('id','wpalchemy').appendTo('body');
1565
1566 })(jQuery);
1567 /* ]]> */
1568 </script>
1569 <?php
1570 }
1571
1572 /**
1573 * Gets the meta data for a meta box
1574 *
1575 * @since 1.0
1576 * @access public
1577 * @param int $post_id optional post ID for which to retrieve the meta data
1578 * @return array
1579 * @see _meta
1580 */
1581 function the_meta($post_id = NULL)
1582 {
1583 return $this->_meta($post_id);
1584 }
1585
1586 /**
1587 * Gets the meta data for a meta box
1588 *
1589 * Internal method calls will typically bypass the data retrieval and will
1590 * immediately return the current meta data
1591 *
1592 * @since 1.3
1593 * @access private
1594 * @param int $post_id optional post ID for which to retrieve the meta data
1595 * @param bool $internal optional boolean if internally calling
1596 * @return array
1597 * @see the_meta()
1598 */
1599 function _meta($post_id = NULL, $internal = FALSE)
1600 {
1601 if ( ! is_numeric($post_id))
1602 {
1603 if ($internal AND $this->current_post_id)
1604 {
1605 $post_id = $this->current_post_id;
1606 }
1607 else
1608 {
1609 global $post;
1610
1611 $post_id = $post->ID;
1612 }
1613 }
1614
1615 // this allows multiple internal calls to _meta() without having to fetch data everytime
1616 if ($internal AND !empty($this->meta) AND $this->current_post_id == $post_id) return $this->meta;
1617
1618 $this->current_post_id = $post_id;
1619
1620 // WPALCHEMY_MODE_ARRAY
1621
1622 $meta = get_post_meta($post_id, $this->id, TRUE);
1623
1624 // var_dump($meta);
1625
1626 // WPALCHEMY_MODE_EXTRACT
1627
1628 $fields = get_post_meta($post_id, $this->id . '_fields', TRUE);
1629
1630 if ( ! empty($fields) AND is_array($fields))
1631 {
1632 $meta = array();
1633
1634 foreach ($fields as $field)
1635 {
1636 $field_noprefix = preg_replace('/^' . $this->prefix . '/i', '', $field);
1637 $meta[$field_noprefix] = get_post_meta($post_id, $field, TRUE);
1638 }
1639 }
1640
1641 $this->meta = $meta;
1642
1643 return $this->meta;
1644 }
1645
1646 // user can also use the_ID(), php functions are case-insensitive
1647 /**
1648 * @since 1.0
1649 * @access public
1650 */
1651 function the_id()
1652 {
1653 echo $this->get_the_id();
1654 }
1655
1656 /**
1657 * @since 1.0
1658 * @access public
1659 */
1660 function get_the_id()
1661 {
1662 return $this->id;
1663 }
1664
1665 /**
1666 * @since 1.0
1667 * @access public
1668 */
1669 function the_field($n, $hint = NULL)
1670 {
1671 $this->name = $n;
1672 $this->hint = $hint;
1673 }
1674
1675 /**
1676 * @since 1.0
1677 * @access public
1678 */
1679 function have_value($n = NULL)
1680 {
1681 if ($this->get_the_value($n)) return TRUE;
1682
1683 return FALSE;
1684 }
1685
1686 /**
1687 * @since 1.0
1688 * @access public
1689 */
1690 function the_value($n = NULL)
1691 {
1692 echo $this->get_the_value($n);
1693 }
1694
1695 function get_the_value($n = NULL, $collection = FALSE)
1696 {
1697 $this->_meta(NULL, TRUE);
1698
1699 $value = null;
1700
1701 if ($this->is_in_loop())
1702 {
1703 $n = is_null($n) ? $this->name : $n ;
1704
1705 if(!is_null($n))
1706 {
1707 if ($collection)
1708 {
1709 $keys = $this->get_the_loop_group_name_array();
1710 }
1711 else
1712 {
1713 $keys = $this->get_the_loop_group_name_array();
1714 $keys[] = $n;
1715 }
1716 }
1717 else
1718 {
1719 if ($collection)
1720 {
1721 $keys = $this->get_the_loop_group_name_array();
1722 end($keys);
1723 $last = key($keys);
1724 unset($keys[$last]);
1725 }
1726 else
1727 {
1728 $keys = $this->get_the_loop_group_name_array();
1729 }
1730 }
1731 $value = $this->get_meta_by_array($keys);
1732 }
1733 else
1734 {
1735 $n = is_null($n) ? $this->name : $n ;
1736
1737 if(isset($this->meta[$n]))
1738 {
1739 $value = $this->meta[$n];
1740 }
1741 }
1742
1743 if (is_string($value) || is_numeric($value))
1744 {
1745 if ($this->in_template)
1746 {
1747 return $value;
1748 }
1749 else
1750 {
1751 // http://wordpress.org/support/topic/call-function-called-by-embed-shortcode-direct
1752 // http://phpdoc.wordpress.org/trunk/WordPress/Embed/WP_Embed.html#run_shortcode
1753
1754 global $wp_embed;
1755
1756 return do_shortcode($wp_embed->run_shortcode($value));
1757 }
1758 }
1759 else
1760 {
1761 // value can sometimes be an array
1762 return $value;
1763 }
1764 }
1765
1766 /**
1767 * @since 1.0
1768 * @access public
1769 */
1770 function the_name($n = NULL)
1771 {
1772 echo $this->get_the_name($n);
1773 }
1774
1775 /**
1776 * @since 1.0
1777 * @access public
1778 */
1779 function get_the_name($n = NULL)
1780 {
1781 if (!$this->in_template AND $this->mode == WPALCHEMY_MODE_EXTRACT)
1782 {
1783 return $this->prefix . str_replace($this->prefix, '', is_null($n) ? $this->name : $n);
1784 }
1785
1786 if ($this->is_in_loop())
1787 {
1788 $n = is_null($n) ? $this->name : $n ;
1789
1790 if (!is_null($n))
1791 $the_field = $this->get_the_loop_group_name(true) . '[' . $n . ']' ;
1792 else
1793 $the_field = $this->get_the_loop_group_name(true);
1794 }
1795 else
1796 {
1797 $n = is_null($n) ? $this->name : $n ;
1798
1799 $the_field = $this->id . '[' . $n . ']';
1800 }
1801
1802 $hints = array
1803 (
1804 WPALCHEMY_FIELD_HINT_CHECKBOX_MULTI,
1805 WPALCHEMY_FIELD_HINT_SELECT_MULTI,
1806 WPALCHEMY_FIELD_HINT_SELECT_MULTIPLE,
1807 );
1808
1809 if (in_array($this->hint, $hints))
1810 {
1811 $the_field .= '[]';
1812 }
1813
1814 return $the_field;
1815 }
1816
1817 /**
1818 * @since 1.1
1819 * @access public
1820 */
1821 function the_index()
1822 {
1823 echo $this->get_the_index();
1824 }
1825
1826 /**
1827 * @since 1.1
1828 * @access public
1829 */
1830 function get_the_index()
1831 {
1832 return $this->in_loop ? $this->get_the_current_group_current() : 0 ;
1833 }
1834
1835 /**
1836 * @since 1.0
1837 * @access public
1838 */
1839 function is_first()
1840 {
1841 if ($this->in_loop AND $this->current == 0) return TRUE;
1842
1843 return FALSE;
1844 }
1845
1846 /**
1847 * @since 1.0
1848 * @access public
1849 */
1850 function is_last()
1851 {
1852 if ($this->in_loop AND ($this->current+1) == $this->length) return TRUE;
1853
1854 return FALSE;
1855 }
1856
1857 /**
1858 * Used to check if a value is a match
1859 *
1860 * @since 1.1
1861 * @access public
1862 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1863 * @param string $v optional the value to check for
1864 * @return bool
1865 * @see is_value()
1866 */
1867 function is_value($n, $v = NULL)
1868 {
1869 if (is_null($v))
1870 {
1871 $the_value = $this->get_the_value();
1872
1873 $v = $n;
1874 }
1875 else
1876 {
1877 $the_value = $this->get_the_value($n);
1878 }
1879
1880 if($v == $the_value) return TRUE;
1881
1882 return FALSE;
1883 }
1884
1885 /**
1886 * Used to check if a value is selected, useful when working with checkbox,
1887 * radio and select values.
1888 *
1889 * @since 1.3
1890 * @access public
1891 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1892 * @param string $v optional the value to check for
1893 * @return bool
1894 * @see is_value()
1895 */
1896 function is_selected($n, $v = NULL)
1897 {
1898 if (is_null($v))
1899 {
1900 $the_value = $this->get_the_value(NULL);
1901
1902 $v = $n;
1903 }
1904 else
1905 {
1906 $the_value = $this->get_the_value($n);
1907 }
1908
1909 if (is_array($the_value))
1910 {
1911 if (in_array($v, $the_value)) return TRUE;
1912 }
1913 elseif($v == $the_value)
1914 {
1915 return TRUE;
1916 }
1917
1918 return FALSE;
1919 }
1920
1921 /**
1922 * Prints the current state of a checkbox field and should be used inline
1923 * within the INPUT tag.
1924 *
1925 * @since 1.3
1926 * @access public
1927 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1928 * @param string $v optional the value to check for
1929 * @see get_the_checkbox_state()
1930 */
1931 function the_checkbox_state($n, $v = NULL)
1932 {
1933 echo $this->get_the_checkbox_state($n, $v);
1934 }
1935
1936 /**
1937 * Returns the current state of a checkbox field, the returned string is
1938 * suitable to be used inline within the INPUT tag.
1939 *
1940 * @since 1.3
1941 * @access public
1942 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1943 * @param string $v optional the value to check for
1944 * @return string suitable to be used inline within the INPUT tag
1945 * @see the_checkbox_state()
1946 */
1947 function get_the_checkbox_state($n, $v = NULL)
1948 {
1949 if ($this->is_selected($n, $v)) return ' checked="checked"';
1950 }
1951
1952 /**
1953 * Prints the current state of a radio field and should be used inline
1954 * within the INPUT tag.
1955 *
1956 * @since 1.3
1957 * @access public
1958 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1959 * @param string $v optional the value to check for
1960 * @see get_the_radio_state()
1961 */
1962 function the_radio_state($n, $v = NULL)
1963 {
1964 echo $this->get_the_checkbox_state($n, $v);
1965 }
1966
1967 /**
1968 * Returns the current state of a radio field, the returned string is
1969 * suitable to be used inline within the INPUT tag.
1970 *
1971 * @since 1.3
1972 * @access public
1973 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1974 * @param string $v optional the value to check for
1975 * @return string suitable to be used inline within the INPUT tag
1976 * @see the_radio_state()
1977 */
1978 function get_the_radio_state($n, $v = NULL)
1979 {
1980 return $this->get_the_checkbox_state($n, $v);
1981 }
1982
1983 /**
1984 * Prints the current state of a select field and should be used inline
1985 * within the SELECT tag.
1986 *
1987 * @since 1.3
1988 * @access public
1989 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1990 * @param string $v optional the value to check for
1991 * @see get_the_select_state()
1992 */
1993 function the_select_state($n, $v = NULL)
1994 {
1995 echo $this->get_the_select_state($n, $v);
1996 }
1997
1998 /**
1999 * Returns the current state of a select field, the returned string is
2000 * suitable to be used inline within the SELECT tag.
2001 *
2002 * @since 1.3
2003 * @access public
2004 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
2005 * @param string $v optional the value to check for
2006 * @return string suitable to be used inline within the SELECT tag
2007 * @see the_select_state()
2008 */
2009 function get_the_select_state($n, $v = NULL)
2010 {
2011 if ($this->is_selected($n, $v)) return ' selected="selected"';
2012 }
2013
2014 /**
2015 * @since 1.1
2016 * @access public
2017 */
2018 function the_group_open($t = 'div')
2019 {
2020 echo $this->get_the_group_open($t);
2021 }
2022
2023 /**
2024 * @since 1.1
2025 * @access public
2026 */
2027 function get_the_group_open($t = 'div')
2028 {
2029 $this->group_tag = $t;
2030
2031 $curr_loop = $this->get_the_current_loop();
2032 $the_name = $curr_loop->name;
2033
2034 $loop_open = NULL;
2035
2036 $loop_open_classes = array('wpa_loop', 'wpa_loop-' . $the_name);
2037
2038 $css_class = array('wpa_group', 'wpa_group-'. $the_name);
2039
2040
2041 if ($curr_loop->is_first())
2042 {
2043 array_push($css_class, 'first');
2044
2045 $loop_open = '<div class="wpa_loop">';
2046
2047 if (isset($this->_loop_data->limit))
2048 {
2049 array_push($loop_open_classes, 'wpa_loop_limit-' . $this->_loop_data->limit);
2050 }
2051
2052 $loop_open = '<div id="wpa_loop-'. $the_name .'" class="' . implode(' ', $loop_open_classes) . '">';
2053 }
2054
2055 if ($curr_loop->is_last())
2056 {
2057 array_push($css_class, 'last');
2058
2059 if ($this->in_loop == 'multi')
2060 {
2061 array_push($css_class, 'tocopy');
2062 }
2063 }
2064
2065 return $loop_open . '<' . $t . ' class="'. implode(' ', $css_class) . '">';
2066 }
2067
2068 /**
2069 * @since 1.1
2070 * @access public
2071 */
2072 function the_group_close()
2073 {
2074 echo $this->get_the_group_close();
2075 }
2076
2077 /**
2078 * @since 1.1
2079 * @access public
2080 */
2081 function get_the_group_close()
2082 {
2083 $loop_close = NULL;
2084
2085 $curr_loop = $this->get_the_current_loop();
2086
2087 if ($curr_loop->is_last())
2088 {
2089 $loop_close = '</div>';
2090 }
2091
2092 return '</' . $this->group_tag . '>' . $loop_close;
2093 }
2094
2095 /**
2096 * @since 1.1
2097 * @access public
2098 */
2099 function have_fields_and_multi($n, $options = NULL)
2100 {
2101 if (is_array($options))
2102 {
2103 // use as stdClass object
2104 $options = (object)$options;
2105
2106 $length = @$options->length;
2107
2108 $this->_loop_data->limit = @$options->limit;
2109 }
2110 else
2111 {
2112 // backward compatibility (bc)
2113 $length = $options;
2114 }
2115
2116 $this->_meta(NULL, TRUE);
2117
2118 $this->in_loop = 'multi';
2119
2120 // push new loop or set loop to current name
2121 $this->push_or_set_current_loop($n, $length, $this->in_loop);
2122
2123 return $this->_loop($n, $length, 2);
2124 }
2125
2126 /**
2127 * @deprecated
2128 * @since 1.0
2129 * @access public
2130 */
2131 function have_fields_and_one($n)
2132 {
2133 $this->_meta(NULL, TRUE);
2134 $this->in_loop = 'single';
2135 $this->push_or_set_current_loop($n, $length, $this->in_loop);
2136 return $this->_loop($n,NULL,1);
2137 }
2138
2139 /**
2140 * @since 1.0
2141 * @access public
2142 */
2143 function have_fields($n,$length=NULL)
2144 {
2145 $this->_meta(NULL, TRUE);
2146 // push new loop or set loop to current name
2147 $this->in_loop = 'normal';
2148 $this->push_or_set_current_loop($n, $length, $this->in_loop);
2149 return $this->_loop($n,$length);
2150 }
2151
2152 /**
2153 * @since 1.0
2154 * @access private
2155 */
2156 function _loop($n,$length=NULL,$and_one=0)
2157 {
2158 if (!$this->in_loop)
2159 {
2160 $this->in_loop = TRUE;
2161 }
2162
2163 $cnt = $this->get_the_current_group_count();
2164
2165 $length = is_null($length) ? $cnt : $length ;
2166
2167 if ($this->in_loop == 'multi' AND $cnt > $length) $length = $cnt;
2168
2169 $this->length = $length;
2170
2171 if ($this->in_template AND $and_one)
2172 {
2173 if ($length == 0)
2174 {
2175 $this->length = $and_one;
2176 }
2177 else
2178 {
2179 $this->length = $length+1;
2180 }
2181 }
2182
2183 $this->set_the_current_group_length($this->length);
2184 $this->increment_current_loop();
2185 $this->current++;
2186
2187 if ($this->get_the_current_group_current() < $this->get_the_current_group_length())
2188 {
2189 $this->name = NULL;
2190 $this->fieldtype = NULL;
2191
2192 return TRUE;
2193 }
2194 else if ($this->get_the_current_group_current() == $this->get_the_current_group_length())
2195 {
2196 $this->name = NULL;
2197 $this->set_the_current_group_current(-1);
2198 $this->prev_loop();
2199 }
2200
2201 $this->in_loop = FALSE;
2202
2203 $this->_loop_data = new stdClass;
2204
2205 return FALSE;
2206 }
2207
2208 /**
2209 * @since 1.0
2210 * @access private
2211 */
2212 function _save($post_id)
2213 {
2214 /**
2215 * note: the "save_post" action fires for saving revisions and post/pages,
2216 * when saving a post this function fires twice, once for a revision save,
2217 * and again for the post/page save ... the $post_id is different for the
2218 * revision save, this means that "get_post_meta()" will not work if trying
2219 * to get values for a revision (as it has no post meta data)
2220 * see http://alexking.org/blog/2008/09/06/wordpress-26x-duplicate-custom-field-issue
2221 *
2222 * why let the code run twice? wordpress does not currently save post meta
2223 * data per revisions (I think it should, so users can do a complete revert),
2224 * so in the case that this functionality changes, let it run twice
2225 */
2226
2227 $real_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : NULL ;
2228
2229 // check autosave
2230 if (defined('DOING_AUTOSAVE') AND DOING_AUTOSAVE AND !$this->autosave) return $post_id;
2231
2232 // make sure data came from our meta box, verify nonce
2233 $nonce = isset($_POST[$this->id.'_nonce']) ? $_POST[$this->id.'_nonce'] : NULL ;
2234 if (!wp_verify_nonce($nonce, $this->id)) return $post_id;
2235
2236 // check user permissions
2237 if ($_POST['post_type'] == 'page')
2238 {
2239 if (!current_user_can('edit_page', $post_id)) return $post_id;
2240 }
2241 else
2242 {
2243 if (!current_user_can('edit_post', $post_id)) return $post_id;
2244 }
2245
2246 // authentication passed, save data
2247
2248 $new_data = isset( $_POST[$this->id] ) ? $_POST[$this->id] : NULL ;
2249
2250 WPAlchemy_MetaBox::clean($new_data);
2251
2252 if (empty($new_data))
2253 {
2254 $new_data = NULL;
2255 }
2256
2257 // filter: save
2258 if ($this->has_filter('save'))
2259 {
2260 $new_data = $this->apply_filters('save', $new_data, $real_post_id);
2261
2262 /**
2263 * halt saving
2264 * @since 1.3.4
2265 */
2266 if (FALSE === $new_data) return $post_id;
2267
2268 WPAlchemy_MetaBox::clean($new_data);
2269 }
2270
2271 // get current fields, use $real_post_id (checked for in both modes)
2272 $current_fields = get_post_meta($real_post_id, $this->id . '_fields', TRUE);
2273
2274 if ($this->mode == WPALCHEMY_MODE_EXTRACT)
2275 {
2276 $new_fields = array();
2277
2278 if (is_array($new_data))
2279 {
2280 foreach ($new_data as $k => $v)
2281 {
2282 $field = $this->prefix . $k;
2283
2284 array_push($new_fields,$field);
2285
2286 $new_value = $new_data[$k];
2287
2288 if (is_null($new_value))
2289 {
2290 delete_post_meta($post_id, $field);
2291 }
2292 else
2293 {
2294 update_post_meta($post_id, $field, $new_value);
2295 }
2296 }
2297 }
2298
2299 $diff_fields = array_diff((array)$current_fields,$new_fields);
2300
2301 if (is_array($diff_fields))
2302 {
2303 foreach ($diff_fields as $field)
2304 {
2305 delete_post_meta($post_id,$field);
2306 }
2307 }
2308
2309 delete_post_meta($post_id, $this->id . '_fields');
2310
2311 if ( ! empty($new_fields))
2312 {
2313 add_post_meta($post_id,$this->id . '_fields', $new_fields, TRUE);
2314 }
2315
2316 // keep data tidy, delete values if previously using WPALCHEMY_MODE_ARRAY
2317 delete_post_meta($post_id, $this->id);
2318 }
2319 else
2320 {
2321 if (is_null($new_data))
2322 {
2323 delete_post_meta($post_id, $this->id);
2324 }
2325 else
2326 {
2327 update_post_meta($post_id, $this->id, $new_data);
2328 }
2329
2330 // keep data tidy, delete values if previously using WPALCHEMY_MODE_EXTRACT
2331 if (is_array($current_fields))
2332 {
2333 foreach ($current_fields as $field)
2334 {
2335 delete_post_meta($post_id, $field);
2336 }
2337
2338 delete_post_meta($post_id, $this->id . '_fields');
2339 }
2340 }
2341
2342 // action: save
2343 if ($this->has_action('save'))
2344 {
2345 $this->do_action('save', $new_data, $real_post_id);
2346 }
2347
2348 return $post_id;
2349 }
2350
2351 /**
2352 * Cleans an array, removing blank ('') values
2353 *
2354 * @static
2355 * @since 1.0
2356 * @access public
2357 * @param array the array to clean (passed by reference)
2358 */
2359 static function clean(&$arr)
2360 {
2361 if (is_array($arr))
2362 {
2363 foreach ($arr as $i => $v)
2364 {
2365 if (is_array($arr[$i]))
2366 {
2367 WPAlchemy_MetaBox::clean($arr[$i]);
2368
2369 if (!count($arr[$i]))
2370 {
2371 unset($arr[$i]);
2372 }
2373 }
2374 else
2375 {
2376 if ('' == trim($arr[$i]) OR is_null($arr[$i]))
2377 {
2378 unset($arr[$i]);
2379 }
2380 }
2381 }
2382
2383 if (!count($arr))
2384 {
2385 $arr = array();
2386 }
2387 else
2388 {
2389 $keys = array_keys($arr);
2390
2391 $is_numeric = TRUE;
2392
2393 foreach ($keys as $key)
2394 {
2395 if (!is_numeric($key))
2396 {
2397 $is_numeric = FALSE;
2398 break;
2399 }
2400 }
2401
2402 if ($is_numeric)
2403 {
2404 $arr = array_values($arr);
2405 }
2406 }
2407 }
2408 }
2409
2410 /**
2411 * Other than core modifications, here is the loop stack function and class to support
2412 * nested repeatable group.
2413 * author Vafpress
2414 */
2415
2416 var $_loop_stack = array();
2417
2418 function push_loop($name, $length, $type)
2419 {
2420 $loop = new WPA_Loop($name, $length, $type);
2421 $parent = $this->get_the_current_loop();
2422 if($parent)
2423 $loop->parent = $parent->name;
2424 else
2425 $loop->parent = false;
2426 $this->_loop_stack[$name] = $loop;
2427 return $loop;
2428 }
2429
2430 function push_or_set_current_loop($name, $length, $type)
2431 {
2432 if( !array_key_exists( $name, $this->_loop_stack ) )
2433 {
2434 $this->push_loop($name, $length, $type);
2435 }
2436
2437 $this->set_current_loop($name);
2438 }
2439
2440 function set_current_loop($name)
2441 {
2442 reset($this->_loop_stack);
2443 if(!array_key_exists($name, $this->_loop_stack)){
2444 return;
2445 }
2446 while(key($this->_loop_stack) !== $name)
2447 next($this->_loop_stack);
2448 }
2449
2450 function next_loop()
2451 {
2452 return next($this->_loop_stack);
2453 }
2454
2455 function prev_loop()
2456 {
2457 $parent = $this->get_the_current_loop()->parent;
2458 if($parent)
2459 {
2460 $this->set_current_loop($parent);
2461 }
2462 else
2463 {
2464 $this->_loop_stack = array();
2465 return false;
2466 }
2467 }
2468
2469 function get_the_current_group_length()
2470 {
2471 return current($this->_loop_stack)->length;
2472 }
2473
2474 function get_the_current_group_current()
2475 {
2476 return current($this->_loop_stack)->current;
2477 }
2478
2479 function set_the_current_group_length($length)
2480 {
2481 current($this->_loop_stack)->length = $length;
2482 }
2483
2484 function set_the_current_group_current($current)
2485 {
2486 current($this->_loop_stack)->current = $current;
2487 }
2488
2489 function get_the_loop_collection($name = null)
2490 {
2491 $collection = array();
2492
2493 if(is_null($name))
2494 {
2495 $curr = $this->get_the_current_loop();
2496 if($curr)
2497 {
2498 $name = $curr->name;
2499 $loop_stack = $this->_loop_stack;
2500 $loop = $loop_stack[$name];
2501 $collection[] = $loop;
2502 while ($loop)
2503 {
2504 $collection[] = $loop;
2505 if($loop->parent)
2506 $loop = $loop_stack[$loop->parent];
2507 else
2508 $loop = false;
2509 }
2510 $collection = array_reverse($collection);
2511 }
2512 }
2513
2514 return $collection;
2515 }
2516
2517 function get_the_loop_group_name($with_id = false)
2518 {
2519 $loop_name = $with_id ? $this->id : '';
2520 $curr = $this->get_the_current_loop();
2521
2522 // copy _loop_stack to prevent internal pointer ruined
2523 $loop_stack = $this->get_the_loop_collection();
2524 // print_r($loop_stack);
2525 foreach ($loop_stack as $loop)
2526 {
2527 $loop_name .= '[' . $loop->name . '][' . $loop->current . ']';
2528
2529 if($loop->name === $curr->name)
2530 break;
2531 }
2532 return $loop_name;
2533 }
2534
2535 function get_the_loop_level()
2536 {
2537 $curr = $this->get_the_current_loop();
2538 $depth = 0;
2539
2540 // copy _loop_stack to prevent internal pointer ruined
2541 $loop_stack = $this->get_the_loop_collection();
2542 foreach ($loop_stack as $loop)
2543 {
2544 if($loop->name === $curr->name)
2545 break;
2546 $depth++;
2547 }
2548 return $depth;
2549 }
2550
2551 function get_the_loop_group_id()
2552 {
2553 $loop_name = '';
2554 $curr = $this->get_the_current_loop();
2555
2556 // copy _loop_stack to prevent internal pointer ruined
2557 $loop_stack = $this->get_the_loop_collection();
2558 foreach ($loop_stack as $key => $loop)
2559 {
2560 $is_first = false;
2561 $is_last = false;
2562
2563 reset($loop_stack);
2564 if ($key === key($loop_stack))
2565 $is_first = true;
2566 if ($loop->name === $curr->name)
2567 $is_last = true;
2568
2569 $loop_name .= '[' . $loop->name . ']';
2570
2571 if(!$is_last)
2572 $loop_name .= '[' . $loop->current . ']';
2573
2574 if($loop->name === $curr->name)
2575 break;
2576 }
2577 return $loop_name;
2578 }
2579
2580 function get_the_loop_group_name_array($with_id = false)
2581 {
2582 $loop_name = array();
2583 $curr = $this->get_the_current_loop();
2584
2585 if($with_id)
2586 {
2587 $loop_name[] = $this->id;
2588 }
2589
2590 // copy _loop_stack to prevent internal pointer ruined
2591 $loop_stack = $this->get_the_loop_collection();
2592 foreach ($loop_stack as $loop)
2593 {
2594 $loop_name[] = $loop->name;
2595 $loop_name[] = $loop->current;
2596
2597 if($loop->name === $curr->name)
2598 break;
2599 }
2600 return $loop_name;
2601 }
2602
2603 function get_the_dotted_loop_group_name($with_id = false)
2604 {
2605 $loop_name = $with_id ? $this->id : '';
2606 $curr = $this->get_the_current_loop();
2607
2608 // copy _loop_stack to prevent internal pointer ruined
2609 $loop_stack = $this->get_the_loop_collection();
2610 foreach ($loop_stack as $loop)
2611 {
2612 $loop_name .= ($loop_name === '' ? '' : '.') . $loop->name . '.' . $loop->current;
2613
2614 if($loop->name === $curr->name)
2615 break;
2616 }
2617 return $loop_name;
2618 }
2619
2620 function get_meta_by_dotted($dotted)
2621 {
2622 $keys = explode('.', $dotted);
2623 $meta = $this->meta;
2624 foreach ($keys as $key)
2625 {
2626 if(array_key_exists($key, $meta))
2627 {
2628 $meta = $meta[$key];
2629 }
2630 else
2631 {
2632 return null;
2633 }
2634 }
2635 return $meta;
2636 }
2637
2638 function get_meta_by_array($arr)
2639 {
2640 $meta = $this->meta;
2641
2642 if(!is_array($arr) || !is_array($meta) || is_null($meta))
2643 return null;
2644
2645 foreach ($arr as $key)
2646 {
2647 if(is_array($meta) and array_key_exists($key, $meta))
2648 {
2649 $meta = $meta[$key];
2650 }
2651 else
2652 {
2653 return null;
2654 }
2655 }
2656 return $meta;
2657 }
2658
2659 function get_the_current_group_count()
2660 {
2661 $arr = $this->get_the_loop_group_name_array();
2662 end($arr);
2663 $last = key($arr);
2664 unset($arr[$last]);
2665 $meta = $this->get_meta_by_array($arr);
2666 return count($meta);
2667 }
2668
2669 function increment_current_loop()
2670 {
2671 current($this->_loop_stack)->current++;
2672 }
2673
2674 function get_the_current_loop()
2675 {
2676 return current($this->_loop_stack);
2677 }
2678
2679 function is_in_multi_last()
2680 {
2681 // copy _loop_stack to prevent internal pointer ruined
2682 $loop_stack = $this->get_the_loop_collection();
2683 foreach ($loop_stack as $loop)
2684 {
2685 if($loop->type === 'multi' and $loop->is_last())
2686 return true;
2687 }
2688 return false;
2689 }
2690
2691 function is_in_loop()
2692 {
2693 if(current($this->_loop_stack) === false)
2694 return false;
2695 return true;
2696 }
2697
2698 function is_parent_multi()
2699 {
2700 // copy _loop_stack to prevent internal pointer ruined
2701 $loop_stack = $this->get_the_loop_collection();
2702 foreach ($loop_stack as $loop)
2703 {
2704 if($loop->type === 'multi')
2705 return true;
2706 }
2707 return false;
2708 }
2709
2710 function the_copy_button_class()
2711 {
2712 $curr = $this->get_the_current_loop();
2713 return 'docopy-' . $curr->get_the_indexed_name();
2714 }
2715
2716 }
2717
2718 class WPA_Loop
2719 {
2720
2721 public $length = 0;
2722
2723 public $parent = NULL;
2724
2725 public $current = -1;
2726
2727 public $name = NULL;
2728
2729 public $type = false;
2730
2731 function __construct($name, $length, $type)
2732 {
2733 $this->name = $name;
2734 $this->length = $length;
2735 $this->type = $type;
2736 }
2737
2738 function the_indexed_name()
2739 {
2740 echo $this->get_the_indexed_name();
2741 }
2742
2743 function get_the_indexed_name()
2744 {
2745 return $this->name . '[' . $this->current . ']';
2746 }
2747
2748 function is_first()
2749 {
2750 if ( $this->current == 0 ) return TRUE;
2751
2752 return FALSE;
2753 }
2754
2755 function is_last()
2756 {
2757 if ( ( $this->current + 1 ) == $this->length ) return TRUE;
2758
2759 return FALSE;
2760 }
2761
2762 }
2763
2764 /* eof */