PluginProbe
WP Ultimate Post Grid / 1.7.2
WP Ultimate Post Grid v1.7.2
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 1.7.2, at vendor/vafpress/includes/wpalchemy/MetaBox.php

2,755 lines 58.4 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 WPAlchemy_MetaBox($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 $value = maybe_unserialize( preg_replace( '!s:(\d+):"(.*?)";!es', "'s:'.strlen('$2').':\"$2\";'", stripslashes( $meta['value'] ) ) );
546
547 update_post_meta( $post_id, $key, $value );
548 }
549 }
550 }
551 }
552 }
553 }
554
555 /**
556 * Used to initialize the meta box, runs on WordPress admin_init action,
557 * properly calls internal WordPress methods
558 *
559 * @since 1.0
560 * @access private
561 */
562 function _init()
563 {
564 // must be creating or editing a post or page
565 if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
566
567 if ( ! empty($this->output_filter))
568 {
569 $this->add_filter('output', $this->output_filter);
570 }
571
572 if ($this->can_output())
573 {
574 foreach ($this->types as $type)
575 {
576 add_meta_box($this->id . '_metabox', $this->title, array($this, '_setup'), $type, $this->context, $this->priority);
577 }
578
579 add_action('save_post', array($this,'_save'));
580
581 $filters = array('save', 'head', 'foot');
582
583 foreach ($filters as $filter)
584 {
585 $var = $filter . '_filter';
586
587 if (!empty($this->$var))
588 {
589 if ('save' == $filter)
590 {
591 $this->add_filter($filter, $this->$var, 10, 2);
592 }
593 else
594 {
595 $this->add_filter($filter, $this->$var);
596 }
597 }
598 }
599
600 $actions = array('save', 'head', 'foot', 'init');
601
602 foreach ($actions as $action)
603 {
604 $var = $action . '_action';
605
606 if (!empty($this->$var))
607 {
608 if ('save' == $action)
609 {
610 $this->add_action($action, $this->$var, 10, 2);
611 }
612 else
613 {
614 $this->add_action($action, $this->$var);
615 }
616 }
617 }
618
619 add_action('admin_head', array($this,'_head'), 11);
620
621 add_action('admin_footer', array($this,'_foot'), 11);
622
623 // action: init
624 if ($this->has_action('init'))
625 {
626 $this->do_action('init');
627 }
628 }
629 }
630
631 /**
632 * Used to insert STYLE or SCRIPT tags into the head, called on WordPress
633 * admin_head action.
634 *
635 * @since 1.3
636 * @access private
637 * @see _foot()
638 */
639 function _head()
640 {
641 $content = NULL;
642
643 ob_start();
644
645 ?>
646 <style type="text/css">
647 <?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 } ?>
648 </style>
649 <?php
650
651 $content = ob_get_contents();
652
653 ob_end_clean();
654
655 // filter: head
656 if ($this->has_filter('head'))
657 {
658 $content = $this->apply_filters('head', $content);
659 }
660
661 echo $content;
662
663 // action: head
664 if ($this->has_action('head'))
665 {
666 $this->do_action('head');
667 }
668 }
669
670 /**
671 * Used to insert SCRIPT tags into the footer, called on WordPress
672 * admin_footer action.
673 *
674 * @since 1.3
675 * @access private
676 * @see _head()
677 */
678 function _foot()
679 {
680 $content = NULL;
681
682 if
683 (
684 $this->lock OR
685 $this->hide_title OR
686 $this->view OR
687 $this->hide_screen_option
688 )
689 {
690 ob_start();
691
692 ?>
693 <script type="text/javascript">
694 /* <![CDATA[ */
695 (function($){ /* not using jQuery ondomready, code runs right away in footer */
696
697 var mb_id = '<?php echo $this->id; ?>';
698 var mb = $('#' + mb_id + '_metabox');
699
700 <?php if (WPALCHEMY_LOCK_TOP == $this->lock): ?>
701 <?php if ('side' == $this->context): ?>
702 var id = 'wpalchemy-side-top';
703 if ( ! $('#'+id).length)
704 {
705 $('<div></div>').attr('id',id).prependTo('#side-info-column');
706 }
707 <?php else: ?>
708 var id = 'wpalchemy-content-top';
709 if ( ! $('#'+id).length)
710 {
711 $('<div></div>').attr('id',id).insertAfter('#postdiv, #postdivrich');
712 }
713 <?php endif; ?>
714 $('#'+id).append(mb);
715 <?php elseif (WPALCHEMY_LOCK_BOTTOM == $this->lock): ?>
716 <?php if ('side' == $this->context): ?>
717 var id = 'wpalchemy-side-bottom';
718 if ( ! $('#'+id).length)
719 {
720 $('<div></div>').attr('id',id).appendTo('#side-info-column');
721 }
722 <?php else: ?>
723 if ( ! $('#advanced-sortables').children().length)
724 {
725 $('#advanced-sortables').css('display','none');
726 }
727
728 var id = 'wpalchemy-content-bottom';
729 if ( ! $('#'+id).length)
730 {
731 $('<div></div>').attr('id',id).insertAfter('#advanced-sortables');
732 }
733 <?php endif; ?>
734 $('#'+id).append(mb);
735 <?php elseif (WPALCHEMY_LOCK_BEFORE_POST_TITLE == $this->lock): ?>
736 <?php if ('side' != $this->context): ?>
737 var id = 'wpalchemy-content-bpt';
738 if ( ! $('#'+id).length)
739 {
740 $('<div></div>').attr('id',id).prependTo('#post-body-content');
741 }
742 $('#'+id).append(mb);
743 <?php endif; ?>
744 <?php elseif (WPALCHEMY_LOCK_AFTER_POST_TITLE == $this->lock): ?>
745 <?php if ('side' != $this->context): ?>
746 var id = 'wpalchemy-content-apt';
747 if ( ! $('#'+id).length)
748 {
749 $('<div></div>').attr('id',id).insertAfter('#titlediv');
750 }
751 $('#'+id).append(mb);
752 <?php endif; ?>
753 <?php endif; ?>
754
755 <?php if ( ! empty($this->lock)): ?>
756 $('.hndle', mb).css('cursor','pointer');
757 $('.handlediv', mb).remove();
758 <?php endif; ?>
759
760 <?php if ($this->hide_title): ?>
761 $('.hndle', mb).remove();
762 $('.handlediv', mb).remove();
763 mb.removeClass('closed'); /* start opened */
764 <?php endif; ?>
765
766 <?php if (WPALCHEMY_VIEW_START_OPENED == $this->view): ?>
767 mb.removeClass('closed');
768 <?php elseif (WPALCHEMY_VIEW_START_CLOSED == $this->view): ?>
769 mb.addClass('closed');
770 <?php elseif (WPALCHEMY_VIEW_ALWAYS_OPENED == $this->view): ?>
771 /* todo: need to find a way to add this script block below, load-scripts.php?... */
772 var h3 = mb.children('h3');
773 setTimeout(function(){ h3.unbind('click'); }, 1000);
774 $('.handlediv', mb).remove();
775 mb.removeClass('closed'); /* start opened */
776 $('.hndle', mb).css('cursor','auto');
777 <?php endif; ?>
778
779 <?php if ($this->hide_screen_option): ?>
780 $('.metabox-prefs label[for='+ mb_id +'_metabox-hide]').remove();
781 <?php endif; ?>
782
783 mb = null;
784
785 })(jQuery);
786 /* ]]> */
787 </script>
788 <?php
789
790 $content = ob_get_contents();
791
792 ob_end_clean();
793 }
794
795 // filter: foot
796 if ($this->has_filter('foot'))
797 {
798 $content = $this->apply_filters('foot', $content);
799 }
800
801 echo $content;
802
803 // action: foot
804 if ($this->has_action('foot'))
805 {
806 $this->do_action('foot');
807 }
808 }
809
810 /**
811 * Used to setup the meta box content template
812 *
813 * @since 1.0
814 * @access private
815 * @see _init()
816 */
817 function _setup()
818 {
819 $this->in_template = TRUE;
820
821 // also make current post data available
822 global $post;
823
824 // shortcuts
825 $mb =& $this;
826 $metabox =& $this;
827 $id = $this->id;
828 $meta = $this->_meta(NULL, TRUE);
829
830 // use include because users may want to use one templete for multiple meta boxes
831 include $this->template;
832
833 // create a nonce for verification
834 echo '<input type="hidden" name="'. $this->id .'_nonce" value="' . wp_create_nonce($this->id) . '" />';
835
836 $this->in_template = FALSE;
837 }
838
839 /**
840 * Used to properly prefix the filter tag, the tag is unique to the meta
841 * box instance
842 *
843 * @since 1.3
844 * @access private
845 * @param string $tag name of the filter
846 * @return string uniquely prefixed tag name
847 */
848 function _get_filter_tag($tag)
849 {
850 $prefix = 'wpalchemy_filter_' . $this->id . '_';
851 $prefix = preg_replace('/_+/', '_', $prefix);
852
853 $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
854 return $prefix . $tag;
855 }
856
857 /**
858 * Uses WordPress add_filter() function, see WordPress add_filter()
859 *
860 * @since 1.3
861 * @access public
862 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L65
863 */
864 function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1)
865 {
866 $tag = $this->_get_filter_tag($tag);;
867 add_filter($tag, $function_to_add, $priority, $accepted_args);
868 }
869
870 /**
871 * Uses WordPress has_filter() function, see WordPress has_filter()
872 *
873 * @since 1.3
874 * @access public
875 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L86
876 */
877 function has_filter($tag, $function_to_check = FALSE)
878 {
879 $tag = $this->_get_filter_tag($tag);
880 return has_filter($tag, $function_to_check);
881 }
882
883 /**
884 * Uses WordPress apply_filters() function, see WordPress apply_filters()
885 *
886 * @since 1.3
887 * @access public
888 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L134
889 */
890 function apply_filters($tag, $value)
891 {
892 $args = func_get_args();
893 $args[0] = $this->_get_filter_tag($tag);
894 return call_user_func_array('apply_filters', $args);
895 }
896
897 /**
898 * Uses WordPress remove_filter() function, see WordPress remove_filter()
899 *
900 * @since 1.3
901 * @access public
902 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L250
903 */
904 function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
905 {
906 $tag = $this->_get_filter_tag($tag);
907 return remove_filter($tag, $function_to_remove, $priority, $accepted_args);
908 }
909
910 /**
911 * Used to properly prefix the action tag, the tag is unique to the meta
912 * box instance
913 *
914 * @since 1.3
915 * @access private
916 * @param string $tag name of the action
917 * @return string uniquely prefixed tag name
918 */
919 function _get_action_tag($tag)
920 {
921 $prefix = 'wpalchemy_action_' . $this->id . '_';
922 $prefix = preg_replace('/_+/', '_', $prefix);
923
924 $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
925 return $prefix . $tag;
926 }
927
928 /**
929 * Uses WordPress add_action() function, see WordPress add_action()
930 *
931 * @since 1.3
932 * @access public
933 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L324
934 */
935 function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1)
936 {
937 $tag = $this->_get_action_tag($tag);
938 add_action($tag, $function_to_add, $priority, $accepted_args);
939 }
940
941 /**
942 * Uses WordPress has_action() function, see WordPress has_action()
943 *
944 * @since 1.3
945 * @access public
946 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L492
947 */
948 function has_action($tag, $function_to_check = FALSE)
949 {
950 $tag = $this->_get_action_tag($tag);
951 return has_action($tag, $function_to_check);
952 }
953
954 /**
955 * Uses WordPress remove_action() function, see WordPress remove_action()
956 *
957 * @since 1.3
958 * @access public
959 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L513
960 */
961 function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
962 {
963 $tag = $this->_get_action_tag($tag);
964 return remove_action($tag, $function_to_remove, $priority, $accepted_args);
965 }
966
967 /**
968 * Uses WordPress do_action() function, see WordPress do_action()
969 * @since 1.3
970 * @access public
971 * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L352
972 */
973 function do_action($tag, $arg = '')
974 {
975 $args = func_get_args();
976 $args[0] = $this->_get_action_tag($tag);
977 return call_user_func_array('do_action', $args);
978 }
979
980 /**
981 * Used to check if creating a new post or editing one
982 *
983 * @static
984 * @since 1.3.7
985 * @access private
986 * @return bool
987 * @see _is_page()
988 */
989 static function _is_post()
990 {
991 if ('post' == WPAlchemy_MetaBox::_is_post_or_page())
992 {
993 return TRUE;
994 }
995
996 return FALSE;
997 }
998
999 /**
1000 * Used to check if creating a new page or editing one
1001 *
1002 * @static
1003 * @since 1.3.7
1004 * @access private
1005 * @return bool
1006 * @see _is_post()
1007 */
1008 static function _is_page()
1009 {
1010 if ('page' == WPAlchemy_MetaBox::_is_post_or_page())
1011 {
1012 return TRUE;
1013 }
1014
1015 return FALSE;
1016 }
1017
1018 /**
1019 * Used to check if creating or editing a post or page
1020 *
1021 * @static
1022 * @since 1.3.8
1023 * @access private
1024 * @return string "post" or "page"
1025 * @see _is_post(), _is_page()
1026 */
1027 static function _is_post_or_page()
1028 {
1029 $post_type = WPAlchemy_MetaBox::_get_current_post_type();
1030
1031 if (isset($post_type))
1032 {
1033 if ('page' == $post_type)
1034 {
1035 return 'page';
1036 }
1037 else
1038 {
1039 return 'post';
1040 }
1041 }
1042
1043 return NULL;
1044 }
1045
1046 /**
1047 * Used to check for the current post type, works when creating or editing a
1048 * new post, page or custom post type.
1049 *
1050 * @static
1051 * @since 1.4.6
1052 * @return string [custom_post_type], page or post
1053 */
1054 static function _get_current_post_type()
1055 {
1056 $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : NULL ;
1057
1058 if ( isset( $uri ) )
1059 {
1060 $uri_parts = parse_url($uri);
1061
1062 $file = basename($uri_parts['path']);
1063
1064 if ($uri AND in_array($file, array('post.php', 'post-new.php')))
1065 {
1066 $post_id = WPAlchemy_MetaBox::_get_post_id();
1067
1068 $post_type = isset($_GET['post_type']) ? $_GET['post_type'] : NULL ;
1069
1070 $post_type = $post_id ? get_post_type($post_id) : $post_type ;
1071
1072 if (isset($post_type))
1073 {
1074 return $post_type;
1075 }
1076 else
1077 {
1078 // because of the 'post.php' and 'post-new.php' checks above, we can default to 'post'
1079 return 'post';
1080 }
1081 }
1082 }
1083
1084 return NULL;
1085 }
1086
1087 /**
1088 * Used to get the current post id.
1089 *
1090 * @static
1091 * @since 1.4.8
1092 * @return int post ID
1093 */
1094 static function _get_post_id()
1095 {
1096 global $post;
1097
1098 $p_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : null ;
1099
1100 $g_post_id = isset($_GET['post']) ? $_GET['post'] : null ;
1101
1102 $post_id = $g_post_id ? $g_post_id : $p_post_id ;
1103
1104 $post_id = isset($post->ID) ? $post->ID : $post_id ;
1105
1106 if (isset($post_id))
1107 {
1108 return (integer) $post_id;
1109 }
1110
1111 return null;
1112 }
1113
1114 /**
1115 * @since 1.0
1116 */
1117 function can_output()
1118 {
1119 $post_id = WPAlchemy_MetaBox::_get_post_id();
1120
1121 if (!empty($this->exclude_template) OR !empty($this->include_template))
1122 {
1123 $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
1124 }
1125
1126 if
1127 (
1128 !empty($this->exclude_category) OR
1129 !empty($this->exclude_category_id) OR
1130 !empty($this->include_category) OR
1131 !empty($this->include_category_id)
1132 )
1133 {
1134 $categories = wp_get_post_categories($post_id,'fields=all');
1135 }
1136
1137 if
1138 (
1139 !empty($this->exclude_tag) OR
1140 !empty($this->exclude_tag_id) OR
1141 !empty($this->include_tag) OR
1142 !empty($this->include_tag_id)
1143 )
1144 {
1145 $tags = wp_get_post_tags($post_id);
1146 }
1147
1148 // processing order: "exclude" then "include"
1149 // processing order: "template" then "category" then "post"
1150
1151 $can_output = TRUE; // include all
1152
1153 if
1154 (
1155 !empty($this->exclude_template) OR
1156 !empty($this->exclude_category_id) OR
1157 !empty($this->exclude_category) OR
1158 !empty($this->exclude_tag_id) OR
1159 !empty($this->exclude_tag) OR
1160 !empty($this->exclude_post_id) OR
1161 !empty($this->include_template) OR
1162 !empty($this->include_category_id) OR
1163 !empty($this->include_category) OR
1164 !empty($this->include_tag_id) OR
1165 !empty($this->include_tag) OR
1166 !empty($this->include_post_id)
1167 )
1168 {
1169 if (!empty($this->exclude_template))
1170 {
1171 if (in_array($template_file,$this->exclude_template))
1172 {
1173 $can_output = FALSE;
1174 }
1175 }
1176
1177 if (!empty($this->exclude_category_id))
1178 {
1179 foreach ($categories as $cat)
1180 {
1181 if (in_array($cat->term_id,$this->exclude_category_id))
1182 {
1183 $can_output = FALSE;
1184 break;
1185 }
1186 }
1187 }
1188
1189 if (!empty($this->exclude_category))
1190 {
1191 foreach ($categories as $cat)
1192 {
1193 if
1194 (
1195 in_array($cat->slug,$this->exclude_category) OR
1196 in_array($cat->name,$this->exclude_category)
1197 )
1198 {
1199 $can_output = FALSE;
1200 break;
1201 }
1202 }
1203 }
1204
1205 if (!empty($this->exclude_tag_id))
1206 {
1207 foreach ($tags as $tag)
1208 {
1209 if (in_array($tag->term_id,$this->exclude_tag_id))
1210 {
1211 $can_output = FALSE;
1212 break;
1213 }
1214 }
1215 }
1216
1217 if (!empty($this->exclude_tag))
1218 {
1219 foreach ($tags as $tag)
1220 {
1221 if
1222 (
1223 in_array($tag->slug,$this->exclude_tag) OR
1224 in_array($tag->name,$this->exclude_tag)
1225 )
1226 {
1227 $can_output = FALSE;
1228 break;
1229 }
1230 }
1231 }
1232
1233 if (!empty($this->exclude_post_id))
1234 {
1235 if (in_array($post_id,$this->exclude_post_id))
1236 {
1237 $can_output = FALSE;
1238 }
1239 }
1240
1241 // excludes are not set use "include only" mode
1242
1243 if
1244 (
1245 empty($this->exclude_template) AND
1246 empty($this->exclude_category_id) AND
1247 empty($this->exclude_category) AND
1248 empty($this->exclude_tag_id) AND
1249 empty($this->exclude_tag) AND
1250 empty($this->exclude_post_id)
1251 )
1252 {
1253 $can_output = FALSE;
1254 }
1255
1256 if (!empty($this->include_template))
1257 {
1258 if (in_array($template_file,$this->include_template))
1259 {
1260 $can_output = TRUE;
1261 }
1262 }
1263
1264 if (!empty($this->include_category_id))
1265 {
1266 foreach ($categories as $cat)
1267 {
1268 if (in_array($cat->term_id,$this->include_category_id))
1269 {
1270 $can_output = TRUE;
1271 break;
1272 }
1273 }
1274 }
1275
1276 if (!empty($this->include_category))
1277 {
1278 foreach ($categories as $cat)
1279 {
1280 if
1281 (
1282 in_array($cat->slug,$this->include_category) OR
1283 in_array($cat->name,$this->include_category)
1284 )
1285 {
1286 $can_output = TRUE;
1287 break;
1288 }
1289 }
1290 }
1291
1292 if (!empty($this->include_tag_id))
1293 {
1294 foreach ($tags as $tag)
1295 {
1296 if (in_array($tag->term_id,$this->include_tag_id))
1297 {
1298 $can_output = TRUE;
1299 break;
1300 }
1301 }
1302 }
1303
1304 if (!empty($this->include_tag))
1305 {
1306 foreach ($tags as $tag)
1307 {
1308 if
1309 (
1310 in_array($tag->slug,$this->include_tag) OR
1311 in_array($tag->name,$this->include_tag)
1312 )
1313 {
1314 $can_output = TRUE;
1315 break;
1316 }
1317 }
1318 }
1319
1320 if (!empty($this->include_post_id))
1321 {
1322 if (in_array($post_id,$this->include_post_id))
1323 {
1324 $can_output = TRUE;
1325 }
1326 }
1327 }
1328
1329 $post_type = WPAlchemy_MetaBox::_get_current_post_type();
1330
1331 if (isset($post_type) AND ! in_array($post_type, $this->types))
1332 {
1333 $can_output = FALSE;
1334 }
1335
1336 // filter: output (can_output)
1337 if ($this->has_filter('output'))
1338 {
1339 $can_output = $this->apply_filters('output', $post_id);
1340 }
1341
1342 return $can_output;
1343 }
1344
1345 /**
1346 * Used to insert global STYLE or SCRIPT tags into the head, called on
1347 * WordPress admin_footer action.
1348 *
1349 * @static
1350 * @since 1.3
1351 * @access private
1352 * @see _global_foot()
1353 */
1354 static function _global_head()
1355 {
1356 // must be creating or editing a post or page
1357 if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
1358
1359 // todo: you're assuming people will want to use this exact functionality
1360 // consider giving a developer access to change this via hooks/callbacks
1361
1362 // include javascript for special functionality
1363 ?><style type="text/css"> .wpa_group.tocopy { display:none; } </style>
1364 <script type="text/javascript">
1365 /* <![CDATA[ */
1366 jQuery(function($)
1367 {
1368 $(document).click(function(e)
1369 {
1370 var elem = $(e.target);
1371
1372 if (elem.attr('class') && elem.filter('[class*=dodelete]').length)
1373 {
1374 e.preventDefault();
1375
1376 var p = elem.parents('.wpa_group:first');
1377
1378 if(p.length <= 0)
1379 p = elem.parents('.postbox'); /*wp*/
1380
1381 var the_name = elem.attr('class').match(/dodelete-([a-zA-Z0-9_-]*)/i);
1382
1383 the_name = (the_name && the_name[1]) ? the_name[1] : null ;
1384
1385 /* todo: expose and allow editing of this message */
1386 if (confirm('This action can not be undone, are you sure?'))
1387 {
1388 if (the_name)
1389 {
1390 $('.wpa_group-'+ the_name, p).not('.tocopy').remove();
1391 }
1392 else
1393 {
1394 elem.parents('.wpa_group:first').remove();
1395 }
1396
1397 if(!the_name)
1398 {
1399 var the_group = elem.parents('.wpa_group');
1400 if(the_group && the_group.attr('class'))
1401 {
1402 the_name = the_group.attr('class').match(/wpa_group-([a-zA-Z0-9_-]*)/i);
1403 the_name = (the_name && the_name[1]) ? the_name[1] : null ;
1404 }
1405 }
1406 checkLoopLimit(the_name);
1407
1408 $.wpalchemy.trigger('wpa_delete');
1409 }
1410 }
1411 });
1412
1413 $(document).on('click', '[class*=docopy-]', function(e)
1414 {
1415 e.preventDefault();
1416
1417 var p = $(this).parents('.wpa_group:first');
1418
1419 if(p.length <= 0)
1420 p = $(this).parents('.postbox'); /*wp*/
1421
1422 var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
1423
1424 var the_group = $('.wpa_group-'+ the_name +'.tocopy', p).first();
1425
1426 var the_clone = the_group.clone().removeClass('tocopy last');
1427
1428 var the_props = ['name', 'id', 'for', 'class'];
1429
1430 the_group.find('*').each(function(i, elem)
1431 {
1432 for (var j = 0; j < the_props.length; j++)
1433 {
1434 var the_prop = $(elem).attr(the_props[j]);
1435
1436 if (the_prop)
1437 {
1438 var reg = new RegExp('\\['+the_name+'\\]\\[(\\d+)\\]', 'i');
1439 var the_match = the_prop.match(reg);
1440
1441 if (the_match)
1442 {
1443 the_prop = the_prop.replace(the_match[0], '['+ the_name + ']' + '['+ (+the_match[1]+1) +']');
1444
1445 $(elem).attr(the_props[j], the_prop);
1446 }
1447
1448 the_match = null;
1449
1450 // todo: this may prove to be too broad of a search
1451 the_match = the_prop.match(/n(\d+)/i);
1452
1453 if (the_match)
1454 {
1455 the_prop = the_prop.replace(the_match[0], 'n' + (+the_match[1]+1));
1456
1457 $(elem).attr(the_props[j], the_prop);
1458 }
1459 }
1460 }
1461 });
1462
1463 // increment the group id
1464 var reg = new RegExp('\\[(\\d+)\\]$', 'i');
1465 var the_id = the_group.attr("id");
1466 var the_match = the_id.match(reg);
1467 if (the_match)
1468 {
1469 the_group.attr("id", the_id.replace(the_match[0], '['+ (+the_match[1]+1) +']'));
1470 }
1471
1472 if ($(this).hasClass('ontop'))
1473 {
1474 $('.wpa_group-'+ the_name, p).first().before(the_clone);
1475 }
1476 else
1477 {
1478 the_group.before(the_clone);
1479 }
1480
1481 checkLoopLimit(the_name);
1482
1483 $.wpalchemy.trigger('wpa_copy', [the_clone]);
1484 });
1485
1486 function checkLoopLimit(name)
1487 {
1488 var elems = $('.docopy-' + name);
1489
1490 $.each(elems, function(idx, elem){
1491
1492 var p = $(this).parents('.wpa_group:first');
1493
1494 if(p.length <= 0)
1495 p = $(this).parents('.postbox'); /*wp*/
1496
1497 var the_class = $('.wpa_loop-' + name, p).attr('class');
1498
1499 if (the_class)
1500 {
1501 var the_match = the_class.match(/wpa_loop_limit-([0-9]*)/i);
1502
1503 if (the_match)
1504 {
1505 var the_limit = the_match[1];
1506
1507 if ($('.wpa_group-' + name, p).not('.wpa_group.tocopy').length >= the_limit)
1508 {
1509 $(this).hide();
1510 }
1511 else
1512 {
1513 $(this).show();
1514 }
1515 }
1516 }
1517
1518 });
1519
1520 }
1521
1522 /* do an initial limit check, show or hide buttons */
1523 $('[class*=docopy-]').each(function()
1524 {
1525 var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
1526
1527 checkLoopLimit(the_name);
1528 });
1529 });
1530 /* ]]> */
1531 </script>
1532 <?php
1533 }
1534
1535 /**
1536 * Used to insert global SCRIPT tags into the footer, called on WordPress
1537 * admin_footer action.
1538 *
1539 * @static
1540 * @since 1.3
1541 * @access private
1542 * @see _global_head()
1543 */
1544 static function _global_foot()
1545 {
1546 // must be creating or editing a post or page
1547 if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
1548
1549 ?>
1550 <script type="text/javascript">
1551 /* <![CDATA[ */
1552 (function($){ /* not using jQuery ondomready, code runs right away in footer */
1553
1554 /* use a global dom element to attach events to */
1555 $.wpalchemy = $('<div></div>').attr('id','wpalchemy').appendTo('body');
1556
1557 })(jQuery);
1558 /* ]]> */
1559 </script>
1560 <?php
1561 }
1562
1563 /**
1564 * Gets the meta data for a meta box
1565 *
1566 * @since 1.0
1567 * @access public
1568 * @param int $post_id optional post ID for which to retrieve the meta data
1569 * @return array
1570 * @see _meta
1571 */
1572 function the_meta($post_id = NULL)
1573 {
1574 return $this->_meta($post_id);
1575 }
1576
1577 /**
1578 * Gets the meta data for a meta box
1579 *
1580 * Internal method calls will typically bypass the data retrieval and will
1581 * immediately return the current meta data
1582 *
1583 * @since 1.3
1584 * @access private
1585 * @param int $post_id optional post ID for which to retrieve the meta data
1586 * @param bool $internal optional boolean if internally calling
1587 * @return array
1588 * @see the_meta()
1589 */
1590 function _meta($post_id = NULL, $internal = FALSE)
1591 {
1592 if ( ! is_numeric($post_id))
1593 {
1594 if ($internal AND $this->current_post_id)
1595 {
1596 $post_id = $this->current_post_id;
1597 }
1598 else
1599 {
1600 global $post;
1601
1602 $post_id = $post->ID;
1603 }
1604 }
1605
1606 // this allows multiple internal calls to _meta() without having to fetch data everytime
1607 if ($internal AND !empty($this->meta) AND $this->current_post_id == $post_id) return $this->meta;
1608
1609 $this->current_post_id = $post_id;
1610
1611 // WPALCHEMY_MODE_ARRAY
1612
1613 $meta = get_post_meta($post_id, $this->id, TRUE);
1614
1615 // var_dump($meta);
1616
1617 // WPALCHEMY_MODE_EXTRACT
1618
1619 $fields = get_post_meta($post_id, $this->id . '_fields', TRUE);
1620
1621 if ( ! empty($fields) AND is_array($fields))
1622 {
1623 $meta = array();
1624
1625 foreach ($fields as $field)
1626 {
1627 $field_noprefix = preg_replace('/^' . $this->prefix . '/i', '', $field);
1628 $meta[$field_noprefix] = get_post_meta($post_id, $field, TRUE);
1629 }
1630 }
1631
1632 $this->meta = $meta;
1633
1634 return $this->meta;
1635 }
1636
1637 // user can also use the_ID(), php functions are case-insensitive
1638 /**
1639 * @since 1.0
1640 * @access public
1641 */
1642 function the_id()
1643 {
1644 echo $this->get_the_id();
1645 }
1646
1647 /**
1648 * @since 1.0
1649 * @access public
1650 */
1651 function get_the_id()
1652 {
1653 return $this->id;
1654 }
1655
1656 /**
1657 * @since 1.0
1658 * @access public
1659 */
1660 function the_field($n, $hint = NULL)
1661 {
1662 $this->name = $n;
1663 $this->hint = $hint;
1664 }
1665
1666 /**
1667 * @since 1.0
1668 * @access public
1669 */
1670 function have_value($n = NULL)
1671 {
1672 if ($this->get_the_value($n)) return TRUE;
1673
1674 return FALSE;
1675 }
1676
1677 /**
1678 * @since 1.0
1679 * @access public
1680 */
1681 function the_value($n = NULL)
1682 {
1683 echo $this->get_the_value($n);
1684 }
1685
1686 function get_the_value($n = NULL, $collection = FALSE)
1687 {
1688 $this->_meta(NULL, TRUE);
1689
1690 $value = null;
1691
1692 if ($this->is_in_loop())
1693 {
1694 $n = is_null($n) ? $this->name : $n ;
1695
1696 if(!is_null($n))
1697 {
1698 if ($collection)
1699 {
1700 $keys = $this->get_the_loop_group_name_array();
1701 }
1702 else
1703 {
1704 $keys = $this->get_the_loop_group_name_array();
1705 $keys[] = $n;
1706 }
1707 }
1708 else
1709 {
1710 if ($collection)
1711 {
1712 $keys = $this->get_the_loop_group_name_array();
1713 end($keys);
1714 $last = key($keys);
1715 unset($keys[$last]);
1716 }
1717 else
1718 {
1719 $keys = $this->get_the_loop_group_name_array();
1720 }
1721 }
1722 $value = $this->get_meta_by_array($keys);
1723 }
1724 else
1725 {
1726 $n = is_null($n) ? $this->name : $n ;
1727
1728 if(isset($this->meta[$n]))
1729 {
1730 $value = $this->meta[$n];
1731 }
1732 }
1733
1734 if (is_string($value) || is_numeric($value))
1735 {
1736 if ($this->in_template)
1737 {
1738 return $value;
1739 }
1740 else
1741 {
1742 // http://wordpress.org/support/topic/call-function-called-by-embed-shortcode-direct
1743 // http://phpdoc.wordpress.org/trunk/WordPress/Embed/WP_Embed.html#run_shortcode
1744
1745 global $wp_embed;
1746
1747 return do_shortcode($wp_embed->run_shortcode($value));
1748 }
1749 }
1750 else
1751 {
1752 // value can sometimes be an array
1753 return $value;
1754 }
1755 }
1756
1757 /**
1758 * @since 1.0
1759 * @access public
1760 */
1761 function the_name($n = NULL)
1762 {
1763 echo $this->get_the_name($n);
1764 }
1765
1766 /**
1767 * @since 1.0
1768 * @access public
1769 */
1770 function get_the_name($n = NULL)
1771 {
1772 if (!$this->in_template AND $this->mode == WPALCHEMY_MODE_EXTRACT)
1773 {
1774 return $this->prefix . str_replace($this->prefix, '', is_null($n) ? $this->name : $n);
1775 }
1776
1777 if ($this->is_in_loop())
1778 {
1779 $n = is_null($n) ? $this->name : $n ;
1780
1781 if (!is_null($n))
1782 $the_field = $this->get_the_loop_group_name(true) . '[' . $n . ']' ;
1783 else
1784 $the_field = $this->get_the_loop_group_name(true);
1785 }
1786 else
1787 {
1788 $n = is_null($n) ? $this->name : $n ;
1789
1790 $the_field = $this->id . '[' . $n . ']';
1791 }
1792
1793 $hints = array
1794 (
1795 WPALCHEMY_FIELD_HINT_CHECKBOX_MULTI,
1796 WPALCHEMY_FIELD_HINT_SELECT_MULTI,
1797 WPALCHEMY_FIELD_HINT_SELECT_MULTIPLE,
1798 );
1799
1800 if (in_array($this->hint, $hints))
1801 {
1802 $the_field .= '[]';
1803 }
1804
1805 return $the_field;
1806 }
1807
1808 /**
1809 * @since 1.1
1810 * @access public
1811 */
1812 function the_index()
1813 {
1814 echo $this->get_the_index();
1815 }
1816
1817 /**
1818 * @since 1.1
1819 * @access public
1820 */
1821 function get_the_index()
1822 {
1823 return $this->in_loop ? $this->get_the_current_group_current() : 0 ;
1824 }
1825
1826 /**
1827 * @since 1.0
1828 * @access public
1829 */
1830 function is_first()
1831 {
1832 if ($this->in_loop AND $this->current == 0) return TRUE;
1833
1834 return FALSE;
1835 }
1836
1837 /**
1838 * @since 1.0
1839 * @access public
1840 */
1841 function is_last()
1842 {
1843 if ($this->in_loop AND ($this->current+1) == $this->length) return TRUE;
1844
1845 return FALSE;
1846 }
1847
1848 /**
1849 * Used to check if a value is a match
1850 *
1851 * @since 1.1
1852 * @access public
1853 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1854 * @param string $v optional the value to check for
1855 * @return bool
1856 * @see is_value()
1857 */
1858 function is_value($n, $v = NULL)
1859 {
1860 if (is_null($v))
1861 {
1862 $the_value = $this->get_the_value();
1863
1864 $v = $n;
1865 }
1866 else
1867 {
1868 $the_value = $this->get_the_value($n);
1869 }
1870
1871 if($v == $the_value) return TRUE;
1872
1873 return FALSE;
1874 }
1875
1876 /**
1877 * Used to check if a value is selected, useful when working with checkbox,
1878 * radio and select values.
1879 *
1880 * @since 1.3
1881 * @access public
1882 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1883 * @param string $v optional the value to check for
1884 * @return bool
1885 * @see is_value()
1886 */
1887 function is_selected($n, $v = NULL)
1888 {
1889 if (is_null($v))
1890 {
1891 $the_value = $this->get_the_value(NULL);
1892
1893 $v = $n;
1894 }
1895 else
1896 {
1897 $the_value = $this->get_the_value($n);
1898 }
1899
1900 if (is_array($the_value))
1901 {
1902 if (in_array($v, $the_value)) return TRUE;
1903 }
1904 elseif($v == $the_value)
1905 {
1906 return TRUE;
1907 }
1908
1909 return FALSE;
1910 }
1911
1912 /**
1913 * Prints the current state of a checkbox field and should be used inline
1914 * within the INPUT tag.
1915 *
1916 * @since 1.3
1917 * @access public
1918 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1919 * @param string $v optional the value to check for
1920 * @see get_the_checkbox_state()
1921 */
1922 function the_checkbox_state($n, $v = NULL)
1923 {
1924 echo $this->get_the_checkbox_state($n, $v);
1925 }
1926
1927 /**
1928 * Returns the current state of a checkbox field, the returned string is
1929 * suitable to be used inline within the INPUT tag.
1930 *
1931 * @since 1.3
1932 * @access public
1933 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1934 * @param string $v optional the value to check for
1935 * @return string suitable to be used inline within the INPUT tag
1936 * @see the_checkbox_state()
1937 */
1938 function get_the_checkbox_state($n, $v = NULL)
1939 {
1940 if ($this->is_selected($n, $v)) return ' checked="checked"';
1941 }
1942
1943 /**
1944 * Prints the current state of a radio field and should be used inline
1945 * within the INPUT tag.
1946 *
1947 * @since 1.3
1948 * @access public
1949 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1950 * @param string $v optional the value to check for
1951 * @see get_the_radio_state()
1952 */
1953 function the_radio_state($n, $v = NULL)
1954 {
1955 echo $this->get_the_checkbox_state($n, $v);
1956 }
1957
1958 /**
1959 * Returns the current state of a radio field, the returned string is
1960 * suitable to be used inline within the INPUT tag.
1961 *
1962 * @since 1.3
1963 * @access public
1964 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1965 * @param string $v optional the value to check for
1966 * @return string suitable to be used inline within the INPUT tag
1967 * @see the_radio_state()
1968 */
1969 function get_the_radio_state($n, $v = NULL)
1970 {
1971 return $this->get_the_checkbox_state($n, $v);
1972 }
1973
1974 /**
1975 * Prints the current state of a select field and should be used inline
1976 * within the SELECT tag.
1977 *
1978 * @since 1.3
1979 * @access public
1980 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1981 * @param string $v optional the value to check for
1982 * @see get_the_select_state()
1983 */
1984 function the_select_state($n, $v = NULL)
1985 {
1986 echo $this->get_the_select_state($n, $v);
1987 }
1988
1989 /**
1990 * Returns the current state of a select field, the returned string is
1991 * suitable to be used inline within the SELECT tag.
1992 *
1993 * @since 1.3
1994 * @access public
1995 * @param string $n the field name to check or the value to check for (if the_field() is used prior)
1996 * @param string $v optional the value to check for
1997 * @return string suitable to be used inline within the SELECT tag
1998 * @see the_select_state()
1999 */
2000 function get_the_select_state($n, $v = NULL)
2001 {
2002 if ($this->is_selected($n, $v)) return ' selected="selected"';
2003 }
2004
2005 /**
2006 * @since 1.1
2007 * @access public
2008 */
2009 function the_group_open($t = 'div')
2010 {
2011 echo $this->get_the_group_open($t);
2012 }
2013
2014 /**
2015 * @since 1.1
2016 * @access public
2017 */
2018 function get_the_group_open($t = 'div')
2019 {
2020 $this->group_tag = $t;
2021
2022 $curr_loop = $this->get_the_current_loop();
2023 $the_name = $curr_loop->name;
2024
2025 $loop_open = NULL;
2026
2027 $loop_open_classes = array('wpa_loop', 'wpa_loop-' . $the_name);
2028
2029 $css_class = array('wpa_group', 'wpa_group-'. $the_name);
2030
2031
2032 if ($curr_loop->is_first())
2033 {
2034 array_push($css_class, 'first');
2035
2036 $loop_open = '<div class="wpa_loop">';
2037
2038 if (isset($this->_loop_data->limit))
2039 {
2040 array_push($loop_open_classes, 'wpa_loop_limit-' . $this->_loop_data->limit);
2041 }
2042
2043 $loop_open = '<div id="wpa_loop-'. $the_name .'" class="' . implode(' ', $loop_open_classes) . '">';
2044 }
2045
2046 if ($curr_loop->is_last())
2047 {
2048 array_push($css_class, 'last');
2049
2050 if ($this->in_loop == 'multi')
2051 {
2052 array_push($css_class, 'tocopy');
2053 }
2054 }
2055
2056 return $loop_open . '<' . $t . ' class="'. implode(' ', $css_class) . '">';
2057 }
2058
2059 /**
2060 * @since 1.1
2061 * @access public
2062 */
2063 function the_group_close()
2064 {
2065 echo $this->get_the_group_close();
2066 }
2067
2068 /**
2069 * @since 1.1
2070 * @access public
2071 */
2072 function get_the_group_close()
2073 {
2074 $loop_close = NULL;
2075
2076 $curr_loop = $this->get_the_current_loop();
2077
2078 if ($curr_loop->is_last())
2079 {
2080 $loop_close = '</div>';
2081 }
2082
2083 return '</' . $this->group_tag . '>' . $loop_close;
2084 }
2085
2086 /**
2087 * @since 1.1
2088 * @access public
2089 */
2090 function have_fields_and_multi($n, $options = NULL)
2091 {
2092 if (is_array($options))
2093 {
2094 // use as stdClass object
2095 $options = (object)$options;
2096
2097 $length = @$options->length;
2098
2099 $this->_loop_data->limit = @$options->limit;
2100 }
2101 else
2102 {
2103 // backward compatibility (bc)
2104 $length = $options;
2105 }
2106
2107 $this->_meta(NULL, TRUE);
2108
2109 $this->in_loop = 'multi';
2110
2111 // push new loop or set loop to current name
2112 $this->push_or_set_current_loop($n, $length, $this->in_loop);
2113
2114 return $this->_loop($n, $length, 2);
2115 }
2116
2117 /**
2118 * @deprecated
2119 * @since 1.0
2120 * @access public
2121 */
2122 function have_fields_and_one($n)
2123 {
2124 $this->_meta(NULL, TRUE);
2125 $this->in_loop = 'single';
2126 $this->push_or_set_current_loop($n, $length, $this->in_loop);
2127 return $this->_loop($n,NULL,1);
2128 }
2129
2130 /**
2131 * @since 1.0
2132 * @access public
2133 */
2134 function have_fields($n,$length=NULL)
2135 {
2136 $this->_meta(NULL, TRUE);
2137 // push new loop or set loop to current name
2138 $this->in_loop = 'normal';
2139 $this->push_or_set_current_loop($n, $length, $this->in_loop);
2140 return $this->_loop($n,$length);
2141 }
2142
2143 /**
2144 * @since 1.0
2145 * @access private
2146 */
2147 function _loop($n,$length=NULL,$and_one=0)
2148 {
2149 if (!$this->in_loop)
2150 {
2151 $this->in_loop = TRUE;
2152 }
2153
2154 $cnt = $this->get_the_current_group_count();
2155
2156 $length = is_null($length) ? $cnt : $length ;
2157
2158 if ($this->in_loop == 'multi' AND $cnt > $length) $length = $cnt;
2159
2160 $this->length = $length;
2161
2162 if ($this->in_template AND $and_one)
2163 {
2164 if ($length == 0)
2165 {
2166 $this->length = $and_one;
2167 }
2168 else
2169 {
2170 $this->length = $length+1;
2171 }
2172 }
2173
2174 $this->set_the_current_group_length($this->length);
2175 $this->increment_current_loop();
2176 $this->current++;
2177
2178 if ($this->get_the_current_group_current() < $this->get_the_current_group_length())
2179 {
2180 $this->name = NULL;
2181 $this->fieldtype = NULL;
2182
2183 return TRUE;
2184 }
2185 else if ($this->get_the_current_group_current() == $this->get_the_current_group_length())
2186 {
2187 $this->name = NULL;
2188 $this->set_the_current_group_current(-1);
2189 $this->prev_loop();
2190 }
2191
2192 $this->in_loop = FALSE;
2193
2194 $this->_loop_data = new stdClass;
2195
2196 return FALSE;
2197 }
2198
2199 /**
2200 * @since 1.0
2201 * @access private
2202 */
2203 function _save($post_id)
2204 {
2205 /**
2206 * note: the "save_post" action fires for saving revisions and post/pages,
2207 * when saving a post this function fires twice, once for a revision save,
2208 * and again for the post/page save ... the $post_id is different for the
2209 * revision save, this means that "get_post_meta()" will not work if trying
2210 * to get values for a revision (as it has no post meta data)
2211 * see http://alexking.org/blog/2008/09/06/wordpress-26x-duplicate-custom-field-issue
2212 *
2213 * why let the code run twice? wordpress does not currently save post meta
2214 * data per revisions (I think it should, so users can do a complete revert),
2215 * so in the case that this functionality changes, let it run twice
2216 */
2217
2218 $real_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : NULL ;
2219
2220 // check autosave
2221 if (defined('DOING_AUTOSAVE') AND DOING_AUTOSAVE AND !$this->autosave) return $post_id;
2222
2223 // make sure data came from our meta box, verify nonce
2224 $nonce = isset($_POST[$this->id.'_nonce']) ? $_POST[$this->id.'_nonce'] : NULL ;
2225 if (!wp_verify_nonce($nonce, $this->id)) return $post_id;
2226
2227 // check user permissions
2228 if ($_POST['post_type'] == 'page')
2229 {
2230 if (!current_user_can('edit_page', $post_id)) return $post_id;
2231 }
2232 else
2233 {
2234 if (!current_user_can('edit_post', $post_id)) return $post_id;
2235 }
2236
2237 // authentication passed, save data
2238
2239 $new_data = isset( $_POST[$this->id] ) ? $_POST[$this->id] : NULL ;
2240
2241 WPAlchemy_MetaBox::clean($new_data);
2242
2243 if (empty($new_data))
2244 {
2245 $new_data = NULL;
2246 }
2247
2248 // filter: save
2249 if ($this->has_filter('save'))
2250 {
2251 $new_data = $this->apply_filters('save', $new_data, $real_post_id);
2252
2253 /**
2254 * halt saving
2255 * @since 1.3.4
2256 */
2257 if (FALSE === $new_data) return $post_id;
2258
2259 WPAlchemy_MetaBox::clean($new_data);
2260 }
2261
2262 // get current fields, use $real_post_id (checked for in both modes)
2263 $current_fields = get_post_meta($real_post_id, $this->id . '_fields', TRUE);
2264
2265 if ($this->mode == WPALCHEMY_MODE_EXTRACT)
2266 {
2267 $new_fields = array();
2268
2269 if (is_array($new_data))
2270 {
2271 foreach ($new_data as $k => $v)
2272 {
2273 $field = $this->prefix . $k;
2274
2275 array_push($new_fields,$field);
2276
2277 $new_value = $new_data[$k];
2278
2279 if (is_null($new_value))
2280 {
2281 delete_post_meta($post_id, $field);
2282 }
2283 else
2284 {
2285 update_post_meta($post_id, $field, $new_value);
2286 }
2287 }
2288 }
2289
2290 $diff_fields = array_diff((array)$current_fields,$new_fields);
2291
2292 if (is_array($diff_fields))
2293 {
2294 foreach ($diff_fields as $field)
2295 {
2296 delete_post_meta($post_id,$field);
2297 }
2298 }
2299
2300 delete_post_meta($post_id, $this->id . '_fields');
2301
2302 if ( ! empty($new_fields))
2303 {
2304 add_post_meta($post_id,$this->id . '_fields', $new_fields, TRUE);
2305 }
2306
2307 // keep data tidy, delete values if previously using WPALCHEMY_MODE_ARRAY
2308 delete_post_meta($post_id, $this->id);
2309 }
2310 else
2311 {
2312 if (is_null($new_data))
2313 {
2314 delete_post_meta($post_id, $this->id);
2315 }
2316 else
2317 {
2318 update_post_meta($post_id, $this->id, $new_data);
2319 }
2320
2321 // keep data tidy, delete values if previously using WPALCHEMY_MODE_EXTRACT
2322 if (is_array($current_fields))
2323 {
2324 foreach ($current_fields as $field)
2325 {
2326 delete_post_meta($post_id, $field);
2327 }
2328
2329 delete_post_meta($post_id, $this->id . '_fields');
2330 }
2331 }
2332
2333 // action: save
2334 if ($this->has_action('save'))
2335 {
2336 $this->do_action('save', $new_data, $real_post_id);
2337 }
2338
2339 return $post_id;
2340 }
2341
2342 /**
2343 * Cleans an array, removing blank ('') values
2344 *
2345 * @static
2346 * @since 1.0
2347 * @access public
2348 * @param array the array to clean (passed by reference)
2349 */
2350 static function clean(&$arr)
2351 {
2352 if (is_array($arr))
2353 {
2354 foreach ($arr as $i => $v)
2355 {
2356 if (is_array($arr[$i]))
2357 {
2358 WPAlchemy_MetaBox::clean($arr[$i]);
2359
2360 if (!count($arr[$i]))
2361 {
2362 unset($arr[$i]);
2363 }
2364 }
2365 else
2366 {
2367 if ('' == trim($arr[$i]) OR is_null($arr[$i]))
2368 {
2369 unset($arr[$i]);
2370 }
2371 }
2372 }
2373
2374 if (!count($arr))
2375 {
2376 $arr = array();
2377 }
2378 else
2379 {
2380 $keys = array_keys($arr);
2381
2382 $is_numeric = TRUE;
2383
2384 foreach ($keys as $key)
2385 {
2386 if (!is_numeric($key))
2387 {
2388 $is_numeric = FALSE;
2389 break;
2390 }
2391 }
2392
2393 if ($is_numeric)
2394 {
2395 $arr = array_values($arr);
2396 }
2397 }
2398 }
2399 }
2400
2401 /**
2402 * Other than core modifications, here is the loop stack function and class to support
2403 * nested repeatable group.
2404 * author Vafpress
2405 */
2406
2407 var $_loop_stack = array();
2408
2409 function push_loop($name, $length, $type)
2410 {
2411 $loop = new WPA_Loop($name, $length, $type);
2412 $parent = $this->get_the_current_loop();
2413 if($parent)
2414 $loop->parent = $parent->name;
2415 else
2416 $loop->parent = false;
2417 $this->_loop_stack[$name] = $loop;
2418 return $loop;
2419 }
2420
2421 function push_or_set_current_loop($name, $length, $type)
2422 {
2423 if( !array_key_exists( $name, $this->_loop_stack ) )
2424 {
2425 $this->push_loop($name, $length, $type);
2426 }
2427
2428 $this->set_current_loop($name);
2429 }
2430
2431 function set_current_loop($name)
2432 {
2433 reset($this->_loop_stack);
2434 if(!array_key_exists($name, $this->_loop_stack)){
2435 return;
2436 }
2437 while(key($this->_loop_stack) !== $name)
2438 next($this->_loop_stack);
2439 }
2440
2441 function next_loop()
2442 {
2443 return next($this->_loop_stack);
2444 }
2445
2446 function prev_loop()
2447 {
2448 $parent = $this->get_the_current_loop()->parent;
2449 if($parent)
2450 {
2451 $this->set_current_loop($parent);
2452 }
2453 else
2454 {
2455 $this->_loop_stack = array();
2456 return false;
2457 }
2458 }
2459
2460 function get_the_current_group_length()
2461 {
2462 return current($this->_loop_stack)->length;
2463 }
2464
2465 function get_the_current_group_current()
2466 {
2467 return current($this->_loop_stack)->current;
2468 }
2469
2470 function set_the_current_group_length($length)
2471 {
2472 current($this->_loop_stack)->length = $length;
2473 }
2474
2475 function set_the_current_group_current($current)
2476 {
2477 current($this->_loop_stack)->current = $current;
2478 }
2479
2480 function get_the_loop_collection($name = null)
2481 {
2482 $collection = array();
2483
2484 if(is_null($name))
2485 {
2486 $curr = $this->get_the_current_loop();
2487 if($curr)
2488 {
2489 $name = $curr->name;
2490 $loop_stack = $this->_loop_stack;
2491 $loop = $loop_stack[$name];
2492 $collection[] = $loop;
2493 while ($loop)
2494 {
2495 $collection[] = $loop;
2496 if($loop->parent)
2497 $loop = $loop_stack[$loop->parent];
2498 else
2499 $loop = false;
2500 }
2501 $collection = array_reverse($collection);
2502 }
2503 }
2504
2505 return $collection;
2506 }
2507
2508 function get_the_loop_group_name($with_id = false)
2509 {
2510 $loop_name = $with_id ? $this->id : '';
2511 $curr = $this->get_the_current_loop();
2512
2513 // copy _loop_stack to prevent internal pointer ruined
2514 $loop_stack = $this->get_the_loop_collection();
2515 // print_r($loop_stack);
2516 foreach ($loop_stack as $loop)
2517 {
2518 $loop_name .= '[' . $loop->name . '][' . $loop->current . ']';
2519
2520 if($loop->name === $curr->name)
2521 break;
2522 }
2523 return $loop_name;
2524 }
2525
2526 function get_the_loop_level()
2527 {
2528 $curr = $this->get_the_current_loop();
2529 $depth = 0;
2530
2531 // copy _loop_stack to prevent internal pointer ruined
2532 $loop_stack = $this->get_the_loop_collection();
2533 foreach ($loop_stack as $loop)
2534 {
2535 if($loop->name === $curr->name)
2536 break;
2537 $depth++;
2538 }
2539 return $depth;
2540 }
2541
2542 function get_the_loop_group_id()
2543 {
2544 $loop_name = '';
2545 $curr = $this->get_the_current_loop();
2546
2547 // copy _loop_stack to prevent internal pointer ruined
2548 $loop_stack = $this->get_the_loop_collection();
2549 foreach ($loop_stack as $key => $loop)
2550 {
2551 $is_first = false;
2552 $is_last = false;
2553
2554 reset($loop_stack);
2555 if ($key === key($loop_stack))
2556 $is_first = true;
2557 if ($loop->name === $curr->name)
2558 $is_last = true;
2559
2560 $loop_name .= '[' . $loop->name . ']';
2561
2562 if(!$is_last)
2563 $loop_name .= '[' . $loop->current . ']';
2564
2565 if($loop->name === $curr->name)
2566 break;
2567 }
2568 return $loop_name;
2569 }
2570
2571 function get_the_loop_group_name_array($with_id = false)
2572 {
2573 $loop_name = array();
2574 $curr = $this->get_the_current_loop();
2575
2576 if($with_id)
2577 {
2578 $loop_name[] = $this->id;
2579 }
2580
2581 // copy _loop_stack to prevent internal pointer ruined
2582 $loop_stack = $this->get_the_loop_collection();
2583 foreach ($loop_stack as $loop)
2584 {
2585 $loop_name[] = $loop->name;
2586 $loop_name[] = $loop->current;
2587
2588 if($loop->name === $curr->name)
2589 break;
2590 }
2591 return $loop_name;
2592 }
2593
2594 function get_the_dotted_loop_group_name($with_id = false)
2595 {
2596 $loop_name = $with_id ? $this->id : '';
2597 $curr = $this->get_the_current_loop();
2598
2599 // copy _loop_stack to prevent internal pointer ruined
2600 $loop_stack = $this->get_the_loop_collection();
2601 foreach ($loop_stack as $loop)
2602 {
2603 $loop_name .= ($loop_name === '' ? '' : '.') . $loop->name . '.' . $loop->current;
2604
2605 if($loop->name === $curr->name)
2606 break;
2607 }
2608 return $loop_name;
2609 }
2610
2611 function get_meta_by_dotted($dotted)
2612 {
2613 $keys = explode('.', $dotted);
2614 $meta = $this->meta;
2615 foreach ($keys as $key)
2616 {
2617 if(array_key_exists($key, $meta))
2618 {
2619 $meta = $meta[$key];
2620 }
2621 else
2622 {
2623 return null;
2624 }
2625 }
2626 return $meta;
2627 }
2628
2629 function get_meta_by_array($arr)
2630 {
2631 $meta = $this->meta;
2632
2633 if(!is_array($arr) || !is_array($meta) || is_null($meta))
2634 return null;
2635
2636 foreach ($arr as $key)
2637 {
2638 if(is_array($meta) and array_key_exists($key, $meta))
2639 {
2640 $meta = $meta[$key];
2641 }
2642 else
2643 {
2644 return null;
2645 }
2646 }
2647 return $meta;
2648 }
2649
2650 function get_the_current_group_count()
2651 {
2652 $arr = $this->get_the_loop_group_name_array();
2653 end($arr);
2654 $last = key($arr);
2655 unset($arr[$last]);
2656 $meta = $this->get_meta_by_array($arr);
2657 return count($meta);
2658 }
2659
2660 function increment_current_loop()
2661 {
2662 current($this->_loop_stack)->current++;
2663 }
2664
2665 function get_the_current_loop()
2666 {
2667 return current($this->_loop_stack);
2668 }
2669
2670 function is_in_multi_last()
2671 {
2672 // copy _loop_stack to prevent internal pointer ruined
2673 $loop_stack = $this->get_the_loop_collection();
2674 foreach ($loop_stack as $loop)
2675 {
2676 if($loop->type === 'multi' and $loop->is_last())
2677 return true;
2678 }
2679 return false;
2680 }
2681
2682 function is_in_loop()
2683 {
2684 if(current($this->_loop_stack) === false)
2685 return false;
2686 return true;
2687 }
2688
2689 function is_parent_multi()
2690 {
2691 // copy _loop_stack to prevent internal pointer ruined
2692 $loop_stack = $this->get_the_loop_collection();
2693 foreach ($loop_stack as $loop)
2694 {
2695 if($loop->type === 'multi')
2696 return true;
2697 }
2698 return false;
2699 }
2700
2701 function the_copy_button_class()
2702 {
2703 $curr = $this->get_the_current_loop();
2704 return 'docopy-' . $curr->get_the_indexed_name();
2705 }
2706
2707 }
2708
2709 class WPA_Loop
2710 {
2711
2712 public $length = 0;
2713
2714 public $parent = NULL;
2715
2716 public $current = -1;
2717
2718 public $name = NULL;
2719
2720 public $type = false;
2721
2722 function __construct($name, $length, $type)
2723 {
2724 $this->name = $name;
2725 $this->length = $length;
2726 $this->type = $type;
2727 }
2728
2729 function the_indexed_name()
2730 {
2731 echo $this->get_the_indexed_name();
2732 }
2733
2734 function get_the_indexed_name()
2735 {
2736 return $this->name . '[' . $this->current . ']';
2737 }
2738
2739 function is_first()
2740 {
2741 if ( $this->current == 0 ) return TRUE;
2742
2743 return FALSE;
2744 }
2745
2746 function is_last()
2747 {
2748 if ( ( $this->current + 1 ) == $this->length ) return TRUE;
2749
2750 return FALSE;
2751 }
2752
2753 }
2754
2755 /* eof */