PageRenderTime 61ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/vendor/farinspace/WPAlchemy/WPAlchemy/MetaBox.php

http://github.com/beingzoe/wpplugin-kitchen-sink-html5-base
PHP | 2237 lines | 1176 code | 329 blank | 732 comment | 175 complexity | ccabf4dd81f89e09fc701d4113978666 MD5 | raw file
Possible License(s): MIT
  1. <?php
  2. /**
  3. * @author Dimas Begunoff
  4. * @copyright Copyright (c) 2009, Dimas Begunoff, http://farinspace.com
  5. * @license http://en.wikipedia.org/wiki/MIT_License The MIT License
  6. * @package WPAlchemy
  7. * @version 1.4.2
  8. * @link http://github.com/farinspace/wpalchemy
  9. * @link http://farinspace.com
  10. */
  11. // todo: perhaps move _global_head and _global_foot locally, when first run
  12. // define a constant to prevent other instances from running again ...
  13. add_action('admin_head', array('WPAlchemy_MetaBox', '_global_head'));
  14. add_action('admin_footer', array('WPAlchemy_MetaBox', '_global_foot'));
  15. define('WPALCHEMY_MODE_ARRAY', 'array');
  16. define('WPALCHEMY_MODE_EXTRACT', 'extract');
  17. define('WPALCHEMY_FIELD_HINT_TEXT', 'text');
  18. define('WPALCHEMY_FIELD_HINT_TEXTAREA', 'textarea');
  19. define('WPALCHEMY_FIELD_HINT_CHECKBOX', 'checkbox');
  20. define('WPALCHEMY_FIELD_HINT_CHECKBOX_MULTI', 'checkbox_multi');
  21. define('WPALCHEMY_FIELD_HINT_RADIO', 'radio');
  22. define('WPALCHEMY_FIELD_HINT_SELECT', 'select');
  23. define('WPALCHEMY_FIELD_HINT_SELECT_MULTI', 'select_multi');
  24. // depreciated, use WPALCHEMY_FIELD_HINT_SELECT_MULTI instead
  25. define('WPALCHEMY_FIELD_HINT_SELECT_MULTIPLE', 'select_multiple');
  26. define('WPALCHEMY_LOCK_TOP', 'top');
  27. define('WPALCHEMY_LOCK_BOTTOM', 'bottom');
  28. define('WPALCHEMY_LOCK_BEFORE_POST_TITLE', 'before_post_title');
  29. define('WPALCHEMY_LOCK_AFTER_POST_TITLE', 'after_post_title');
  30. define('WPALCHEMY_VIEW_START_OPENED', 'opened');
  31. define('WPALCHEMY_VIEW_START_CLOSED', 'closed');
  32. define('WPALCHEMY_VIEW_ALWAYS_OPENED', 'always_opened');
  33. class WPAlchemy_MetaBox
  34. {
  35. /**
  36. * User defined identifier for the meta box, prefix with an underscore to
  37. * prevent option(s) form showing up in the custom fields meta box, this
  38. * option should be used when instantiating the class.
  39. *
  40. * @since 1.0
  41. * @access public
  42. * @var string required
  43. */
  44. var $id;
  45. /**
  46. * Used to set the title of the meta box, this option should be used when
  47. * instantiating the class.
  48. *
  49. * @since 1.0
  50. * @access public
  51. * @var string required
  52. * @see $hide_title
  53. */
  54. var $title = 'Custom Meta';
  55. /**
  56. * Used to set the meta box content, the contents of your meta box should be
  57. * defined within this file, this option should be used when instantiating
  58. * the class.
  59. *
  60. * @since 1.0
  61. * @access public
  62. * @var string required
  63. */
  64. var $template;
  65. /**
  66. * Used to set the post types that the meta box can appear in, this option
  67. * should be used when instantiating the class.
  68. *
  69. * @since 1.0
  70. * @access public
  71. * @var array
  72. */
  73. var $types;
  74. /**
  75. * @since 1.0
  76. * @access public
  77. * @var bool
  78. */
  79. var $context = 'normal';
  80. /**
  81. * @since 1.0
  82. * @access public
  83. * @var bool
  84. */
  85. var $priority = 'high';
  86. /**
  87. * @since 1.0
  88. * @access public
  89. * @var bool
  90. */
  91. var $autosave = TRUE;
  92. /**
  93. * Used to set how the class does its data storage, data will be stored as
  94. * an associative array in a single meta entry in the wp_postmeta table or
  95. * data can be set and individual entries in the wp_postmeta table, the
  96. * following constants should be used when setting this option,
  97. * WPALCHEMY_MODE_ARRAY (default) and WPALCHEMY_MODE_EXTRACT, this option
  98. * should be used when instantiating the class.
  99. *
  100. * @since 1.2
  101. * @access public
  102. * @var string
  103. */
  104. var $mode = WPALCHEMY_MODE_ARRAY;
  105. /**
  106. * When the mode option is set to WPALCHEMY_MODE_EXTRACT, you have to take
  107. * care to avoid name collisions with other meta entries. Use this option to
  108. * automatically add a prefix to your variables, this option should be used
  109. * when instantiating the class.
  110. *
  111. * @since 1.2
  112. * @access public
  113. * @var array
  114. */
  115. var $prefix;
  116. /**
  117. * @since 1.0
  118. * @access public
  119. * @var bool
  120. */
  121. var $exclude_template;
  122. /**
  123. * @since 1.0
  124. * @access public
  125. * @var bool
  126. */
  127. var $exclude_category_id;
  128. /**
  129. * @since 1.0
  130. * @access public
  131. * @var bool
  132. */
  133. var $exclude_category;
  134. /**
  135. * @since 1.0
  136. * @access public
  137. * @var bool
  138. */
  139. var $exclude_tag_id;
  140. /**
  141. * @since 1.0
  142. * @access public
  143. * @var bool
  144. */
  145. var $exclude_tag;
  146. /**
  147. * @since 1.0
  148. * @access public
  149. * @var bool
  150. */
  151. var $exclude_post_id;
  152. /**
  153. * @since 1.0
  154. * @access public
  155. * @var bool
  156. */
  157. var $include_template;
  158. /**
  159. * @since 1.0
  160. * @access public
  161. * @var bool
  162. */
  163. var $include_category_id;
  164. /**
  165. * @since 1.0
  166. * @access public
  167. * @var bool
  168. */
  169. var $include_category;
  170. /**
  171. * @since 1.0
  172. * @access public
  173. * @var bool
  174. */
  175. var $include_tag_id;
  176. /**
  177. * @since 1.0
  178. * @access public
  179. * @var bool
  180. */
  181. var $include_tag;
  182. /**
  183. * @since 1.0
  184. * @access public
  185. * @var bool
  186. */
  187. var $include_post_id;
  188. /**
  189. * Callback used on the WordPress "admin_init" action, the main benefit is
  190. * that this callback is executed only when the meta box is present, this
  191. * option should be used when instantiating the class.
  192. *
  193. * @since 1.3.4
  194. * @access public
  195. * @var string|array optional
  196. */
  197. var $init_action;
  198. /**
  199. * Callback used to override when the meta box gets displayed, must return
  200. * true or false to determine if the meta box should or should not be
  201. * displayed, this option should be used when instantiating the class.
  202. *
  203. * @since 1.3
  204. * @access public
  205. * @var string|array optional
  206. * @param array $post_id first variable passed to the callback function
  207. * @see can_output()
  208. */
  209. var $output_filter;
  210. /**
  211. * Callback used to override or insert meta data before saving, you can halt
  212. * saving by passing back FALSE (return FALSE), this option should be used
  213. * when instantiating the class.
  214. *
  215. * @since 1.3
  216. * @access public
  217. * @var string|array optional
  218. * @param array $meta meta box data, first variable passed to the callback function
  219. * @param string $post_id second variable passed to the callback function
  220. * @see $save_action, add_filter()
  221. */
  222. var $save_filter;
  223. /**
  224. * Callback used to execute custom code after saving, this option should be
  225. * used when instantiating the class.
  226. *
  227. * @since 1.3
  228. * @access public
  229. * @var string|array optional
  230. * @param array $meta meta box data, first variable passed to the callback function
  231. * @param string $post_id second variable passed to the callback function
  232. * @see $save_filter, add_filter()
  233. */
  234. var $save_action;
  235. /**
  236. * Callback used to override or insert STYLE or SCRIPT tags into the head,
  237. * this option should be used when instantiating the class.
  238. *
  239. * @since 1.3
  240. * @access public
  241. * @var string|array optional
  242. * @param array $content current head content, first variable passed to the callback function
  243. * @see $head_action, add_filter()
  244. */
  245. var $head_filter;
  246. /**
  247. * Callback used to insert STYLE or SCRIPT tags into the head,
  248. * this option should be used when instantiating the class.
  249. *
  250. * @since 1.3
  251. * @access public
  252. * @var string|array optional
  253. * @see $head_filter, add_action()
  254. */
  255. var $head_action;
  256. /**
  257. * Callback used to override or insert SCRIPT tags into the footer, this
  258. * option should be used when instantiating the class.
  259. *
  260. * @since 1.3
  261. * @access public
  262. * @var string|array optional
  263. * @param array $content current foot content, first variable passed to the callback function
  264. * @see $foot_action, add_filter()
  265. */
  266. var $foot_filter;
  267. /**
  268. * Callback used to insert SCRIPT tags into the footer, this option should
  269. * be used when instantiating the class.
  270. *
  271. * @since 1.3
  272. * @access public
  273. * @var string|array optional
  274. * @see $foot_filter, add_action()
  275. */
  276. var $foot_action;
  277. /**
  278. * Used to hide the default content editor in a page or post, this option
  279. * should be used when instantiating the class.
  280. *
  281. * @since 1.3
  282. * @access public
  283. * @var bool optional
  284. */
  285. var $hide_editor = FALSE;
  286. /**
  287. * Used to hide the meta box title, this option should be used when
  288. * instantiating the class.
  289. *
  290. * @since 1.3
  291. * @access public
  292. * @var bool optional
  293. * @see $title
  294. */
  295. var $hide_title = FALSE;
  296. /**
  297. * Used to lock a meta box in place, possible values are: top, bottom,
  298. * before_post_title, after_post_title, this option should be used when
  299. * instantiating the class.
  300. *
  301. * @since 1.3.3
  302. * @access public
  303. * @var string optional possible values are: top, bottom, before_post_title, after_post_title
  304. */
  305. var $lock;
  306. /**
  307. * Used to lock a meta box at top (below the default content editor), this
  308. * option should be used when instantiating the class.
  309. *
  310. * @deprecated deprecated since version 1.3.3
  311. * @since 1.3
  312. * @access public
  313. * @var bool optional
  314. * @see $lock
  315. */
  316. var $lock_on_top = FALSE;
  317. /**
  318. * Used to lock a meta box at bottom, this option should be used when
  319. * instantiating the class.
  320. *
  321. * @deprecated deprecated since version 1.3.3
  322. * @since 1.3
  323. * @access public
  324. * @var bool optional
  325. * @see $lock
  326. */
  327. var $lock_on_bottom = FALSE;
  328. /**
  329. * Used to set the initial view state of the meta box, possible values are:
  330. * opened, closed, always_opened, this option should be used when
  331. * instantiating the class.
  332. *
  333. * @since 1.3.3
  334. * @access public
  335. * @var string optional possible values are: opened, closed, always_opened
  336. */
  337. var $view;
  338. /**
  339. * Used to hide the show/hide checkbox option from the screen options area,
  340. * this option should be used when instantiating the class.
  341. *
  342. * @since 1.3.4
  343. * @access public
  344. * @var bool optional
  345. */
  346. var $hide_screen_option = FALSE;
  347. // private
  348. var $meta;
  349. var $name;
  350. var $subname;
  351. /**
  352. * Used to provide field type hinting
  353. *
  354. * @since 1.3
  355. * @access private
  356. * @var string
  357. * @see the_field()
  358. */
  359. var $hint;
  360. var $length = 0;
  361. var $current = -1;
  362. var $in_loop = FALSE;
  363. var $in_template = FALSE;
  364. var $group_tag;
  365. var $current_post_id;
  366. /**
  367. * Used to store current loop details, cleared after loop ends
  368. *
  369. * @since 1.4
  370. * @access private
  371. * @var stdClass
  372. * @see have_fields_and_multi(), have_fields()
  373. */
  374. var $_loop_data;
  375. function WPAlchemy_MetaBox($arr)
  376. {
  377. $this->_loop_data = new stdClass;
  378. $this->meta = array();
  379. $this->types = array('post', 'page');
  380. if (is_array($arr))
  381. {
  382. foreach ($arr as $n => $v)
  383. {
  384. $this->$n = $v;
  385. }
  386. if (empty($this->id)) die('Meta box ID required');
  387. if (is_numeric($this->id)) die('Meta box ID must be a string');
  388. if (empty($this->template)) die('Meta box template file required');
  389. // check for nonarray values
  390. $exc_inc = array
  391. (
  392. 'exclude_template',
  393. 'exclude_category_id',
  394. 'exclude_category',
  395. 'exclude_tag_id',
  396. 'exclude_tag',
  397. 'exclude_post_id',
  398. 'include_template',
  399. 'include_category_id',
  400. 'include_category',
  401. 'include_tag_id',
  402. 'include_tag',
  403. 'include_post_id'
  404. );
  405. foreach ($exc_inc as $v)
  406. {
  407. // ideally the exclude and include values should be in array form, convert to array otherwise
  408. if (!empty($this->$v) AND !is_array($this->$v))
  409. {
  410. $this->$v = array_map('trim',explode(',',$this->$v));
  411. }
  412. }
  413. // convert depreciated variables
  414. if ($this->lock_on_top) $this->lock = WPALCHEMY_LOCK_TOP;
  415. elseif ($this->lock_on_bottom) $this->lock = WPALCHEMY_LOCK_BOTTOM;
  416. add_action('admin_init', array($this,'_init'));
  417. // uses the default wordpress-importer plugin hook
  418. add_action('import_post_meta', array($this, '_import'), 10, 3);
  419. }
  420. else
  421. {
  422. die('Associative array parameters required');
  423. }
  424. }
  425. /**
  426. * Used to correct double serialized data during post/page export/import
  427. *
  428. * @since 1.3.16
  429. * @access private
  430. */
  431. function _import($post_id, $key, $value)
  432. {
  433. if (WPALCHEMY_MODE_ARRAY == $this->mode AND $key == $this->id)
  434. {
  435. // maybe_unserialize fixes a wordpress bug which double serializes already serialized data during export/import
  436. update_post_meta($post_id, $key, maybe_unserialize(stripslashes($value)));
  437. }
  438. }
  439. /**
  440. * Used to initialize the meta box, runs on WordPress admin_init action,
  441. * properly calls internal WordPress methods
  442. *
  443. * @since 1.0
  444. * @access private
  445. */
  446. function _init()
  447. {
  448. // must be creating or editing a post or page
  449. if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
  450. if ( ! empty($this->output_filter))
  451. {
  452. $this->add_filter('output', $this->output_filter);
  453. }
  454. if ($this->can_output())
  455. {
  456. foreach ($this->types as $type)
  457. {
  458. add_meta_box($this->id . '_metabox', $this->title, array($this, '_setup'), $type, $this->context, $this->priority);
  459. }
  460. add_action('save_post', array($this,'_save'));
  461. $filters = array('save', 'head', 'foot');
  462. foreach ($filters as $filter)
  463. {
  464. $var = $filter . '_filter';
  465. if (!empty($this->$var))
  466. {
  467. if ('save' == $filter)
  468. {
  469. $this->add_filter($filter, $this->$var, 10, 2);
  470. }
  471. else
  472. {
  473. $this->add_filter($filter, $this->$var);
  474. }
  475. }
  476. }
  477. $actions = array('save', 'head', 'foot', 'init');
  478. foreach ($actions as $action)
  479. {
  480. $var = $action . '_action';
  481. if (!empty($this->$var))
  482. {
  483. if ('save' == $action)
  484. {
  485. $this->add_action($action, $this->$var, 10, 2);
  486. }
  487. else
  488. {
  489. $this->add_action($action, $this->$var);
  490. }
  491. }
  492. }
  493. add_action('admin_head', array($this,'_head'), 11);
  494. add_action('admin_footer', array($this,'_foot'), 11);
  495. // action: init
  496. if ($this->has_action('init'))
  497. {
  498. $this->do_action('init');
  499. }
  500. }
  501. }
  502. /**
  503. * Used to insert STYLE or SCRIPT tags into the head, called on WordPress
  504. * admin_head action.
  505. *
  506. * @since 1.3
  507. * @access private
  508. * @see _foot()
  509. */
  510. function _head()
  511. {
  512. $content = NULL;
  513. ob_start();
  514. ?>
  515. <style type="text/css">
  516. <?php if ($this->hide_editor): ?> #postdiv, #postdivrich { display:none; } <?php endif; ?>
  517. </style>
  518. <?php
  519. $content = ob_get_contents();
  520. ob_end_clean();
  521. // filter: head
  522. if ($this->has_filter('head'))
  523. {
  524. $content = $this->apply_filters('head', $content);
  525. }
  526. echo $content;
  527. // action: head
  528. if ($this->has_action('head'))
  529. {
  530. $this->do_action('head');
  531. }
  532. }
  533. /**
  534. * Used to insert SCRIPT tags into the footer, called on WordPress
  535. * admin_footer action.
  536. *
  537. * @since 1.3
  538. * @access private
  539. * @see _head()
  540. */
  541. function _foot()
  542. {
  543. $content = NULL;
  544. if
  545. (
  546. $this->lock OR
  547. $this->hide_title OR
  548. $this->view OR
  549. $this->hide_screen_option
  550. )
  551. {
  552. ob_start();
  553. ?>
  554. <script type="text/javascript">
  555. /* <![CDATA[ */
  556. (function($){ /* not using jQuery ondomready, code runs right away in footer */
  557. var mb_id = '<?php echo $this->id; ?>';
  558. var mb = $('#' + mb_id + '_metabox');
  559. <?php if (WPALCHEMY_LOCK_TOP == $this->lock): ?>
  560. <?php if ('side' == $this->context): ?>
  561. var id = 'wpalchemy-side-top';
  562. if ( ! $('#'+id).length)
  563. {
  564. $('<div></div>').attr('id',id).prependTo('#side-info-column');
  565. }
  566. <?php else: ?>
  567. var id = 'wpalchemy-content-top';
  568. if ( ! $('#'+id).length)
  569. {
  570. $('<div></div>').attr('id',id).insertAfter('#postdiv, #postdivrich');
  571. }
  572. <?php endif; ?>
  573. $('#'+id).append(mb);
  574. <?php elseif (WPALCHEMY_LOCK_BOTTOM == $this->lock): ?>
  575. <?php if ('side' == $this->context): ?>
  576. var id = 'wpalchemy-side-bottom';
  577. if ( ! $('#'+id).length)
  578. {
  579. $('<div></div>').attr('id',id).appendTo('#side-info-column');
  580. }
  581. <?php else: ?>
  582. if ( ! $('#advanced-sortables').children().length)
  583. {
  584. $('#advanced-sortables').css('display','none');
  585. }
  586. var id = 'wpalchemy-content-bottom';
  587. if ( ! $('#'+id).length)
  588. {
  589. $('<div></div>').attr('id',id).insertAfter('#advanced-sortables');
  590. }
  591. <?php endif; ?>
  592. $('#'+id).append(mb);
  593. <?php elseif (WPALCHEMY_LOCK_BEFORE_POST_TITLE == $this->lock): ?>
  594. <?php if ('side' != $this->context): ?>
  595. var id = 'wpalchemy-content-bpt';
  596. if ( ! $('#'+id).length)
  597. {
  598. $('<div></div>').attr('id',id).prependTo('#post-body-content');
  599. }
  600. $('#'+id).append(mb);
  601. <?php endif; ?>
  602. <?php elseif (WPALCHEMY_LOCK_AFTER_POST_TITLE == $this->lock): ?>
  603. <?php if ('side' != $this->context): ?>
  604. var id = 'wpalchemy-content-apt';
  605. if ( ! $('#'+id).length)
  606. {
  607. $('<div></div>').attr('id',id).insertAfter('#titlediv');
  608. }
  609. $('#'+id).append(mb);
  610. <?php endif; ?>
  611. <?php endif; ?>
  612. <?php if ( ! empty($this->lock)): ?>
  613. $('.hndle', mb).css('cursor','pointer');
  614. $('.handlediv', mb).remove();
  615. <?php endif; ?>
  616. <?php if ($this->hide_title): ?>
  617. $('.hndle', mb).remove();
  618. $('.handlediv', mb).remove();
  619. mb.removeClass('closed'); /* start opened */
  620. <?php endif; ?>
  621. <?php if (WPALCHEMY_VIEW_START_OPENED == $this->view): ?>
  622. mb.removeClass('closed');
  623. <?php elseif (WPALCHEMY_VIEW_START_CLOSED == $this->view): ?>
  624. mb.addClass('closed');
  625. <?php elseif (WPALCHEMY_VIEW_ALWAYS_OPENED == $this->view): ?>
  626. /* todo: need to find a way to add this script block below, load-scripts.php?... */
  627. var h3 = mb.children('h3');
  628. setTimeout(function(){ h3.unbind('click'); }, 1000);
  629. $('.handlediv', mb).remove();
  630. mb.removeClass('closed'); /* start opened */
  631. $('.hndle', mb).css('cursor','auto');
  632. <?php endif; ?>
  633. <?php if ($this->hide_screen_option): ?>
  634. $('.metabox-prefs label[for='+ mb_id +'_metabox-hide]').remove();
  635. <?php endif; ?>
  636. mb = null;
  637. })(jQuery);
  638. /* ]]> */
  639. </script>
  640. <?php
  641. $content = ob_get_contents();
  642. ob_end_clean();
  643. }
  644. // filter: foot
  645. if ($this->has_filter('foot'))
  646. {
  647. $content = $this->apply_filters('foot', $content);
  648. }
  649. echo $content;
  650. // action: foot
  651. if ($this->has_action('foot'))
  652. {
  653. $this->do_action('foot');
  654. }
  655. }
  656. /**
  657. * Used to setup the meta box content template
  658. *
  659. * @since 1.0
  660. * @access private
  661. * @see _init()
  662. */
  663. function _setup()
  664. {
  665. $this->in_template = TRUE;
  666. // also make current post data available
  667. global $post;
  668. // shortcuts
  669. $mb =& $this;
  670. $metabox =& $this;
  671. $id = $this->id;
  672. $meta = $this->_meta(NULL, TRUE);
  673. // use include because users may want to use one templete for multiple meta boxes
  674. include $this->template;
  675. // create a nonce for verification
  676. echo '<input type="hidden" name="'. $this->id .'_nonce" value="' . wp_create_nonce($this->id) . '" />';
  677. $this->in_template = FALSE;
  678. }
  679. /**
  680. * Used to properly prefix the filter tag, the tag is unique to the meta
  681. * box instance
  682. *
  683. * @since 1.3
  684. * @access private
  685. * @param string $tag name of the filter
  686. * @return string uniquely prefixed tag name
  687. */
  688. function _get_filter_tag($tag)
  689. {
  690. $prefix = 'wpalchemy_filter_' . $this->id . '_';
  691. $prefix = preg_replace('/_+/', '_', $prefix);
  692. $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
  693. return $prefix . $tag;
  694. }
  695. /**
  696. * Uses WordPress add_filter() function, see WordPress add_filter()
  697. *
  698. * @since 1.3
  699. * @access public
  700. * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L65
  701. */
  702. function add_filter($tag, $function_to_add, $priority = 10, $accepted_args = 1)
  703. {
  704. $tag = $this->_get_filter_tag($tag);;
  705. add_filter($tag, $function_to_add, $priority, $accepted_args);
  706. }
  707. /**
  708. * Uses WordPress has_filter() function, see WordPress has_filter()
  709. *
  710. * @since 1.3
  711. * @access public
  712. * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L86
  713. */
  714. function has_filter($tag, $function_to_check = FALSE)
  715. {
  716. $tag = $this->_get_filter_tag($tag);
  717. return has_filter($tag, $function_to_check);
  718. }
  719. /**
  720. * Uses WordPress apply_filters() function, see WordPress apply_filters()
  721. *
  722. * @since 1.3
  723. * @access public
  724. * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L134
  725. */
  726. function apply_filters($tag, $value)
  727. {
  728. $args = func_get_args();
  729. $args[0] = $this->_get_filter_tag($tag);
  730. return call_user_func_array('apply_filters', $args);
  731. }
  732. /**
  733. * Uses WordPress remove_filter() function, see WordPress remove_filter()
  734. *
  735. * @since 1.3
  736. * @access public
  737. * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L250
  738. */
  739. function remove_filter($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
  740. {
  741. $tag = $this->_get_filter_tag($tag);
  742. return remove_filter($tag, $function_to_remove, $priority, $accepted_args);
  743. }
  744. /**
  745. * Used to properly prefix the action tag, the tag is unique to the meta
  746. * box instance
  747. *
  748. * @since 1.3
  749. * @access private
  750. * @param string $tag name of the action
  751. * @return string uniquely prefixed tag name
  752. */
  753. function _get_action_tag($tag)
  754. {
  755. $prefix = 'wpalchemy_action_' . $this->id . '_';
  756. $prefix = preg_replace('/_+/', '_', $prefix);
  757. $tag = preg_replace('/^'. $prefix .'/i', '', $tag);
  758. return $prefix . $tag;
  759. }
  760. /**
  761. * Uses WordPress add_action() function, see WordPress add_action()
  762. *
  763. * @since 1.3
  764. * @access public
  765. * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L324
  766. */
  767. function add_action($tag, $function_to_add, $priority = 10, $accepted_args = 1)
  768. {
  769. $tag = $this->_get_action_tag($tag);
  770. add_action($tag, $function_to_add, $priority, $accepted_args);
  771. }
  772. /**
  773. * Uses WordPress has_action() function, see WordPress has_action()
  774. *
  775. * @since 1.3
  776. * @access public
  777. * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L492
  778. */
  779. function has_action($tag, $function_to_check = FALSE)
  780. {
  781. $tag = $this->_get_action_tag($tag);
  782. return has_action($tag, $function_to_check);
  783. }
  784. /**
  785. * Uses WordPress remove_action() function, see WordPress remove_action()
  786. *
  787. * @since 1.3
  788. * @access public
  789. * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L513
  790. */
  791. function remove_action($tag, $function_to_remove, $priority = 10, $accepted_args = 1)
  792. {
  793. $tag = $this->_get_action_tag($tag);
  794. return remove_action($tag, $function_to_remove, $priority, $accepted_args);
  795. }
  796. /**
  797. * Uses WordPress do_action() function, see WordPress do_action()
  798. * @since 1.3
  799. * @access public
  800. * @link http://core.trac.wordpress.org/browser/trunk/wp-includes/plugin.php#L352
  801. */
  802. function do_action($tag, $arg = '')
  803. {
  804. $args = func_get_args();
  805. $args[0] = $this->_get_action_tag($tag);
  806. return call_user_func_array('do_action', $args);
  807. }
  808. /**
  809. * Used to check if creating a new post or editing one
  810. *
  811. * @static
  812. * @since 1.3.7
  813. * @access private
  814. * @return bool
  815. * @see _is_page()
  816. */
  817. function _is_post()
  818. {
  819. if ('post' == WPAlchemy_MetaBox::_is_post_or_page())
  820. {
  821. return TRUE;
  822. }
  823. return FALSE;
  824. }
  825. /**
  826. * Used to check if creating a new page or editing one
  827. *
  828. * @static
  829. * @since 1.3.7
  830. * @access private
  831. * @return bool
  832. * @see _is_post()
  833. */
  834. function _is_page()
  835. {
  836. if ('page' == WPAlchemy_MetaBox::_is_post_or_page())
  837. {
  838. return TRUE;
  839. }
  840. return FALSE;
  841. }
  842. /**
  843. * Used to check if creating or editing a post or page
  844. *
  845. * @static
  846. * @since 1.3.8
  847. * @access private
  848. * @return string "post" or "page"
  849. * @see _is_post(), _is_page()
  850. */
  851. function _is_post_or_page()
  852. {
  853. $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : NULL ;
  854. $file = basename(parse_url($uri, PHP_URL_PATH));
  855. if ($uri AND in_array($file, array('post.php', 'post-new.php')))
  856. {
  857. $post_id = isset($_GET['post']) ? $_GET['post'] : NULL ;
  858. $post_type = isset($_GET['post_type']) ? $_GET['post_type'] : NULL ;
  859. $post_type = $post_id ? get_post_type($post_id) : $post_type ;
  860. if ('page' == $post_type)
  861. {
  862. return 'page';
  863. }
  864. else
  865. {
  866. return 'post';
  867. }
  868. }
  869. return NULL;
  870. }
  871. /**
  872. * @since 1.0
  873. */
  874. function can_output()
  875. {
  876. global $post;
  877. $p_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : '' ;
  878. $g_post_id = isset($_GET['post']) ? $_GET['post'] : '' ;
  879. $post_id = $g_post_id ? $g_post_id : $p_post_id ;
  880. $post_id = (!empty($post) AND $post->ID) ? $post->ID : $post_id ;
  881. if (!empty($this->exclude_template) OR !empty($this->include_template))
  882. {
  883. $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
  884. }
  885. if
  886. (
  887. !empty($this->exclude_category) OR
  888. !empty($this->exclude_category_id) OR
  889. !empty($this->include_category) OR
  890. !empty($this->include_category_id)
  891. )
  892. {
  893. $categories = wp_get_post_categories($post_id,'fields=all');
  894. }
  895. if
  896. (
  897. !empty($this->exclude_tag) OR
  898. !empty($this->exclude_tag_id) OR
  899. !empty($this->include_tag) OR
  900. !empty($this->include_tag_id)
  901. )
  902. {
  903. $tags = wp_get_post_tags($post_id);
  904. }
  905. // processing order: "exclude" then "include"
  906. // processing order: "template" then "category" then "post"
  907. $can_output = TRUE; // include all
  908. if
  909. (
  910. !empty($this->exclude_template) OR
  911. !empty($this->exclude_category_id) OR
  912. !empty($this->exclude_category) OR
  913. !empty($this->exclude_tag_id) OR
  914. !empty($this->exclude_tag) OR
  915. !empty($this->exclude_post_id) OR
  916. !empty($this->include_template) OR
  917. !empty($this->include_category_id) OR
  918. !empty($this->include_category) OR
  919. !empty($this->include_tag_id) OR
  920. !empty($this->include_tag) OR
  921. !empty($this->include_post_id)
  922. )
  923. {
  924. if (!empty($this->exclude_template))
  925. {
  926. if (in_array($template_file,$this->exclude_template))
  927. {
  928. $can_output = FALSE;
  929. }
  930. }
  931. if (!empty($this->exclude_category_id))
  932. {
  933. foreach ($categories as $cat)
  934. {
  935. if (in_array($cat->term_id,$this->exclude_category_id))
  936. {
  937. $can_output = FALSE;
  938. break;
  939. }
  940. }
  941. }
  942. if (!empty($this->exclude_category))
  943. {
  944. foreach ($categories as $cat)
  945. {
  946. if
  947. (
  948. in_array($cat->slug,$this->exclude_category) OR
  949. in_array($cat->name,$this->exclude_category)
  950. )
  951. {
  952. $can_output = FALSE;
  953. break;
  954. }
  955. }
  956. }
  957. if (!empty($this->exclude_tag_id))
  958. {
  959. foreach ($tags as $tag)
  960. {
  961. if (in_array($tag->term_id,$this->exclude_tag_id))
  962. {
  963. $can_output = FALSE;
  964. break;
  965. }
  966. }
  967. }
  968. if (!empty($this->exclude_tag))
  969. {
  970. foreach ($tags as $tag)
  971. {
  972. if
  973. (
  974. in_array($tag->slug,$this->exclude_tag) OR
  975. in_array($tag->name,$this->exclude_tag)
  976. )
  977. {
  978. $can_output = FALSE;
  979. break;
  980. }
  981. }
  982. }
  983. if (!empty($this->exclude_post_id))
  984. {
  985. if (in_array($post_id,$this->exclude_post_id))
  986. {
  987. $can_output = FALSE;
  988. }
  989. }
  990. // excludes are not set use "include only" mode
  991. if
  992. (
  993. empty($this->exclude_template) AND
  994. empty($this->exclude_category_id) AND
  995. empty($this->exclude_category) AND
  996. empty($this->exclude_tag_id) AND
  997. empty($this->exclude_tag) AND
  998. empty($this->exclude_post_id)
  999. )
  1000. {
  1001. $can_output = FALSE;
  1002. }
  1003. if (!empty($this->include_template))
  1004. {
  1005. if (in_array($template_file,$this->include_template))
  1006. {
  1007. $can_output = TRUE;
  1008. }
  1009. }
  1010. if (!empty($this->include_category_id))
  1011. {
  1012. foreach ($categories as $cat)
  1013. {
  1014. if (in_array($cat->term_id,$this->include_category_id))
  1015. {
  1016. $can_output = TRUE;
  1017. break;
  1018. }
  1019. }
  1020. }
  1021. if (!empty($this->include_category))
  1022. {
  1023. foreach ($categories as $cat)
  1024. {
  1025. if
  1026. (
  1027. in_array($cat->slug,$this->include_category) OR
  1028. in_array($cat->name,$this->include_category)
  1029. )
  1030. {
  1031. $can_output = TRUE;
  1032. break;
  1033. }
  1034. }
  1035. }
  1036. if (!empty($this->include_tag_id))
  1037. {
  1038. foreach ($tags as $tag)
  1039. {
  1040. if (in_array($tag->term_id,$this->include_tag_id))
  1041. {
  1042. $can_output = TRUE;
  1043. break;
  1044. }
  1045. }
  1046. }
  1047. if (!empty($this->include_tag))
  1048. {
  1049. foreach ($tags as $tag)
  1050. {
  1051. if
  1052. (
  1053. in_array($tag->slug,$this->include_tag) OR
  1054. in_array($tag->name,$this->include_tag)
  1055. )
  1056. {
  1057. $can_output = TRUE;
  1058. break;
  1059. }
  1060. }
  1061. }
  1062. if (!empty($this->include_post_id))
  1063. {
  1064. if (in_array($post_id,$this->include_post_id))
  1065. {
  1066. $can_output = TRUE;
  1067. }
  1068. }
  1069. }
  1070. // $_GET['post_type'] used with post-new.php
  1071. $post_type = isset($_GET['post_type']) ? $_GET['post_type'] : NULL ;
  1072. // get_post_type() works only with existing posts or pages get_post_type($post_id);
  1073. $post_type = $post_type ? $post_type : get_post_type($post_id) ;
  1074. if (! empty($post_type) AND ! in_array($post_type, $this->types))
  1075. {
  1076. $can_output = FALSE;
  1077. }
  1078. // filter: output (can_output)
  1079. if ($this->has_filter('output'))
  1080. {
  1081. $can_output = $this->apply_filters('output', $post_id);
  1082. }
  1083. return $can_output;
  1084. }
  1085. /**
  1086. * Used to insert global STYLE or SCRIPT tags into the head, called on
  1087. * WordPress admin_footer action.
  1088. *
  1089. * @static
  1090. * @since 1.3
  1091. * @access private
  1092. * @see _global_foot()
  1093. */
  1094. function _global_head()
  1095. {
  1096. // must be creating or editing a post or page
  1097. if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
  1098. // todo: you're assuming people will want to use this exact functionality
  1099. // consider giving a developer access to change this via hooks/callbacks
  1100. // include javascript for special functionality
  1101. ?><style type="text/css"> .wpa_group.tocopy { display:none; } </style>
  1102. <script type="text/javascript">
  1103. /* <![CDATA[ */
  1104. jQuery(function($)
  1105. {
  1106. $(document).click(function(e)
  1107. {
  1108. var elem = $(e.target);
  1109. if (elem.attr('class') && elem.filter('[class*=dodelete]').length)
  1110. {
  1111. e.preventDefault();
  1112. var p = elem.parents('.postbox'); /*wp*/
  1113. var the_name = elem.attr('class').match(/dodelete-([a-zA-Z0-9_-]*)/i);
  1114. the_name = (the_name && the_name[1]) ? the_name[1] : null ;
  1115. /* todo: expose and allow editing of this message */
  1116. if (confirm('This action can not be undone, are you sure?'))
  1117. {
  1118. if (the_name)
  1119. {
  1120. $('.wpa_group-'+ the_name, p).not('.tocopy').remove();
  1121. }
  1122. else
  1123. {
  1124. elem.parents('.wpa_group').remove();
  1125. }
  1126. the_name = elem.parents('.wpa_group').attr('class').match(/wpa_group-([a-zA-Z0-9_-]*)/i)[1];
  1127. checkLoopLimit(the_name);
  1128. $.wpalchemy.trigger('wpa_delete');
  1129. }
  1130. }
  1131. });
  1132. $('[class*=docopy-]').click(function(e)
  1133. {
  1134. e.preventDefault();
  1135. var p = $(this).parents('.postbox'); /*wp*/
  1136. var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
  1137. var the_group = $('.wpa_group-'+ the_name +':first.tocopy', p);
  1138. var the_clone = the_group.clone().removeClass('tocopy');
  1139. var the_props = ['name', 'id', 'for'];
  1140. the_group.find('input, textarea, select, button, label').each(function(i,elem)
  1141. {
  1142. for (var j = 0; j < the_props.length; j++)
  1143. {
  1144. var the_prop = $(elem).attr(the_props[j]);
  1145. if (the_prop)
  1146. {
  1147. var the_match = the_prop.match(/\[(\d+)\]/i);
  1148. the_prop = the_prop.replace(the_match[0],'['+(+the_match[1]+1)+']');
  1149. $(elem).attr(the_props[j], the_prop);
  1150. }
  1151. }
  1152. });
  1153. if ($(this).hasClass('ontop'))
  1154. {
  1155. $('.wpa_group-'+ the_name +':first', p).before(the_clone);
  1156. }
  1157. else
  1158. {
  1159. the_group.before(the_clone);
  1160. }
  1161. checkLoopLimit(the_name);
  1162. $.wpalchemy.trigger('wpa_copy', [the_clone]);
  1163. });
  1164. function checkLoopLimit(name)
  1165. {
  1166. var elem = $('.docopy-' + name);
  1167. var the_match = $('.wpa_loop-' + name).attr('class').match(/wpa_loop_limit-([0-9]*)/i);
  1168. if (the_match)
  1169. {
  1170. var the_limit = the_match[1];
  1171. if ($('.wpa_group-' + name).not('.wpa_group.tocopy').length >= the_limit)
  1172. {
  1173. elem.hide();
  1174. }
  1175. else
  1176. {
  1177. elem.show();
  1178. }
  1179. }
  1180. }
  1181. /* do an initial limit check, show or hide buttons */
  1182. $('[class*=docopy-]').each(function()
  1183. {
  1184. var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
  1185. checkLoopLimit(the_name);
  1186. });
  1187. });
  1188. /* ]]> */
  1189. </script>
  1190. <?php
  1191. }
  1192. /**
  1193. * Used to insert global SCRIPT tags into the footer, called on WordPress
  1194. * admin_footer action.
  1195. *
  1196. * @static
  1197. * @since 1.3
  1198. * @access private
  1199. * @see _global_head()
  1200. */
  1201. function _global_foot()
  1202. {
  1203. // must be creating or editing a post or page
  1204. if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
  1205. ?>
  1206. <script type="text/javascript">
  1207. /* <![CDATA[ */
  1208. (function($){ /* not using jQuery ondomready, code runs right away in footer */
  1209. /* use a global dom element to attach events to */
  1210. $.wpalchemy = $('<div></div>').attr('id','wpalchemy').appendTo('body');
  1211. })(jQuery);
  1212. /* ]]> */
  1213. </script>
  1214. <?php
  1215. }
  1216. /**
  1217. * Gets the meta data for a meta box
  1218. *
  1219. * @since 1.0
  1220. * @access public
  1221. * @param int $post_id optional post ID for which to retrieve the meta data
  1222. * @return array
  1223. * @see _meta
  1224. */
  1225. function the_meta($post_id = NULL)
  1226. {
  1227. return $this->_meta($post_id);
  1228. }
  1229. /**
  1230. * Gets the meta data for a meta box
  1231. *
  1232. * Internal method calls will typically bypass the data retrieval and will
  1233. * immediately return the current meta data
  1234. *
  1235. * @since 1.3
  1236. * @access private
  1237. * @param int $post_id optional post ID for which to retrieve the meta data
  1238. * @param bool $internal optional boolean if internally calling
  1239. * @return array
  1240. * @see the_meta()
  1241. */
  1242. function _meta($post_id = NULL, $internal = FALSE)
  1243. {
  1244. if ( ! is_numeric($post_id))
  1245. {
  1246. if ($internal AND $this->current_post_id)
  1247. {
  1248. $post_id = $this->current_post_id;
  1249. }
  1250. else
  1251. {
  1252. global $post;
  1253. $post_id = $post->ID;
  1254. }
  1255. }
  1256. // this allows multiple internal calls to _meta() without having to fetch data everytime
  1257. if ($internal AND !empty($this->meta) AND $this->current_post_id == $post_id) return $this->meta;
  1258. $this->current_post_id = $post_id;
  1259. // WPALCHEMY_MODE_ARRAY
  1260. $meta = get_post_meta($post_id, $this->id, TRUE);
  1261. // WPALCHEMY_MODE_EXTRACT
  1262. $fields = get_post_meta($post_id, $this->id . '_fields', TRUE);
  1263. if ( ! empty($fields) AND is_array($fields))
  1264. {
  1265. $meta = array();
  1266. foreach ($fields as $field)
  1267. {
  1268. $field_noprefix = preg_replace('/^' . $this->prefix . '/i', '', $field);
  1269. $meta[$field_noprefix] = get_post_meta($post_id, $field, TRUE);
  1270. }
  1271. }
  1272. $this->meta = $meta;
  1273. return $this->meta;
  1274. }
  1275. // user can also use the_ID(), php functions are case-insensitive
  1276. /**
  1277. * @since 1.0
  1278. * @access public
  1279. */
  1280. function the_id()
  1281. {
  1282. echo $this->get_the_id();
  1283. }
  1284. /**
  1285. * @since 1.0
  1286. * @access public
  1287. */
  1288. function get_the_id()
  1289. {
  1290. return $this->id;
  1291. }
  1292. /**
  1293. * @since 1.0
  1294. * @access public
  1295. */
  1296. function the_field($n, $hint = NULL)
  1297. {
  1298. if ($this->in_loop) $this->subname = $n;
  1299. else $this->name = $n;
  1300. $this->hint = $hint;
  1301. }
  1302. /**
  1303. * @since 1.0
  1304. * @access public
  1305. */
  1306. function have_value($n = NULL)
  1307. {
  1308. if ($this->get_the_value($n)) return TRUE;
  1309. return FALSE;
  1310. }
  1311. /**
  1312. * @since 1.0
  1313. * @access public
  1314. */
  1315. function the_value($n = NULL)
  1316. {
  1317. echo $this->get_the_value($n);
  1318. }
  1319. /**
  1320. * @since 1.0
  1321. * @access public
  1322. */
  1323. function get_the_value($n = NULL, $collection = FALSE)
  1324. {
  1325. $this->_meta(NULL, TRUE);
  1326. if ($this->in_loop)
  1327. {
  1328. if(isset($this->meta[$this->name]))
  1329. {
  1330. $n = is_null($n) ? $this->subname : $n ;
  1331. if(!is_null($n))
  1332. {
  1333. if ($collection)
  1334. {
  1335. if(isset($this->meta[$this->name][$this->current]))
  1336. {
  1337. return $this->meta[$this->name][$this->current];
  1338. }
  1339. }
  1340. else
  1341. {
  1342. if(isset($this->meta[$this->name][$this->current][$n]))
  1343. {
  1344. return $this->meta[$this->name][$this->current][$n];
  1345. }
  1346. }
  1347. }
  1348. else
  1349. {
  1350. if ($collection)
  1351. {
  1352. if(isset($this->meta[$this->name]))
  1353. {
  1354. return $this->meta[$this->name];
  1355. }
  1356. }
  1357. else
  1358. {
  1359. if(isset($this->meta[$this->name][$this->current]))
  1360. {
  1361. return $this->meta[$this->name][$this->current];
  1362. }
  1363. }
  1364. }
  1365. }
  1366. }
  1367. else
  1368. {
  1369. $n = is_null($n) ? $this->name : $n ;
  1370. if(isset($this->meta[$n])) return $this->meta[$n];
  1371. }
  1372. return NULL;
  1373. }
  1374. /**
  1375. * @since 1.0
  1376. * @access public
  1377. */
  1378. function the_name($n = NULL)
  1379. {
  1380. echo $this->get_the_name($n);
  1381. }
  1382. /**
  1383. * @since 1.0
  1384. * @access public
  1385. */
  1386. function get_the_name($n = NULL)
  1387. {
  1388. if (!$this->in_template AND $this->mode == WPALCHEMY_MODE_EXTRACT)
  1389. {
  1390. return $this->prefix . str_replace($this->prefix, '', is_null($n) ? $this->name : $n);
  1391. }
  1392. if ($this->in_loop)
  1393. {
  1394. $n = is_null($n) ? $this->subname : $n ;
  1395. if (!is_null($n)) return $this->id . '[' . $this->name . '][' . $this->current . '][' . $n . ']' ;
  1396. $the_field = $this->id . '[' . $this->name . '][' . $this->current . ']' ;
  1397. }
  1398. else
  1399. {
  1400. $n = is_null($n) ? $this->name : $n ;
  1401. $the_field = $this->id . '[' . $n . ']';
  1402. }
  1403. $hints = array
  1404. (
  1405. WPALCHEMY_FIELD_HINT_CHECKBOX_MULTI,
  1406. WPALCHEMY_FIELD_HINT_SELECT_MULTI,
  1407. WPALCHEMY_FIELD_HINT_SELECT_MULTIPLE,
  1408. );
  1409. if (in_array($this->hint, $hints))
  1410. {
  1411. $the_field .= '[]';
  1412. }
  1413. return $the_field;
  1414. }
  1415. /**
  1416. * @since 1.1
  1417. * @access public
  1418. */
  1419. function the_index()
  1420. {
  1421. echo $this->get_the_index();
  1422. }
  1423. /**
  1424. * @since 1.1
  1425. * @access public
  1426. */
  1427. function get_the_index()
  1428. {
  1429. return $this->in_loop ? $this->current : 0 ;
  1430. }
  1431. /**
  1432. * @since 1.0
  1433. * @access public
  1434. */
  1435. function is_first()
  1436. {
  1437. if ($this->in_loop AND $this->current == 0) return TRUE;
  1438. return FALSE;
  1439. }
  1440. /**
  1441. * @since 1.0
  1442. * @access public
  1443. */
  1444. function is_last()
  1445. {
  1446. if ($this->in_loop AND ($this->current+1) == $this->length) return TRUE;
  1447. return FALSE;
  1448. }
  1449. /**
  1450. * Used to check if a value is a match
  1451. *
  1452. * @since 1.1
  1453. * @access public
  1454. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1455. * @param string $v optional the value to check for
  1456. * @return bool
  1457. * @see is_value()
  1458. */
  1459. function is_value($n, $v = NULL)
  1460. {
  1461. if (is_null($v))
  1462. {
  1463. $the_value = $this->get_the_value();
  1464. $v = $n;
  1465. }
  1466. else
  1467. {
  1468. $the_value = $this->get_the_value($n);
  1469. }
  1470. if($v == $the_value) return TRUE;
  1471. return FALSE;
  1472. }
  1473. /**
  1474. * Used to check if a value is selected, useful when working with checkbox,
  1475. * radio and select values.
  1476. *
  1477. * @since 1.3
  1478. * @access public
  1479. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1480. * @param string $v optional the value to check for
  1481. * @return bool
  1482. * @see is_value()
  1483. */
  1484. function is_selected($n, $v = NULL)
  1485. {
  1486. if (is_null($v))
  1487. {
  1488. $the_value = $this->get_the_value(NULL, TRUE);
  1489. $v = $n;
  1490. }
  1491. else
  1492. {
  1493. $the_value = $this->get_the_value($n, TRUE);
  1494. }
  1495. if (is_array($the_value))
  1496. {
  1497. if (in_array($v, $the_value)) return TRUE;
  1498. }
  1499. elseif($v == $the_value)
  1500. {
  1501. return TRUE;
  1502. }
  1503. return FALSE;
  1504. }
  1505. /**
  1506. * Prints the current state of a checkbox field and should be used inline
  1507. * within the INPUT tag.
  1508. *
  1509. * @since 1.3
  1510. * @access public
  1511. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1512. * @param string $v optional the value to check for
  1513. * @see get_the_checkbox_state()
  1514. */
  1515. function the_checkbox_state($n, $v = NULL)
  1516. {
  1517. echo $this->get_the_checkbox_state($n, $v);
  1518. }
  1519. /**
  1520. * Returns the current state of a checkbox field, the returned string is
  1521. * suitable to be used inline within the INPUT tag.
  1522. *
  1523. * @since 1.3
  1524. * @access public
  1525. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1526. * @param string $v optional the value to check for
  1527. * @return string suitable to be used inline within the INPUT tag
  1528. * @see the_checkbox_state()
  1529. */
  1530. function get_the_checkbox_state($n, $v = NULL)
  1531. {
  1532. if ($this->is_selected($n, $v)) return ' checked="checked"';
  1533. }
  1534. /**
  1535. * Prints the current state of a radio field and should be used inline
  1536. * within the INPUT tag.
  1537. *
  1538. * @since 1.3
  1539. * @access public
  1540. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1541. * @param string $v optional the value to check for
  1542. * @see get_the_radio_state()
  1543. */
  1544. function the_radio_state($n, $v = NULL)
  1545. {
  1546. echo $this->get_the_checkbox_state($n, $v);
  1547. }
  1548. /**
  1549. * Returns the current state of a radio field, the returned string is
  1550. * suitable to be used inline within the INPUT tag.
  1551. *
  1552. * @since 1.3
  1553. * @access public
  1554. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1555. * @param string $v optional the value to check for
  1556. * @return string suitable to be used inline within the INPUT tag
  1557. * @see the_radio_state()
  1558. */
  1559. function get_the_radio_state($n, $v = NULL)
  1560. {
  1561. return $this->get_the_checkbox_state($n, $v);
  1562. }
  1563. /**
  1564. * Prints the current state of a select field and should be used inline
  1565. * within the SELECT tag.
  1566. *
  1567. * @since 1.3
  1568. * @access public
  1569. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1570. * @param string $v optional the value to check for
  1571. * @see get_the_select_state()
  1572. */
  1573. function the_select_state($n, $v = NULL)
  1574. {
  1575. echo $this->get_the_select_state($n, $v);
  1576. }
  1577. /**
  1578. * Returns the current state of a select field, the returned string is
  1579. * suitable to be used inline within the SELECT tag.
  1580. *
  1581. * @since 1.3
  1582. * @access public
  1583. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1584. * @param string $v optional the value to check for
  1585. * @return string suitable to be used inline within the SELECT tag
  1586. * @see the_select_state()
  1587. */
  1588. function get_the_select_state($n, $v = NULL)
  1589. {
  1590. if ($this->is_selected($n, $v)) return ' selected="selected"';
  1591. }
  1592. /**
  1593. * @since 1.1
  1594. * @access public
  1595. */
  1596. function the_group_open($t = 'div')
  1597. {
  1598. echo $this->get_the_group_open($t);
  1599. }
  1600. /**
  1601. * @since 1.1
  1602. * @access public
  1603. */
  1604. function get_the_group_open($t = 'div')
  1605. {
  1606. $this->group_tag = $t;
  1607. $loop_open = NULL;
  1608. $loop_open_classes = array('wpa_loop', 'wpa_loop-' . $this->name);
  1609. $css_class = array('wpa_group', 'wpa_group-'. $this->name);
  1610. if ($this->is_first())
  1611. {
  1612. array_push($css_class, 'first');
  1613. $loop_open = '<div class="wpa_loop">';
  1614. if (isset($this->_loop_data->limit))
  1615. {
  1616. array_push($loop_open_classes, 'wpa_loop_limit-' . $this->_loop_data->limit);
  1617. }
  1618. $loop_open = '<div class="' . implode(' ', $loop_open_classes) . '">';
  1619. }
  1620. if ($this->is_last())
  1621. {
  1622. array_push($css_class, 'last');
  1623. if ($this->in_loop == 'multi')
  1624. {
  1625. array_push($css_class, 'tocopy');
  1626. }
  1627. }
  1628. return $loop_open . '<' . $t . ' class="'. implode(' ', $css_class) . '">';
  1629. }
  1630. /**
  1631. * @since 1.1
  1632. * @access public
  1633. */
  1634. function the_group_close()
  1635. {
  1636. echo $this->get_the_group_close();
  1637. }
  1638. /**
  1639. * @since 1.1
  1640. * @access public
  1641. */
  1642. function get_the_group_close()
  1643. {
  1644. $loop_close = NULL;
  1645. if ($this->is_last())
  1646. {
  1647. $loop_close = '</div>';
  1648. }
  1649. return '</' . $this->group_tag . '>' . $loop_close;
  1650. }
  1651. /**
  1652. * @since 1.1
  1653. * @access public
  1654. */
  1655. function have_fields_and_multi($n, $options = NULL)
  1656. {
  1657. if (is_array($options))
  1658. {
  1659. // use as stdClass object
  1660. $options = (object)$options;
  1661. $length = @$options->length;
  1662. $this->_loop_data->limit = @$options->limit;
  1663. }
  1664. else
  1665. {
  1666. // backward compatibility (bc)
  1667. $length = $options;
  1668. }
  1669. $this->_meta(NULL, TRUE);
  1670. $this->in_loop = 'multi';
  1671. return $this->_loop($n, $length, 2);
  1672. }
  1673. /**
  1674. * @deprecated
  1675. * @since 1.0
  1676. * @access public
  1677. */
  1678. function have_fields_and_one($n)
  1679. {
  1680. $this->_meta(NULL, TRUE);
  1681. $this->in_loop = 'single';
  1682. return $this->_loop($n,NULL,1);
  1683. }
  1684. /**
  1685. * @since 1.0
  1686. * @access public
  1687. */
  1688. function have_fields($n,$length=NULL)
  1689. {
  1690. $this->_meta(NULL, TRUE);
  1691. //print_r($this->_meta(NULL, TRUE));
  1692. //echo "<br />";
  1693. $this->in_loop = 'normal';
  1694. //echo "<br />this->_loop({$n}, {$length})" . $this->_loop($n,$length) . "<br />";
  1695. return $this->_loop($n,$length);
  1696. }
  1697. /**
  1698. * @since 1.0
  1699. * @access private
  1700. */
  1701. function _loop($n,$length=NULL,$and_one=0)
  1702. {
  1703. if (!$this->in_loop)
  1704. {
  1705. $this->in_loop = TRUE;
  1706. }
  1707. $this->name = $n;
  1708. $cnt = count(!empty($this->meta[$n])?$this->meta[$n]:NULL);
  1709. $length = is_null($length) ? $cnt : $length ;
  1710. if ($this->in_loop == 'multi' AND $cnt > $length) $length = $cnt;
  1711. $this->length = $length;
  1712. if ($this->in_template AND $and_one)
  1713. {
  1714. if ($length == 0)
  1715. {
  1716. $this->length = $and_one;
  1717. }
  1718. else
  1719. {
  1720. $this->length = $length+1;
  1721. }
  1722. }
  1723. $this->current++;
  1724. if ($this->current < $this->length)
  1725. {
  1726. $this->subname = NULL;
  1727. $this->fieldtype = NULL;
  1728. return TRUE;
  1729. }
  1730. else if ($this->current == $this->length)
  1731. {
  1732. $this->name = NULL;
  1733. $this->current = -1;
  1734. }
  1735. $this->in_loop = FALSE;
  1736. $this->_loop_data = new stdClass;
  1737. return FALSE;
  1738. }
  1739. /**
  1740. * @since 1.0
  1741. * @access private
  1742. */
  1743. function _save($post_id)
  1744. {
  1745. /**
  1746. * note: the "save_post" action fires for saving revisions and post/pages,
  1747. * when saving a post this function fires twice, once for a revision save,
  1748. * and again for the post/page save ... the $post_id is different for the
  1749. * revision save, this means that "get_post_meta()" will not work if trying
  1750. * to get values for a revision (as it has no post meta data)
  1751. * see http://alexking.org/blog/2008/09/06/wordpress-26x-duplicate-custom-field-issue
  1752. *
  1753. * why let the code run twice? wordpress does not currently save post meta
  1754. * data per revisions (I think it should, so users can do a complete revert),
  1755. * so in the case that this functionality changes, let it run twice
  1756. */
  1757. $real_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : NULL ;
  1758. // check autosave
  1759. if (defined('DOING_AUTOSAVE') AND DOING_AUTOSAVE AND !$this->autosave) return $post_id;
  1760. // make sure data came from our meta box, verify nonce
  1761. $nonce = isset($_POST[$this->id.'_nonce']) ? $_POST[$this->id.'_nonce'] : NULL ;
  1762. if (!wp_verify_nonce($nonce, $this->id)) return $post_id;
  1763. // check user permissions
  1764. if ($_POST['post_type'] == 'page')
  1765. {
  1766. if (!current_user_can('edit_page', $post_id)) return $post_id;
  1767. }
  1768. else
  1769. {
  1770. if (!current_user_can('edit_post', $post_id)) return $post_id;
  1771. }
  1772. // authentication passed, save data
  1773. $new_data = $_POST[$this->id];
  1774. WPAlchemy_MetaBox::clean($new_data);
  1775. if (empty($new_data))
  1776. {
  1777. $new_data = NULL;
  1778. }
  1779. // filter: save
  1780. if ($this->has_filter('save'))
  1781. {
  1782. $new_data = $this->apply_filters('save', $new_data, $real_post_id);
  1783. /**
  1784. * halt saving
  1785. * @since 1.3.4
  1786. */
  1787. if (FALSE === $new_data) return $post_id;
  1788. }
  1789. // get current fields, use $real_post_id (checked for in both modes)
  1790. $current_fields = get_post_meta($real_post_id, $this->id . '_fields', TRUE);
  1791. if ($this->mode == WPALCHEMY_MODE_EXTRACT)
  1792. {
  1793. $new_fields = array();
  1794. if (is_array($new_data))
  1795. {
  1796. foreach ($new_data as $k => $v)
  1797. {
  1798. $field = $this->prefix . $k;
  1799. array_push($new_fields,$field);
  1800. $new_value = $new_data[$k];
  1801. if (is_null($new_value) || (is_integer($new_value) && $new_value <= 0))
  1802. {
  1803. delete_post_meta($post_id, $field);
  1804. }
  1805. else
  1806. {
  1807. update_post_meta($post_id, $field, $new_value);
  1808. }
  1809. }
  1810. }
  1811. $diff_fields = array_diff((array)$current_fields,$new_fields);
  1812. if (is_array($diff_fields))
  1813. {
  1814. foreach ($diff_fields as $field)
  1815. {
  1816. delete_post_meta($post_id,$field);
  1817. }
  1818. }
  1819. delete_post_meta($post_id, $this->id . '_fields');
  1820. if ( ! empty($new_fields))
  1821. {
  1822. add_post_meta($post_id,$this->id . '_fields', $new_fields, TRUE);
  1823. }
  1824. // keep data tidy, delete values if previously using WPALCHEMY_MODE_ARRAY
  1825. delete_post_meta($post_id, $this->id);
  1826. }
  1827. else
  1828. {
  1829. if (is_null($new_data))
  1830. {
  1831. delete_post_meta($post_id, $this->id);
  1832. }
  1833. else
  1834. {
  1835. update_post_meta($post_id, $this->id, $new_data);
  1836. }
  1837. // keep data tidy, delete values if previously using WPALCHEMY_MODE_EXTRACT
  1838. if (is_array($current_fields))
  1839. {
  1840. foreach ($current_fields as $field)
  1841. {
  1842. delete_post_meta($post_id, $field);
  1843. }
  1844. delete_post_meta($post_id, $this->id . '_fields');
  1845. }
  1846. }
  1847. // action: save
  1848. if ($this->has_action('save'))
  1849. {
  1850. $this->do_action('save', $new_data, $real_post_id);
  1851. }
  1852. return $post_id;
  1853. }
  1854. /**
  1855. * Cleans an array, removing blank ('') values
  1856. *
  1857. * @static
  1858. * @since 1.0
  1859. * @access public
  1860. * @param array the array to clean (passed by reference)
  1861. */
  1862. function clean(&$arr)
  1863. {
  1864. if (is_array($arr))
  1865. {
  1866. foreach ($arr as $i => $v)
  1867. {
  1868. if (is_array($arr[$i]))
  1869. {
  1870. WPAlchemy_MetaBox::clean($arr[$i]);
  1871. if ( isset($arr[$i]) && !count($arr[$i]))
  1872. {
  1873. unset($arr[$i]);
  1874. }
  1875. }
  1876. else
  1877. {
  1878. if ( '' == trim($arr[$i]) OR is_null($arr[$i]) )
  1879. {
  1880. unset($arr[$i]);
  1881. }
  1882. }
  1883. }
  1884. if (!count($arr))
  1885. {
  1886. $arr = array();
  1887. }
  1888. else
  1889. {
  1890. $keys = array_keys($arr);
  1891. $is_numeric = TRUE;
  1892. foreach ($keys as $key)
  1893. {
  1894. if (!is_numeric($key))
  1895. {
  1896. $is_numeric = FALSE;
  1897. break;
  1898. }
  1899. }
  1900. if ($is_numeric)
  1901. {
  1902. $arr = array_values($arr);
  1903. }
  1904. }
  1905. }
  1906. }
  1907. }
  1908. /* End of file */