PageRenderTime 256ms CodeModel.GetById 31ms RepoModel.GetById 0ms app.codeStats 0ms

/wpalchemy/MetaBox.php

https://bitbucket.org/todoubled/wp-myshopproducts
PHP | 2328 lines | 1234 code | 347 blank | 747 comment | 193 complexity | 653d20db5b08e911f6241369e7e50ac7 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  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.17
  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. $post_type = WPAlchemy_MetaBox::_get_current_post_type();
  854. if (isset($post_type))
  855. {
  856. if ('page' == $post_type)
  857. {
  858. return 'page';
  859. }
  860. else
  861. {
  862. return 'post';
  863. }
  864. }
  865. return NULL;
  866. }
  867. /**
  868. * Used to check for the current post type, works when creating or editing a
  869. * new post, page or custom post type.
  870. *
  871. * @static
  872. * @since 1.4.6
  873. * @return string [custom_post_type], page or post
  874. */
  875. function _get_current_post_type()
  876. {
  877. $uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : NULL ;
  878. if ( isset( $uri ) )
  879. {
  880. $uri_parts = parse_url($uri);
  881. $file = basename($uri_parts['path']);
  882. if ($uri AND in_array($file, array('post.php', 'post-new.php')))
  883. {
  884. $post_id = WPAlchemy_MetaBox::_get_post_id();
  885. $post_type = isset($_GET['post_type']) ? $_GET['post_type'] : NULL ;
  886. $post_type = $post_id ? get_post_type($post_id) : $post_type ;
  887. if (isset($post_type))
  888. {
  889. return $post_type;
  890. }
  891. else
  892. {
  893. // because of the 'post.php' and 'post-new.php' checks above, we can default to 'post'
  894. return 'post';
  895. }
  896. }
  897. }
  898. return NULL;
  899. }
  900. /**
  901. * Used to get the current post id.
  902. *
  903. * @static
  904. * @since 1.4.8
  905. * @return int post ID
  906. */
  907. function _get_post_id()
  908. {
  909. global $post;
  910. $p_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : null ;
  911. $g_post_id = isset($_GET['post']) ? $_GET['post'] : null ;
  912. $post_id = $g_post_id ? $g_post_id : $p_post_id ;
  913. $post_id = isset($post->ID) ? $post->ID : $post_id ;
  914. if (isset($post_id))
  915. {
  916. return (integer) $post_id;
  917. }
  918. return null;
  919. }
  920. /**
  921. * @since 1.0
  922. */
  923. function can_output()
  924. {
  925. $post_id = WPAlchemy_MetaBox::_get_post_id();
  926. if (!empty($this->exclude_template) OR !empty($this->include_template))
  927. {
  928. $template_file = get_post_meta($post_id,'_wp_page_template',TRUE);
  929. }
  930. if
  931. (
  932. !empty($this->exclude_category) OR
  933. !empty($this->exclude_category_id) OR
  934. !empty($this->include_category) OR
  935. !empty($this->include_category_id)
  936. )
  937. {
  938. $categories = wp_get_post_categories($post_id,'fields=all');
  939. }
  940. if
  941. (
  942. !empty($this->exclude_tag) OR
  943. !empty($this->exclude_tag_id) OR
  944. !empty($this->include_tag) OR
  945. !empty($this->include_tag_id)
  946. )
  947. {
  948. $tags = wp_get_post_tags($post_id);
  949. }
  950. // processing order: "exclude" then "include"
  951. // processing order: "template" then "category" then "post"
  952. $can_output = TRUE; // include all
  953. if
  954. (
  955. !empty($this->exclude_template) OR
  956. !empty($this->exclude_category_id) OR
  957. !empty($this->exclude_category) OR
  958. !empty($this->exclude_tag_id) OR
  959. !empty($this->exclude_tag) OR
  960. !empty($this->exclude_post_id) OR
  961. !empty($this->include_template) OR
  962. !empty($this->include_category_id) OR
  963. !empty($this->include_category) OR
  964. !empty($this->include_tag_id) OR
  965. !empty($this->include_tag) OR
  966. !empty($this->include_post_id)
  967. )
  968. {
  969. if (!empty($this->exclude_template))
  970. {
  971. if (in_array($template_file,$this->exclude_template))
  972. {
  973. $can_output = FALSE;
  974. }
  975. }
  976. if (!empty($this->exclude_category_id))
  977. {
  978. foreach ($categories as $cat)
  979. {
  980. if (in_array($cat->term_id,$this->exclude_category_id))
  981. {
  982. $can_output = FALSE;
  983. break;
  984. }
  985. }
  986. }
  987. if (!empty($this->exclude_category))
  988. {
  989. foreach ($categories as $cat)
  990. {
  991. if
  992. (
  993. in_array($cat->slug,$this->exclude_category) OR
  994. in_array($cat->name,$this->exclude_category)
  995. )
  996. {
  997. $can_output = FALSE;
  998. break;
  999. }
  1000. }
  1001. }
  1002. if (!empty($this->exclude_tag_id))
  1003. {
  1004. foreach ($tags as $tag)
  1005. {
  1006. if (in_array($tag->term_id,$this->exclude_tag_id))
  1007. {
  1008. $can_output = FALSE;
  1009. break;
  1010. }
  1011. }
  1012. }
  1013. if (!empty($this->exclude_tag))
  1014. {
  1015. foreach ($tags as $tag)
  1016. {
  1017. if
  1018. (
  1019. in_array($tag->slug,$this->exclude_tag) OR
  1020. in_array($tag->name,$this->exclude_tag)
  1021. )
  1022. {
  1023. $can_output = FALSE;
  1024. break;
  1025. }
  1026. }
  1027. }
  1028. if (!empty($this->exclude_post_id))
  1029. {
  1030. if (in_array($post_id,$this->exclude_post_id))
  1031. {
  1032. $can_output = FALSE;
  1033. }
  1034. }
  1035. // excludes are not set use "include only" mode
  1036. if
  1037. (
  1038. empty($this->exclude_template) AND
  1039. empty($this->exclude_category_id) AND
  1040. empty($this->exclude_category) AND
  1041. empty($this->exclude_tag_id) AND
  1042. empty($this->exclude_tag) AND
  1043. empty($this->exclude_post_id)
  1044. )
  1045. {
  1046. $can_output = FALSE;
  1047. }
  1048. if (!empty($this->include_template))
  1049. {
  1050. if (in_array($template_file,$this->include_template))
  1051. {
  1052. $can_output = TRUE;
  1053. }
  1054. }
  1055. if (!empty($this->include_category_id))
  1056. {
  1057. foreach ($categories as $cat)
  1058. {
  1059. if (in_array($cat->term_id,$this->include_category_id))
  1060. {
  1061. $can_output = TRUE;
  1062. break;
  1063. }
  1064. }
  1065. }
  1066. if (!empty($this->include_category))
  1067. {
  1068. foreach ($categories as $cat)
  1069. {
  1070. if
  1071. (
  1072. in_array($cat->slug,$this->include_category) OR
  1073. in_array($cat->name,$this->include_category)
  1074. )
  1075. {
  1076. $can_output = TRUE;
  1077. break;
  1078. }
  1079. }
  1080. }
  1081. if (!empty($this->include_tag_id))
  1082. {
  1083. foreach ($tags as $tag)
  1084. {
  1085. if (in_array($tag->term_id,$this->include_tag_id))
  1086. {
  1087. $can_output = TRUE;
  1088. break;
  1089. }
  1090. }
  1091. }
  1092. if (!empty($this->include_tag))
  1093. {
  1094. foreach ($tags as $tag)
  1095. {
  1096. if
  1097. (
  1098. in_array($tag->slug,$this->include_tag) OR
  1099. in_array($tag->name,$this->include_tag)
  1100. )
  1101. {
  1102. $can_output = TRUE;
  1103. break;
  1104. }
  1105. }
  1106. }
  1107. if (!empty($this->include_post_id))
  1108. {
  1109. if (in_array($post_id,$this->include_post_id))
  1110. {
  1111. $can_output = TRUE;
  1112. }
  1113. }
  1114. }
  1115. $post_type = WPAlchemy_MetaBox::_get_current_post_type();
  1116. if (isset($post_type) AND ! in_array($post_type, $this->types))
  1117. {
  1118. $can_output = FALSE;
  1119. }
  1120. // filter: output (can_output)
  1121. if ($this->has_filter('output'))
  1122. {
  1123. $can_output = $this->apply_filters('output', $post_id);
  1124. }
  1125. return $can_output;
  1126. }
  1127. /**
  1128. * Used to insert global STYLE or SCRIPT tags into the head, called on
  1129. * WordPress admin_footer action.
  1130. *
  1131. * @static
  1132. * @since 1.3
  1133. * @access private
  1134. * @see _global_foot()
  1135. */
  1136. function _global_head()
  1137. {
  1138. // must be creating or editing a post or page
  1139. if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
  1140. // todo: you're assuming people will want to use this exact functionality
  1141. // consider giving a developer access to change this via hooks/callbacks
  1142. // include javascript for special functionality
  1143. ?><style type="text/css"> .wpa_group.tocopy { display:none; } </style>
  1144. <script type="text/javascript">
  1145. /* <![CDATA[ */
  1146. jQuery(function($)
  1147. {
  1148. $(document).click(function(e)
  1149. {
  1150. var elem = $(e.target);
  1151. if (elem.attr('class') && elem.filter('[class*=dodelete]').length)
  1152. {
  1153. e.preventDefault();
  1154. var p = elem.parents('.postbox'); /*wp*/
  1155. var the_name = elem.attr('class').match(/dodelete-([a-zA-Z0-9_-]*)/i);
  1156. the_name = (the_name && the_name[1]) ? the_name[1] : null ;
  1157. /* todo: expose and allow editing of this message */
  1158. if (confirm('This action can not be undone, are you sure?'))
  1159. {
  1160. if (the_name)
  1161. {
  1162. $('.wpa_group-'+ the_name, p).not('.tocopy').remove();
  1163. }
  1164. else
  1165. {
  1166. elem.parents('.wpa_group').remove();
  1167. }
  1168. the_name = elem.parents('.wpa_group').attr('class').match(/wpa_group-([a-zA-Z0-9_-]*)/i)[1];
  1169. checkLoopLimit(the_name);
  1170. $.wpalchemy.trigger('wpa_delete');
  1171. }
  1172. }
  1173. });
  1174. $('[class*=docopy-]').click(function(e)
  1175. {
  1176. e.preventDefault();
  1177. var p = $(this).parents('.postbox'); /*wp*/
  1178. var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
  1179. var the_group = $('.wpa_group-'+ the_name +':first.tocopy', p);
  1180. var the_clone = the_group.clone().removeClass('tocopy last');
  1181. var the_props = ['name', 'id', 'for', 'class'];
  1182. the_group.find('*').each(function(i, elem)
  1183. {
  1184. for (var j = 0; j < the_props.length; j++)
  1185. {
  1186. var the_prop = $(elem).attr(the_props[j]);
  1187. if (the_prop)
  1188. {
  1189. var the_match = the_prop.match(/\[(\d+)\]/i);
  1190. if (the_match)
  1191. {
  1192. the_prop = the_prop.replace(the_match[0],'['+ (+the_match[1]+1) +']');
  1193. $(elem).attr(the_props[j], the_prop);
  1194. }
  1195. the_match = null;
  1196. // todo: this may prove to be too broad of a search
  1197. the_match = the_prop.match(/n(\d+)/i);
  1198. if (the_match)
  1199. {
  1200. the_prop = the_prop.replace(the_match[0], 'n' + (+the_match[1]+1));
  1201. $(elem).attr(the_props[j], the_prop);
  1202. }
  1203. }
  1204. }
  1205. });
  1206. if ($(this).hasClass('ontop'))
  1207. {
  1208. $('.wpa_group-'+ the_name +':first', p).before(the_clone);
  1209. }
  1210. else
  1211. {
  1212. the_group.before(the_clone);
  1213. }
  1214. checkLoopLimit(the_name);
  1215. $.wpalchemy.trigger('wpa_copy', [the_clone]);
  1216. });
  1217. function checkLoopLimit(name)
  1218. {
  1219. var elem = $('.docopy-' + name);
  1220. var the_match = $('.wpa_loop-' + name).attr('class').match(/wpa_loop_limit-([0-9]*)/i);
  1221. if (the_match)
  1222. {
  1223. var the_limit = the_match[1];
  1224. if ($('.wpa_group-' + name).not('.wpa_group.tocopy').length >= the_limit)
  1225. {
  1226. elem.hide();
  1227. }
  1228. else
  1229. {
  1230. elem.show();
  1231. }
  1232. }
  1233. }
  1234. /* do an initial limit check, show or hide buttons */
  1235. $('[class*=docopy-]').each(function()
  1236. {
  1237. var the_name = $(this).attr('class').match(/docopy-([a-zA-Z0-9_-]*)/i)[1];
  1238. checkLoopLimit(the_name);
  1239. });
  1240. });
  1241. /* ]]> */
  1242. </script>
  1243. <?php
  1244. }
  1245. /**
  1246. * Used to insert global SCRIPT tags into the footer, called on WordPress
  1247. * admin_footer action.
  1248. *
  1249. * @static
  1250. * @since 1.3
  1251. * @access private
  1252. * @see _global_head()
  1253. */
  1254. function _global_foot()
  1255. {
  1256. // must be creating or editing a post or page
  1257. if ( ! WPAlchemy_MetaBox::_is_post() AND ! WPAlchemy_MetaBox::_is_page()) return;
  1258. ?>
  1259. <script type="text/javascript">
  1260. /* <![CDATA[ */
  1261. (function($){ /* not using jQuery ondomready, code runs right away in footer */
  1262. /* use a global dom element to attach events to */
  1263. $.wpalchemy = $('<div></div>').attr('id','wpalchemy').appendTo('body');
  1264. })(jQuery);
  1265. /* ]]> */
  1266. </script>
  1267. <?php
  1268. }
  1269. /**
  1270. * Gets the meta data for a meta box
  1271. *
  1272. * @since 1.0
  1273. * @access public
  1274. * @param int $post_id optional post ID for which to retrieve the meta data
  1275. * @return array
  1276. * @see _meta
  1277. */
  1278. function the_meta($post_id = NULL)
  1279. {
  1280. return $this->_meta($post_id);
  1281. }
  1282. /**
  1283. * Gets the meta data for a meta box
  1284. *
  1285. * Internal method calls will typically bypass the data retrieval and will
  1286. * immediately return the current meta data
  1287. *
  1288. * @since 1.3
  1289. * @access private
  1290. * @param int $post_id optional post ID for which to retrieve the meta data
  1291. * @param bool $internal optional boolean if internally calling
  1292. * @return array
  1293. * @see the_meta()
  1294. */
  1295. function _meta($post_id = NULL, $internal = FALSE)
  1296. {
  1297. if ( ! is_numeric($post_id))
  1298. {
  1299. if ($internal AND $this->current_post_id)
  1300. {
  1301. $post_id = $this->current_post_id;
  1302. }
  1303. else
  1304. {
  1305. global $post;
  1306. $post_id = $post->ID;
  1307. }
  1308. }
  1309. // this allows multiple internal calls to _meta() without having to fetch data everytime
  1310. if ($internal AND !empty($this->meta) AND $this->current_post_id == $post_id) return $this->meta;
  1311. $this->current_post_id = $post_id;
  1312. // WPALCHEMY_MODE_ARRAY
  1313. $meta = get_post_meta($post_id, $this->id, TRUE);
  1314. // WPALCHEMY_MODE_EXTRACT
  1315. $fields = get_post_meta($post_id, $this->id . '_fields', TRUE);
  1316. if ( ! empty($fields) AND is_array($fields))
  1317. {
  1318. $meta = array();
  1319. foreach ($fields as $field)
  1320. {
  1321. $field_noprefix = preg_replace('/^' . $this->prefix . '/i', '', $field);
  1322. $meta[$field_noprefix] = get_post_meta($post_id, $field, TRUE);
  1323. }
  1324. }
  1325. $this->meta = $meta;
  1326. return $this->meta;
  1327. }
  1328. // user can also use the_ID(), php functions are case-insensitive
  1329. /**
  1330. * @since 1.0
  1331. * @access public
  1332. */
  1333. function the_id()
  1334. {
  1335. echo $this->get_the_id();
  1336. }
  1337. /**
  1338. * @since 1.0
  1339. * @access public
  1340. */
  1341. function get_the_id()
  1342. {
  1343. return $this->id;
  1344. }
  1345. /**
  1346. * @since 1.0
  1347. * @access public
  1348. */
  1349. function the_field($n, $hint = NULL)
  1350. {
  1351. if ($this->in_loop) $this->subname = $n;
  1352. else $this->name = $n;
  1353. $this->hint = $hint;
  1354. }
  1355. /**
  1356. * @since 1.0
  1357. * @access public
  1358. */
  1359. function have_value($n = NULL)
  1360. {
  1361. if ($this->get_the_value($n)) return TRUE;
  1362. return FALSE;
  1363. }
  1364. /**
  1365. * @since 1.0
  1366. * @access public
  1367. */
  1368. function the_value($n = NULL)
  1369. {
  1370. echo $this->get_the_value($n);
  1371. }
  1372. /**
  1373. * @since 1.0
  1374. * @access public
  1375. */
  1376. function get_the_value($n = NULL, $collection = FALSE)
  1377. {
  1378. $this->_meta(NULL, TRUE);
  1379. $value = null;
  1380. if ($this->in_loop)
  1381. {
  1382. if(isset($this->meta[$this->name]))
  1383. {
  1384. $n = is_null($n) ? $this->subname : $n ;
  1385. if(!is_null($n))
  1386. {
  1387. if ($collection)
  1388. {
  1389. if(isset($this->meta[$this->name][$this->current]))
  1390. {
  1391. $value = $this->meta[$this->name][$this->current];
  1392. }
  1393. }
  1394. else
  1395. {
  1396. if(isset($this->meta[$this->name][$this->current][$n]))
  1397. {
  1398. $value = $this->meta[$this->name][$this->current][$n];
  1399. }
  1400. }
  1401. }
  1402. else
  1403. {
  1404. if ($collection)
  1405. {
  1406. if(isset($this->meta[$this->name]))
  1407. {
  1408. $value = $this->meta[$this->name];
  1409. }
  1410. }
  1411. else
  1412. {
  1413. if(isset($this->meta[$this->name][$this->current]))
  1414. {
  1415. $value = $this->meta[$this->name][$this->current];
  1416. }
  1417. }
  1418. }
  1419. }
  1420. }
  1421. else
  1422. {
  1423. $n = is_null($n) ? $this->name : $n ;
  1424. if(isset($this->meta[$n]))
  1425. {
  1426. $value = $this->meta[$n];
  1427. }
  1428. }
  1429. if (is_string($value) || is_numeric($value))
  1430. {
  1431. if ($this->in_template)
  1432. {
  1433. return htmlentities($value, ENT_QUOTES, 'UTF-8');
  1434. }
  1435. else
  1436. {
  1437. // http://wordpress.org/support/topic/call-function-called-by-embed-shortcode-direct
  1438. // http://phpdoc.wordpress.org/trunk/WordPress/Embed/WP_Embed.html#run_shortcode
  1439. global $wp_embed;
  1440. return do_shortcode($wp_embed->run_shortcode($value));
  1441. }
  1442. }
  1443. else
  1444. {
  1445. // value can sometimes be an array
  1446. return $value;
  1447. }
  1448. }
  1449. /**
  1450. * @since 1.0
  1451. * @access public
  1452. */
  1453. function the_name($n = NULL)
  1454. {
  1455. echo $this->get_the_name($n);
  1456. }
  1457. /**
  1458. * @since 1.0
  1459. * @access public
  1460. */
  1461. function get_the_name($n = NULL)
  1462. {
  1463. if (!$this->in_template AND $this->mode == WPALCHEMY_MODE_EXTRACT)
  1464. {
  1465. return $this->prefix . str_replace($this->prefix, '', is_null($n) ? $this->name : $n);
  1466. }
  1467. if ($this->in_loop)
  1468. {
  1469. $n = is_null($n) ? $this->subname : $n ;
  1470. if (!is_null($n)) $the_field = $this->id . '[' . $this->name . '][' . $this->current . '][' . $n . ']' ;
  1471. else $the_field = $this->id . '[' . $this->name . '][' . $this->current . ']' ;
  1472. }
  1473. else
  1474. {
  1475. $n = is_null($n) ? $this->name : $n ;
  1476. $the_field = $this->id . '[' . $n . ']';
  1477. }
  1478. $hints = array
  1479. (
  1480. WPALCHEMY_FIELD_HINT_CHECKBOX_MULTI,
  1481. WPALCHEMY_FIELD_HINT_SELECT_MULTI,
  1482. WPALCHEMY_FIELD_HINT_SELECT_MULTIPLE,
  1483. );
  1484. if (in_array($this->hint, $hints))
  1485. {
  1486. $the_field .= '[]';
  1487. }
  1488. return $the_field;
  1489. }
  1490. /**
  1491. * @since 1.1
  1492. * @access public
  1493. */
  1494. function the_index()
  1495. {
  1496. echo $this->get_the_index();
  1497. }
  1498. /**
  1499. * @since 1.1
  1500. * @access public
  1501. */
  1502. function get_the_index()
  1503. {
  1504. return $this->in_loop ? $this->current : 0 ;
  1505. }
  1506. /**
  1507. * @since 1.0
  1508. * @access public
  1509. */
  1510. function is_first()
  1511. {
  1512. if ($this->in_loop AND $this->current == 0) return TRUE;
  1513. return FALSE;
  1514. }
  1515. /**
  1516. * @since 1.0
  1517. * @access public
  1518. */
  1519. function is_last()
  1520. {
  1521. if ($this->in_loop AND ($this->current+1) == $this->length) return TRUE;
  1522. return FALSE;
  1523. }
  1524. /**
  1525. * Used to check if a value is a match
  1526. *
  1527. * @since 1.1
  1528. * @access public
  1529. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1530. * @param string $v optional the value to check for
  1531. * @return bool
  1532. * @see is_value()
  1533. */
  1534. function is_value($n, $v = NULL)
  1535. {
  1536. if (is_null($v))
  1537. {
  1538. $the_value = $this->get_the_value();
  1539. $v = $n;
  1540. }
  1541. else
  1542. {
  1543. $the_value = $this->get_the_value($n);
  1544. }
  1545. if($v == $the_value) return TRUE;
  1546. return FALSE;
  1547. }
  1548. /**
  1549. * Used to check if a value is selected, useful when working with checkbox,
  1550. * radio and select values.
  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 bool
  1557. * @see is_value()
  1558. */
  1559. function is_selected($n, $v = NULL)
  1560. {
  1561. if (is_null($v))
  1562. {
  1563. $the_value = $this->get_the_value(NULL);
  1564. $v = $n;
  1565. }
  1566. else
  1567. {
  1568. $the_value = $this->get_the_value($n);
  1569. }
  1570. if (is_array($the_value))
  1571. {
  1572. if (in_array($v, $the_value)) return TRUE;
  1573. }
  1574. elseif($v == $the_value)
  1575. {
  1576. return TRUE;
  1577. }
  1578. return FALSE;
  1579. }
  1580. /**
  1581. * Prints the current state of a checkbox field and should be used inline
  1582. * within the INPUT tag.
  1583. *
  1584. * @since 1.3
  1585. * @access public
  1586. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1587. * @param string $v optional the value to check for
  1588. * @see get_the_checkbox_state()
  1589. */
  1590. function the_checkbox_state($n, $v = NULL)
  1591. {
  1592. echo $this->get_the_checkbox_state($n, $v);
  1593. }
  1594. /**
  1595. * Returns the current state of a checkbox field, the returned string is
  1596. * suitable to be used inline within the INPUT tag.
  1597. *
  1598. * @since 1.3
  1599. * @access public
  1600. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1601. * @param string $v optional the value to check for
  1602. * @return string suitable to be used inline within the INPUT tag
  1603. * @see the_checkbox_state()
  1604. */
  1605. function get_the_checkbox_state($n, $v = NULL)
  1606. {
  1607. if ($this->is_selected($n, $v)) return ' checked="checked"';
  1608. }
  1609. /**
  1610. * Prints the current state of a radio field and should be used inline
  1611. * within the INPUT tag.
  1612. *
  1613. * @since 1.3
  1614. * @access public
  1615. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1616. * @param string $v optional the value to check for
  1617. * @see get_the_radio_state()
  1618. */
  1619. function the_radio_state($n, $v = NULL)
  1620. {
  1621. echo $this->get_the_checkbox_state($n, $v);
  1622. }
  1623. /**
  1624. * Returns the current state of a radio field, the returned string is
  1625. * suitable to be used inline within the INPUT tag.
  1626. *
  1627. * @since 1.3
  1628. * @access public
  1629. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1630. * @param string $v optional the value to check for
  1631. * @return string suitable to be used inline within the INPUT tag
  1632. * @see the_radio_state()
  1633. */
  1634. function get_the_radio_state($n, $v = NULL)
  1635. {
  1636. return $this->get_the_checkbox_state($n, $v);
  1637. }
  1638. /**
  1639. * Prints the current state of a select field and should be used inline
  1640. * within the SELECT tag.
  1641. *
  1642. * @since 1.3
  1643. * @access public
  1644. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1645. * @param string $v optional the value to check for
  1646. * @see get_the_select_state()
  1647. */
  1648. function the_select_state($n, $v = NULL)
  1649. {
  1650. echo $this->get_the_select_state($n, $v);
  1651. }
  1652. /**
  1653. * Returns the current state of a select field, the returned string is
  1654. * suitable to be used inline within the SELECT tag.
  1655. *
  1656. * @since 1.3
  1657. * @access public
  1658. * @param string $n the field name to check or the value to check for (if the_field() is used prior)
  1659. * @param string $v optional the value to check for
  1660. * @return string suitable to be used inline within the SELECT tag
  1661. * @see the_select_state()
  1662. */
  1663. function get_the_select_state($n, $v = NULL)
  1664. {
  1665. if ($this->is_selected($n, $v)) return ' selected="selected"';
  1666. }
  1667. /**
  1668. * @since 1.1
  1669. * @access public
  1670. */
  1671. function the_group_open($t = 'div')
  1672. {
  1673. echo $this->get_the_group_open($t);
  1674. }
  1675. /**
  1676. * @since 1.1
  1677. * @access public
  1678. */
  1679. function get_the_group_open($t = 'div')
  1680. {
  1681. $this->group_tag = $t;
  1682. $loop_open = NULL;
  1683. $loop_open_classes = array('wpa_loop', 'wpa_loop-' . $this->name);
  1684. $css_class = array('wpa_group', 'wpa_group-'. $this->name);
  1685. if ($this->is_first())
  1686. {
  1687. array_push($css_class, 'first');
  1688. $loop_open = '<div class="wpa_loop">';
  1689. if (isset($this->_loop_data->limit))
  1690. {
  1691. array_push($loop_open_classes, 'wpa_loop_limit-' . $this->_loop_data->limit);
  1692. }
  1693. $loop_open = '<div id="wpa_loop-'. $this->name .'" class="' . implode(' ', $loop_open_classes) . '">';
  1694. }
  1695. if ($this->is_last())
  1696. {
  1697. array_push($css_class, 'last');
  1698. if ($this->in_loop == 'multi')
  1699. {
  1700. array_push($css_class, 'tocopy');
  1701. }
  1702. }
  1703. return $loop_open . '<' . $t . ' class="'. implode(' ', $css_class) . '">';
  1704. }
  1705. /**
  1706. * @since 1.1
  1707. * @access public
  1708. */
  1709. function the_group_close()
  1710. {
  1711. echo $this->get_the_group_close();
  1712. }
  1713. /**
  1714. * @since 1.1
  1715. * @access public
  1716. */
  1717. function get_the_group_close()
  1718. {
  1719. $loop_close = NULL;
  1720. if ($this->is_last())
  1721. {
  1722. $loop_close = '</div>';
  1723. }
  1724. return '</' . $this->group_tag . '>' . $loop_close;
  1725. }
  1726. /**
  1727. * @since 1.1
  1728. * @access public
  1729. */
  1730. function have_fields_and_multi($n, $options = NULL)
  1731. {
  1732. if (is_array($options))
  1733. {
  1734. // use as stdClass object
  1735. $options = (object)$options;
  1736. $length = @$options->length;
  1737. $this->_loop_data->limit = @$options->limit;
  1738. }
  1739. else
  1740. {
  1741. // backward compatibility (bc)
  1742. $length = $options;
  1743. }
  1744. $this->_meta(NULL, TRUE);
  1745. $this->in_loop = 'multi';
  1746. return $this->_loop($n, $length, 2);
  1747. }
  1748. /**
  1749. * @deprecated
  1750. * @since 1.0
  1751. * @access public
  1752. */
  1753. function have_fields_and_one($n)
  1754. {
  1755. $this->_meta(NULL, TRUE);
  1756. $this->in_loop = 'single';
  1757. return $this->_loop($n,NULL,1);
  1758. }
  1759. /**
  1760. * @since 1.0
  1761. * @access public
  1762. */
  1763. function have_fields($n,$length=NULL)
  1764. {
  1765. $this->_meta(NULL, TRUE);
  1766. $this->in_loop = 'normal';
  1767. return $this->_loop($n,$length);
  1768. }
  1769. /**
  1770. * @since 1.0
  1771. * @access private
  1772. */
  1773. function _loop($n,$length=NULL,$and_one=0)
  1774. {
  1775. if (!$this->in_loop)
  1776. {
  1777. $this->in_loop = TRUE;
  1778. }
  1779. $this->name = $n;
  1780. $cnt = count(!empty($this->meta[$n])?$this->meta[$n]:NULL);
  1781. $length = is_null($length) ? $cnt : $length ;
  1782. if ($this->in_loop == 'multi' AND $cnt > $length) $length = $cnt;
  1783. $this->length = $length;
  1784. if ($this->in_template AND $and_one)
  1785. {
  1786. if ($length == 0)
  1787. {
  1788. $this->length = $and_one;
  1789. }
  1790. else
  1791. {
  1792. $this->length = $length+1;
  1793. }
  1794. }
  1795. $this->current++;
  1796. if ($this->current < $this->length)
  1797. {
  1798. $this->subname = NULL;
  1799. $this->fieldtype = NULL;
  1800. return TRUE;
  1801. }
  1802. else if ($this->current == $this->length)
  1803. {
  1804. $this->name = NULL;
  1805. $this->current = -1;
  1806. }
  1807. $this->in_loop = FALSE;
  1808. $this->_loop_data = new stdClass;
  1809. return FALSE;
  1810. }
  1811. /**
  1812. * @since 1.0
  1813. * @access private
  1814. */
  1815. function _save($post_id)
  1816. {
  1817. /**
  1818. * note: the "save_post" action fires for saving revisions and post/pages,
  1819. * when saving a post this function fires twice, once for a revision save,
  1820. * and again for the post/page save ... the $post_id is different for the
  1821. * revision save, this means that "get_post_meta()" will not work if trying
  1822. * to get values for a revision (as it has no post meta data)
  1823. * see http://alexking.org/blog/2008/09/06/wordpress-26x-duplicate-custom-field-issue
  1824. *
  1825. * why let the code run twice? wordpress does not currently save post meta
  1826. * data per revisions (I think it should, so users can do a complete revert),
  1827. * so in the case that this functionality changes, let it run twice
  1828. */
  1829. $real_post_id = isset($_POST['post_ID']) ? $_POST['post_ID'] : NULL ;
  1830. // check autosave
  1831. if (defined('DOING_AUTOSAVE') AND DOING_AUTOSAVE AND !$this->autosave) return $post_id;
  1832. // make sure data came from our meta box, verify nonce
  1833. $nonce = isset($_POST[$this->id.'_nonce']) ? $_POST[$this->id.'_nonce'] : NULL ;
  1834. if (!wp_verify_nonce($nonce, $this->id)) return $post_id;
  1835. // check user permissions
  1836. if ($_POST['post_type'] == 'page')
  1837. {
  1838. if (!current_user_can('edit_page', $post_id)) return $post_id;
  1839. }
  1840. else
  1841. {
  1842. if (!current_user_can('edit_post', $post_id)) return $post_id;
  1843. }
  1844. // authentication passed, save data
  1845. $new_data = isset( $_POST[$this->id] ) ? $_POST[$this->id] : NULL ;
  1846. WPAlchemy_MetaBox::clean($new_data);
  1847. if (empty($new_data))
  1848. {
  1849. $new_data = NULL;
  1850. }
  1851. // filter: save
  1852. if ($this->has_filter('save'))
  1853. {
  1854. $new_data = $this->apply_filters('save', $new_data, $real_post_id);
  1855. /**
  1856. * halt saving
  1857. * @since 1.3.4
  1858. */
  1859. if (FALSE === $new_data) return $post_id;
  1860. WPAlchemy_MetaBox::clean($new_data);
  1861. }
  1862. // get current fields, use $real_post_id (checked for in both modes)
  1863. $current_fields = get_post_meta($real_post_id, $this->id . '_fields', TRUE);
  1864. if ($this->mode == WPALCHEMY_MODE_EXTRACT)
  1865. {
  1866. $new_fields = array();
  1867. if (is_array($new_data))
  1868. {
  1869. foreach ($new_data as $k => $v)
  1870. {
  1871. $field = $this->prefix . $k;
  1872. array_push($new_fields,$field);
  1873. $new_value = $new_data[$k];
  1874. if (is_null($new_value))
  1875. {
  1876. delete_post_meta($post_id, $field);
  1877. }
  1878. else
  1879. {
  1880. update_post_meta($post_id, $field, $new_value);
  1881. }
  1882. }
  1883. }
  1884. $diff_fields = array_diff((array)$current_fields,$new_fields);
  1885. if (is_array($diff_fields))
  1886. {
  1887. foreach ($diff_fields as $field)
  1888. {
  1889. delete_post_meta($post_id,$field);
  1890. }
  1891. }
  1892. delete_post_meta($post_id, $this->id . '_fields');
  1893. if ( ! empty($new_fields))
  1894. {
  1895. add_post_meta($post_id,$this->id . '_fields', $new_fields, TRUE);
  1896. }
  1897. // keep data tidy, delete values if previously using WPALCHEMY_MODE_ARRAY
  1898. delete_post_meta($post_id, $this->id);
  1899. }
  1900. else
  1901. {
  1902. if (is_null($new_data))
  1903. {
  1904. delete_post_meta($post_id, $this->id);
  1905. }
  1906. else
  1907. {
  1908. update_post_meta($post_id, $this->id, $new_data);
  1909. }
  1910. // keep data tidy, delete values if previously using WPALCHEMY_MODE_EXTRACT
  1911. if (is_array($current_fields))
  1912. {
  1913. foreach ($current_fields as $field)
  1914. {
  1915. delete_post_meta($post_id, $field);
  1916. }
  1917. delete_post_meta($post_id, $this->id . '_fields');
  1918. }
  1919. }
  1920. // action: save
  1921. if ($this->has_action('save'))
  1922. {
  1923. $this->do_action('save', $new_data, $real_post_id);
  1924. }
  1925. return $post_id;
  1926. }
  1927. /**
  1928. * Cleans an array, removing blank ('') values
  1929. *
  1930. * @static
  1931. * @since 1.0
  1932. * @access public
  1933. * @param array the array to clean (passed by reference)
  1934. */
  1935. function clean(&$arr)
  1936. {
  1937. if (is_array($arr))
  1938. {
  1939. foreach ($arr as $i => $v)
  1940. {
  1941. if (is_array($arr[$i]))
  1942. {
  1943. WPAlchemy_MetaBox::clean($arr[$i]);
  1944. if (!count($arr[$i]))
  1945. {
  1946. unset($arr[$i]);
  1947. }
  1948. }
  1949. else
  1950. {
  1951. if ('' == trim($arr[$i]) OR is_null($arr[$i]))
  1952. {
  1953. unset($arr[$i]);
  1954. }
  1955. }
  1956. }
  1957. if (!count($arr))

Large files files are truncated, but you can click here to view the full file