RW_Meta_Box

A class to rapid develop meta boxes for custom & built in content types Piggybacks on WordPress.

Defined (2)

The class is defined in the following location(s).

/meta-box/inc/classes/meta-box.php  
  1. class RW_Meta_Box 
  2. /** 
  3. * Meta box information 
  4. */ 
  5. var $meta_box; 
  6.  
  7. /** 
  8. * Fields information 
  9. */ 
  10. var $fields; 
  11.  
  12. /** 
  13. * Contains all field types of current meta box 
  14. */ 
  15. var $types; 
  16.  
  17. /** 
  18. * Create meta box based on given data 
  19. * @see demo/demo.php file for details 
  20. * @param array $meta_box Meta box definition 
  21. * @return \RW_Meta_Box 
  22. */ 
  23. function __construct( $meta_box ) 
  24. // Run script only in admin area 
  25. if ( ! is_admin() ) 
  26. return; 
  27.  
  28. // Assign meta box values to local variables and add it's missed values 
  29. $this->meta_box = self::normalize( $meta_box ); 
  30. $this->fields = &$this->meta_box['fields']; 
  31.  
  32. // List of meta box field types 
  33. $this->types = array_unique( wp_list_pluck( $this->fields, 'type' ) ); 
  34.  
  35. // Load translation file 
  36. // Call directly because we define meta boxes in 'admin_init' hook (@see demo/demo.php) 
  37. // So the function won't run if we use 'add_action' to load textdomain here 
  38. self::load_textdomain(); 
  39.  
  40. // Enqueue common styles and scripts 
  41. add_action( 'admin_enqueue_scripts', array( &$this, 'admin_enqueue_scripts' ) ); 
  42.  
  43. foreach ( $this->types as $type ) 
  44. $class = self::get_class_name( $type ); 
  45.  
  46. // Add additional actions for fields 
  47. if ( method_exists( $class, 'add_actions' ) ) 
  48. call_user_func( array( $class, 'add_actions' ) ); 
  49.  
  50. // Add meta box 
  51. foreach ( $this->meta_box['pages'] as $page ) 
  52. add_action( "add_meta_boxes_{$page}", array( &$this, 'add_meta_boxes' ) ); 
  53.  
  54. // Save post meta 
  55. add_action( 'save_post', array( &$this, 'save_post' ) ); 
  56.  
  57. /** 
  58. * Load plugin translation 
  59. * @link http://wordpress.stackexchange.com/a/33314 Translation Tutorial by the author 
  60. * @return void 
  61. */ 
  62. static function load_textdomain() 
  63. // l18n translation files 
  64. $locale = get_locale(); 
  65. $dir = trailingslashit( RWMB_DIR . 'lang' ); 
  66. $mofile = "{$dir}{$locale}.mo"; 
  67.  
  68. // In themes/plugins/mu-plugins directory 
  69. load_textdomain( 'rwmb', $mofile ); 
  70.  
  71. /** 
  72. * Enqueue common styles 
  73. * @return void 
  74. */ 
  75. function admin_enqueue_scripts() 
  76. $screen = get_current_screen(); 
  77.  
  78. // Enqueue scripts and styles for registered pages (post types) only 
  79. if ( 'post' != $screen->base || ! in_array( $screen->post_type, $this->meta_box['pages'] ) ) 
  80. return; 
  81.  
  82. wp_enqueue_style( 'rwmb', RWMB_CSS_URL . 'style.css', RWMB_VER ); 
  83.  
  84. // Load clone script conditionally 
  85. $has_clone = false; 
  86. foreach ( $this->fields as $field ) 
  87. if ( self::is_cloneable( $field ) ) 
  88. $has_clone = true; 
  89.  
  90. // Enqueue scripts and styles for fields 
  91. $class = self::get_class_name( $field['type'] ); 
  92. if ( method_exists( $class, 'admin_enqueue_scripts' ) ) 
  93. call_user_func( array( $class, 'admin_enqueue_scripts' ) ); 
  94.  
  95. if ( $has_clone ) 
  96. wp_enqueue_script( 'rwmb-clone', RWMB_JS_URL . 'clone.js', array( 'jquery' ), RWMB_VER, true ); 
  97.  
  98. /************************************************** 
  99. SHOW META BOX 
  100. **************************************************/ 
  101.  
  102. /** 
  103. * Add meta box for multiple post types 
  104. * @return void 
  105. */ 
  106. function add_meta_boxes() 
  107. foreach ( $this->meta_box['pages'] as $page ) 
  108. add_meta_box( 
  109. $this->meta_box['id'],  
  110. $this->meta_box['title'],  
  111. array( &$this, 'show' ),  
  112. $page,  
  113. $this->meta_box['context'],  
  114. $this->meta_box['priority'] 
  115. ); 
  116.  
  117. /** 
  118. * Callback function to show fields in meta box 
  119. * @return void 
  120. */ 
  121. public function show() 
  122. global $post; 
  123.  
  124. $saved = self::has_been_saved( $post->ID, $this->fields ); 
  125.  
  126. wp_nonce_field( "rwmb-save-{$this->meta_box['id']}", "nonce_{$this->meta_box['id']}" ); 
  127.  
  128. // Allow users to add custom code before meta box content 
  129. // 1st action applies to all meta boxes 
  130. // 2nd action applies to only current meta box 
  131. do_action( 'rwmb_before' ); 
  132. do_action( "rwmb_before_{$this->meta_box['id']}" ); 
  133.  
  134. foreach ( $this->fields as $field ) 
  135. $type = $field['type']; 
  136. $id = $field['id']; 
  137. $meta = self::apply_field_class_filters( $field, 'meta', '', $post->ID, $saved ); 
  138. $meta = apply_filters( "rwmb_{$type}_meta", $meta ); 
  139. $meta = apply_filters( "rwmb_{$id}_meta", $meta ); 
  140.  
  141. $begin = self::apply_field_class_filters( $field, 'begin_html', '', $meta ); 
  142.  
  143. // Apply filter to field begin HTML 
  144. // 1st filter applies to all fields 
  145. // 2nd filter applies to all fields with the same type 
  146. // 3rd filter applies to current field only 
  147. $begin = apply_filters( "rwmb_begin_html", $begin, $field, $meta ); 
  148. $begin = apply_filters( "rwmb_{$type}_begin_html", $begin, $field, $meta ); 
  149. $begin = apply_filters( "rwmb_{$id}_begin_html", $begin, $field, $meta ); 
  150.  
  151. // Separate code for clonable and non-cloneable fields to make easy to maintain 
  152.  
  153. // Cloneable fields 
  154. if ( self::is_cloneable( $field ) ) 
  155. $field_html = ''; 
  156.  
  157. $meta = (array) $meta; 
  158. foreach ( $meta as $meta_data ) 
  159. add_filter( "rwmb_{$id}_html", array( &$this, 'add_delete_clone_button' ), 10, 3 ); 
  160.  
  161. // Wrap field HTML in a div with class="rwmb-clone" if needed 
  162. $input_html = '<div class="rwmb-clone">'; 
  163.  
  164. // Call separated methods for displaying each type of field 
  165. $input_html .= self::apply_field_class_filters( $field, 'html', '', $meta_data ); 
  166.  
  167. // Apply filter to field HTML 
  168. // 1st filter applies to all fields with the same type 
  169. // 2nd filter applies to current field only 
  170. $input_html = apply_filters( "rwmb_{$type}_html", $input_html, $field, $meta_data ); 
  171. $input_html = apply_filters( "rwmb_{$id}_html", $input_html, $field, $meta_data ); 
  172.  
  173. $input_html .= '</div>'; 
  174.  
  175. $field_html .= $input_html; 
  176. // Non-cloneable fields 
  177. else 
  178. // Call separated methods for displaying each type of field 
  179. $field_html = self::apply_field_class_filters( $field, 'html', '', $meta ); 
  180.  
  181. // Apply filter to field HTML 
  182. // 1st filter applies to all fields with the same type 
  183. // 2nd filter applies to current field only 
  184. $field_html = apply_filters( "rwmb_{$type}_html", $field_html, $field, $meta ); 
  185. $field_html = apply_filters( "rwmb_{$id}_html", $field_html, $field, $meta ); 
  186.  
  187. $end = self::apply_field_class_filters( $field, 'end_html', '', $meta ); 
  188.  
  189. // Apply filter to field end HTML 
  190. // 1st filter applies to all fields 
  191. // 2nd filter applies to all fields with the same type 
  192. // 3rd filter applies to current field only 
  193. $end = apply_filters( "rwmb_end_html", $end, $field, $meta ); 
  194. $end = apply_filters( "rwmb_{$type}_end_html", $end, $field, $meta ); 
  195. $end = apply_filters( "rwmb_{$id}_end_html", $end, $field, $meta ); 
  196.  
  197. // Apply filter to field wrapper 
  198. // This allow users to change whole HTML markup of the field wrapper (i.e. table row) 
  199. // 1st filter applies to all fields with the same type 
  200. // 2nd filter applies to current field only 
  201. $html = apply_filters( "rwmb_{$type}_wrapper_html", "{$begin}{$field_html}{$end}", $field, $meta ); 
  202. $html = apply_filters( "rwmb_{$id}_wrapper_html", $html, $field, $meta ); 
  203.  
  204. // Display label and input in DIV and allow user-defined classes to be appended 
  205. $class = 'rwmb-field'; 
  206. if ( isset( $field['class'] ) ) 
  207. $class = $this->add_cssclass( $field[ 'class' ], $class ); 
  208.  
  209. // If the 'hidden' argument is set and TRUE, the div will be hidden 
  210. if ( isset( $field['hidden'] ) && $field['hidden'] ) 
  211. $class = $this->add_cssclass( 'hidden', $class ); 
  212. echo "<div class='{$class}'>{$html}</div>"; 
  213.  
  214. // Allow users to add custom code after meta box content 
  215. // 1st action applies to all meta boxes 
  216. // 2nd action applies to only current meta box 
  217. do_action( 'rwmb_after' ); 
  218. do_action( "rwmb_after_{$this->meta_box['id']}" ); 
  219.  
  220. /** 
  221. * Show begin HTML markup for fields 
  222. * @param string $html 
  223. * @param mixed $meta 
  224. * @param array $field 
  225. * @return string 
  226. */ 
  227. static function begin_html( $html, $meta, $field ) 
  228. $class = 'rwmb-label'; 
  229. if ( ! empty( $field['class'] ) ) 
  230. $class = self::add_cssclass( $field['class'], $class ); 
  231.  
  232. if ( empty( $field['name'] ) ) 
  233. return '<div class="rwmb-input">'; 
  234.  
  235. $html = <<<HTML 
  236. <div class="{$class}"> 
  237. <label for="{$field['id']}">{$field['name']}</label> 
  238. </div> 
  239. <div class="rwmb-input"> 
  240. HTML; 
  241.  
  242. return $html; 
  243.  
  244. /** 
  245. * Show end HTML markup for fields 
  246. * @param string $html 
  247. * @param mixed $meta 
  248. * @param array $field 
  249. * @return string 
  250. */ 
  251. static function end_html( $html, $meta, $field ) 
  252. $id = $field['id']; 
  253.  
  254. $button = ''; 
  255. if ( self::is_cloneable( $field ) ) 
  256. $button = '<a href="#" class="rwmb-button button-primary add-clone">' . __( '+', 'rwmb' ) . '</a>'; 
  257.  
  258. $desc = ! empty( $field[ 'desc' ] ) ? "<p id='{$id}_description' class='description'>{$field['desc']}</p>" : ''; 
  259.  
  260. // Closes the container 
  261. $html = "{$button}{$desc}</div>"; 
  262.  
  263. return $html; 
  264.  
  265. /** 
  266. * Callback function to add clone buttons on demand 
  267. * Hooks on the flight into the "rwmb_{$field_id}_html" filter before the closing div 
  268. * @param string $html 
  269. * @param array $field 
  270. * @param mixed $meta_data 
  271. * @return string $html 
  272. */ 
  273. static function add_delete_clone_button( $html, $field, $meta_data ) 
  274. $id = $field['id']; 
  275.  
  276. $button = '<a href="#" class="rwmb-button button-secondary remove-clone">' . __( '–', 'rwmb' ) . '</a>'; 
  277.  
  278. return "{$html}{$button}"; 
  279.  
  280. /** 
  281. * Standard meta retrieval 
  282. * @param mixed $meta 
  283. * @param int $post_id 
  284. * @param array $field 
  285. * @param bool $saved 
  286. * @return mixed 
  287. */ 
  288. static function meta( $meta, $post_id, $saved, $field ) 
  289. $meta = get_post_meta( $post_id, $field['id'], ! $field['multiple'] ); 
  290.  
  291. // Use $field['std'] only when the meta box hasn't been saved (i.e. the first time we run) 
  292. $meta = ( ! $saved && '' === $meta || array() === $meta ) ? $field['std'] : $meta; 
  293.  
  294. // Escape attributes for non-wysiwyg fields 
  295. if ( 'wysiwyg' !== $field['type'] ) 
  296. $meta = is_array( $meta ) ? array_map( 'esc_attr', $meta ) : esc_attr( $meta ); 
  297.  
  298. return $meta; 
  299.  
  300. /************************************************** 
  301. SAVE META BOX 
  302. **************************************************/ 
  303.  
  304. /** 
  305. * Save data from meta box 
  306. * @param int $post_id Post ID 
  307. * @return int|void 
  308. */ 
  309. function save_post( $post_id ) 
  310. global $post_type; 
  311. $post_type_object = get_post_type_object( $post_type ); 
  312.  
  313. // Check whether: 
  314. // - the post is autosaved 
  315. // - the post is a revision 
  316. // - current post type is supported 
  317. // - user has proper capability 
  318. if ( 
  319. ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) 
  320. || ( ! isset( $_POST['post_ID'] ) || $post_id != $_POST['post_ID'] ) 
  321. || ( ! in_array( $post_type, $this->meta_box['pages'] ) ) 
  322. || ( ! current_user_can( $post_type_object->cap->edit_post, $post_id ) ) 
  323. return $post_id; 
  324.  
  325. // Verify nonce 
  326. check_admin_referer( "rwmb-save-{$this->meta_box['id']}", "nonce_{$this->meta_box['id']}" ); 
  327.  
  328. foreach ( $this->fields as $field ) 
  329. $name = $field['id']; 
  330. $old = get_post_meta( $post_id, $name, ! $field['multiple'] ); 
  331. $new = isset( $_POST[ $name ] ) ? $_POST[ $name ] : ( $field['multiple'] ? array() : '' ); 
  332.  
  333. // Allow field class change the value 
  334. $new = self::apply_field_class_filters( $field, 'value', $new, $old, $post_id ); 
  335.  
  336. // Use filter to change field value 
  337. // 1st filter applies to all fields with the same type 
  338. // 2nd filter applies to current field only 
  339. $new = apply_filters( "rwmb_{$field['type']}_value", $new, $field, $old ); 
  340. $new = apply_filters( "rwmb_{$name}_value", $new, $field, $old ); 
  341.  
  342. // Call defined method to save meta value, if there's no methods, call common one 
  343. self::do_field_class_actions( $field, 'save', $new, $old, $post_id ); 
  344.  
  345. /** 
  346. * Common functions for saving field 
  347. * @param mixed $new 
  348. * @param mixed $old 
  349. * @param int $post_id 
  350. * @param array $field 
  351. * @return void 
  352. */ 
  353. static function save( $new, $old, $post_id, $field ) 
  354. $name = $field['id']; 
  355.  
  356. delete_post_meta( $post_id, $name ); 
  357. if ( '' === $new || array() === $new ) 
  358. return; 
  359.  
  360. if ( $field['multiple'] ) 
  361. foreach ( $new as $add_new ) 
  362. add_post_meta( $post_id, $name, $add_new, false ); 
  363. else 
  364. update_post_meta( $post_id, $name, $new ); 
  365.  
  366. /************************************************** 
  367. HELPER FUNCTIONS 
  368. **************************************************/ 
  369.  
  370. /** 
  371. * Normalize parameters for meta box 
  372. * @param array $meta_box Meta box definition 
  373. * @return array $meta_box Normalized meta box 
  374. */ 
  375. static function normalize( $meta_box ) 
  376. // Set default values for meta box 
  377. $meta_box = wp_parse_args( $meta_box, array( 
  378. 'context' => 'normal',  
  379. 'priority' => 'high',  
  380. 'pages' => array( 'post' ) 
  381. ) ); 
  382.  
  383. // Set default values for fields 
  384. foreach ( $meta_box['fields'] as &$field ) 
  385. $clone = isset( $field['clone'] ) ? $field['clone'] : false; 
  386. $multiple = in_array( $field['type'], array( 'checkbox_list', 'file', 'image', 'plupload_image' ) ) ; 
  387. $std = $multiple ? array() : ''; 
  388. $format = 'date' === $field['type'] ? 'yy-mm-dd' : ( 'time' === $field['type'] ? 'hh:mm' : '' ); 
  389.  
  390. $field = wp_parse_args( $field, array( 
  391. 'multiple' => $multiple,  
  392. 'clone' => $clone,  
  393. 'std' => $std,  
  394. 'desc' => '',  
  395. 'format' => $format 
  396. ) ); 
  397.  
  398. $field['field_name'] = $field['id'] . ( $field['multiple'] || $field['clone'] ? '[]' : '' ); 
  399.  
  400. // Allow field class add/change default field values 
  401. $field = self::apply_field_class_filters( $field, 'normalize_field', $field ); 
  402.  
  403. return $meta_box; 
  404.  
  405. /** 
  406. * Get field class name 
  407. * @param string $type Field type 
  408. * @return bool|string Field class name OR false on failure 
  409. */ 
  410. static function get_class_name( $type ) 
  411. $type = ucwords( $type ); 
  412. $class = "RWMB_{$type}_Field"; 
  413.  
  414. if ( class_exists( $class ) ) 
  415. return $class; 
  416.  
  417. return false; 
  418.  
  419. /** 
  420. * Apply filters by field class, fallback to RW_Meta_Box method 
  421. * @param array $field 
  422. * @param string $method_name 
  423. * @param mixed $value 
  424. * @return mixed $value 
  425. */ 
  426. static function apply_field_class_filters( $field, $method_name, $value ) 
  427. $args = array_slice( func_get_args(), 2 ); 
  428. $args[] = $field; 
  429.  
  430. // Call: field class method 
  431. // Fallback: RW_Meta_Box method 
  432. $class = self::get_class_name( $field['type'] ); 
  433. if ( method_exists( $class, $method_name ) ) 
  434. $value = call_user_func_array( array( $class, $method_name ), $args ); 
  435. elseif ( method_exists( __CLASS__, $method_name ) ) 
  436. $value = call_user_func_array( array( __CLASS__, $method_name ), $args ); 
  437.  
  438. return $value; 
  439.  
  440. /** 
  441. * Call field class method for actions, fallback to RW_Meta_Box method 
  442. * @param array $field 
  443. * @param string $method_name 
  444. * @return mixed 
  445. */ 
  446. static function do_field_class_actions( $field, $method_name ) 
  447. $args = array_slice( func_get_args(), 2 ); 
  448. $args[] = $field; 
  449.  
  450. // Call: field class method 
  451. // Fallback: RW_Meta_Box method 
  452. $class = self::get_class_name( $field['type'] ); 
  453. if ( method_exists( $class, $method_name ) ) 
  454. call_user_func_array( array( $class, $method_name ), $args ); 
  455. elseif ( method_exists( __CLASS__, $method_name ) ) 
  456. call_user_func_array( array( __CLASS__, $method_name ), $args ); 
  457.  
  458. /** 
  459. * Format Ajax response 
  460. * @param string $message 
  461. * @param string $status 
  462. * @return void 
  463. */ 
  464. static function ajax_response( $message, $status ) 
  465. $response = array( 'what' => 'meta-box' ); 
  466. $response['data'] = 'error' === $status ? new WP_Error( 'error', $message ) : $message; 
  467. $x = new WP_Ajax_Response( $response ); 
  468. $x->send(); 
  469.  
  470. /** 
  471. * Check if meta box has been saved 
  472. * This helps saving empty value in meta fields (for text box, check box, etc.) 
  473. * @param int $post_id 
  474. * @param array $fields 
  475. * @return bool 
  476. */ 
  477. static function has_been_saved( $post_id, $fields ) 
  478. $saved = false; 
  479. foreach ( $fields as $field ) 
  480. if ( get_post_meta( $post_id, $field['id'], ! $field['multiple'] ) ) 
  481. $saved = true; 
  482. break; 
  483. return $saved; 
  484.  
  485. /** 
  486. * Adds a css class 
  487. * Mainly a copy of the core admin menu function 
  488. * As the core function is only meant to be used by core internally,  
  489. * We copy it here - in case core changes functionality or drops the function. 
  490. * @param string $add 
  491. * @param string $class | Class name - Default: empty 
  492. * @return string $class 
  493. */ 
  494. static function add_cssclass( $add, $class = '' ) 
  495. $class .= empty( $class ) ? $add : " {$add}"; 
  496.  
  497. return $class; 
  498.  
  499.  
  500. /** 
  501. * Helper function to check for multi/clone field IDs 
  502. * @param array $field 
  503. * @return bool False if no cloneable 
  504. */ 
  505. static function is_cloneable( $field ) 
  506. return $field['clone']; 
/meta-box/meta-box-3.2.2.class.php  
  1. class RW_Meta_Box { 
  2.  
  3. protected $_meta_box; 
  4. protected $_fields; 
  5.  
  6. // Create meta box based on given data 
  7. function __construct($meta_box) { 
  8. // run script only in admin area 
  9. if (!is_admin()) return; 
  10.  
  11. // assign meta box values to local variables and add it's missed values 
  12. $this->_meta_box = $meta_box; 
  13. $this->_fields = &$this->_meta_box['fields']; 
  14. $this->add_missed_values(); 
  15.  
  16. add_action('add_meta_boxes', array(&$this, 'add')); // add meta box, using 'add_meta_boxes' for WP 3.0+ 
  17. add_action('save_post', array(&$this, 'save')); // save meta box's data 
  18.  
  19. // check for some special fields and add needed actions for them 
  20. $this->check_field_upload(); 
  21. $this->check_field_color(); 
  22. $this->check_field_date(); 
  23. $this->check_field_time(); 
  24. $this->check_field_wysiwyg(); 
  25.  
  26. // load common js, css files 
  27. // must enqueue for all pages as we need js for the media upload, too 
  28. add_action('admin_print_styles', array(__CLASS__, 'js_css')); 
  29.  
  30. // Load common js, css files for the script 
  31. static function js_css() { 
  32. // change '\' to '/' in case using Windows 
  33. $content_dir = str_replace('\\', '/', WP_CONTENT_DIR); 
  34. $script_dir = str_replace('\\', '/', dirname(__FILE__)); 
  35.  
  36. // get URL of the directory of current file, this works in both theme or plugin 
  37. $base_url = str_replace($content_dir, WP_CONTENT_URL, $script_dir); 
  38.  
  39. wp_enqueue_style('rw-meta-box', $base_url . '/meta-box.css'); 
  40. wp_enqueue_script('rw-meta-box', $base_url . '/meta-box.js', array('jquery'), null, true); 
  41.  
  42. /******************** BEGIN UPLOAD **********************/ 
  43.  
  44. // Check field upload and add needed actions 
  45. function check_field_upload() { 
  46. if (!$this->has_field('image') && !$this->has_field('file')) return; 
  47.  
  48. add_action('post_edit_form_tag', array(&$this, 'add_enctype')); // add data encoding type for file uploading 
  49.  
  50. // make upload feature works even when custom post type doesn't support 'editor' 
  51. wp_enqueue_script('media-upload'); 
  52. add_thickbox(); 
  53. wp_enqueue_script('jquery-ui-core'); 
  54. wp_enqueue_script('jquery-ui-sortable'); 
  55.  
  56. add_filter('media_upload_gallery', array(&$this, 'insert_images')); // process adding multiple images to image meta field 
  57. add_filter('media_upload_library', array(&$this, 'insert_images')); 
  58. add_filter('media_upload_image', array(&$this, 'insert_images')); 
  59.  
  60. // add_action('delete_post', array(&$this, 'delete_attachments')); // delete all attachments when delete post 
  61. add_action('wp_ajax_rw_delete_file', array(&$this, 'delete_file')); // ajax delete files 
  62. add_action('wp_ajax_rw_reorder_images', array(&$this, 'reorder_images')); // ajax reorder images 
  63.  
  64. // Add data encoding type for file uploading 
  65. function add_enctype() { 
  66. echo ' enctype="multipart/form-data"'; 
  67.  
  68. // Process adding images to image meta field, modifiy from 'Faster image insert' plugin 
  69. function insert_images() { 
  70. if (!isset($_POST['rw-insert']) || empty($_POST['attachments'])) return; 
  71.  
  72. check_admin_referer('media-form'); 
  73.  
  74. $nonce = wp_create_nonce('rw_ajax_delete'); 
  75. $post_id = $_POST['post_id']; 
  76. $id = $_POST['field_id']; 
  77.  
  78. // modify the insertion string 
  79. $html = ''; 
  80. foreach ($_POST['attachments'] as $attachment_id => $attachment) { 
  81. $attachment = stripslashes_deep($attachment); 
  82. if (empty($attachment['selected']) || empty($attachment['url'])) continue; 
  83.  
  84. $li = "<li id='item_$attachment_id'>"; 
  85. $li .= "<img src='{$attachment['url']}' />"; 
  86. $li .= "<a title='" . __('Delete this image', 'delicacy') . "' class='rw-delete-file' href='#' rel='$nonce|$post_id|$id|$attachment_id'>" . __('Delete', 'delicacy') . "</a>"; 
  87. $li .= "<input type='hidden' name='{$id}[]' value='$attachment_id' />"; 
  88. $li .= "</li>"; 
  89. $html .= $li; 
  90.  
  91. media_send_to_editor($html); 
  92.  
  93. // Delete all attachments when delete post 
  94. function delete_attachments($post_id) { 
  95. $attachments = get_posts(array( 
  96. 'numberposts' => -1,  
  97. 'post_type' => 'attachment',  
  98. 'post_parent' => $post_id 
  99. )); 
  100. if (!empty($attachments)) { 
  101. foreach ($attachments as $att) { 
  102. wp_delete_attachment($att->ID); 
  103.  
  104. // Ajax callback for deleting files. Modified from a function used by "Verve Meta Boxes" plugin (http://goo.gl/LzYSq) 
  105. function delete_file() { 
  106. if (!isset($_POST['data'])) die(); 
  107.  
  108. list($nonce, $post_id, $key, $attach_id) = explode('|', $_POST['data']); 
  109.  
  110. if (!wp_verify_nonce($nonce, 'rw_ajax_delete')) die('1'); 
  111.  
  112. // wp_delete_attachment($attach_id); 
  113. delete_post_meta($post_id, $key, $attach_id); 
  114.  
  115. die('0'); 
  116.  
  117. // Ajax callback for reordering images 
  118. function reorder_images() { 
  119. if (!isset($_POST['data'])) die(); 
  120.  
  121. list($order, $post_id, $key, $nonce) = explode('|', $_POST['data']); 
  122.  
  123. if (!wp_verify_nonce($nonce, 'rw_ajax_reorder')) die('1'); 
  124.  
  125. parse_str($order, $items); 
  126. $items = $items['item']; 
  127. $order = 1; 
  128. foreach ($items as $item) { 
  129. wp_update_post(array( 
  130. 'ID' => $item,  
  131. 'post_parent' => $post_id,  
  132. 'menu_order' => $order 
  133. )); 
  134. $order++; 
  135.  
  136. die('0'); 
  137.  
  138. /******************** END UPLOAD **********************/ 
  139.  
  140. /******************** BEGIN OTHER FIELDS **********************/ 
  141.  
  142. // Check field color 
  143. function check_field_color() { 
  144. if ($this->has_field('color') && self::is_edit_page()) { 
  145. wp_enqueue_style('farbtastic'); // enqueue built-in script and style for color picker 
  146. wp_enqueue_script('farbtastic'); 
  147.  
  148. // Check field date 
  149. function check_field_date() { 
  150. if ($this->has_field('date') && self::is_edit_page()) { 
  151. // add style and script, use proper jQuery UI version 
  152. wp_enqueue_style('rw-jquery-ui-css', 'http://ajax.googleapis.com/ajax/libs/jqueryui/' . self::get_jqueryui_ver() . '/themes/base/jquery-ui.css'); 
  153. wp_enqueue_script('rw-jquery-ui', 'https://ajax.googleapis.com/ajax/libs/jqueryui/' . self::get_jqueryui_ver() . '/jquery-ui.min.js', array('jquery')); 
  154.  
  155. // Check field time 
  156. function check_field_time() { 
  157. if ($this->has_field('time') && self::is_edit_page()) { 
  158. // add style and script, use proper jQuery UI version 
  159. wp_enqueue_style('rw-jquery-ui-css', 'http://ajax.googleapis.com/ajax/libs/jqueryui/' . self::get_jqueryui_ver() . '/themes/base/jquery-ui.css'); 
  160. wp_enqueue_script('rw-jquery-ui', 'https://ajax.googleapis.com/ajax/libs/jqueryui/' . self::get_jqueryui_ver() . '/jquery-ui.min.js', array('jquery')); 
  161. wp_enqueue_script('rw-timepicker', 'https://github.com/trentrichardson/jQuery-Timepicker-Addon/raw/master/jquery-ui-timepicker-addon.js', array('rw-jquery-ui')); 
  162.  
  163. // Check field WYSIWYG 
  164. function check_field_wysiwyg() { 
  165. if ($this->has_field('wysiwyg') && self::is_edit_page()) { 
  166. add_action('admin_print_footer_scripts', 'wp_tiny_mce', 25); 
  167.  
  168. /******************** END OTHER FIELDS **********************/ 
  169.  
  170. /******************** BEGIN META BOX PAGE **********************/ 
  171.  
  172. // Add meta box for multiple post types 
  173. function add() { 
  174. foreach ($this->_meta_box['pages'] as $page) { 
  175. add_meta_box($this->_meta_box['id'], $this->_meta_box['title'], array(&$this, 'show'), $page, $this->_meta_box['context'], $this->_meta_box['priority']); 
  176.  
  177. // Callback function to show fields in meta box 
  178. function show() { 
  179. global $post; 
  180.  
  181. wp_nonce_field(basename(__FILE__), 'rw_meta_box_nonce'); 
  182. echo '<table class="form-table">'; 
  183.  
  184. foreach ($this->_fields as $field) { 
  185. $meta = get_post_meta($post->ID, $field['id'], !$field['multiple']); 
  186. $meta = ($meta !== '') ? $meta : $field['std']; 
  187.  
  188. $meta = is_array($meta) ? array_map('esc_attr', $meta) : esc_attr($meta); 
  189.  
  190. echo '<tr>'; 
  191. // call separated methods for displaying each type of field 
  192. call_user_func(array(&$this, 'show_field_' . $field['type']), $field, $meta); 
  193. echo '</tr>'; 
  194. echo '</table>'; 
  195.  
  196. /******************** END META BOX PAGE **********************/ 
  197.  
  198. /******************** BEGIN META BOX FIELDS **********************/ 
  199.  
  200. function show_field_begin($field, $meta) { 
  201. echo "<th class='rw-label'><label for='{$field['id']}'>{$field['name']}</label></th><td class='rw-field'>"; 
  202.  
  203. function show_field_end($field, $meta) { 
  204. echo "<br />{$field['desc']}</td>"; 
  205.  
  206. function show_field_text($field, $meta) { 
  207. $this->show_field_begin($field, $meta); 
  208. echo "<input type='text' class='rw-text' name='{$field['id']}' id='{$field['id']}' value='$meta' size='30' />"; 
  209. $this->show_field_end($field, $meta); 
  210.  
  211. function show_field_textarea($field, $meta) { 
  212. $this->show_field_begin($field, $meta); 
  213. echo "<textarea class='rw-textarea large-text' name='{$field['id']}' id='{$field['id']}' cols='60' rows='10'>$meta</textarea>"; 
  214. $this->show_field_end($field, $meta); 
  215.  
  216. function show_field_select($field, $meta) { 
  217. if (!is_array($meta)) $meta = (array) $meta; 
  218. $this->show_field_begin($field, $meta); 
  219. echo "<select class='rw-select' name='{$field['id']}" . ($field['multiple'] ? "[]' id='{$field['id']}' multiple='multiple'" : "'") . ">"; 
  220. foreach ($field['options'] as $key => $value) { 
  221. echo "<option value='$key'" . selected(in_array($key, $meta), true, false) . ">$value</option>"; 
  222. echo "</select>"; 
  223. $this->show_field_end($field, $meta); 
  224.  
  225. function show_field_radio($field, $meta) { 
  226. $this->show_field_begin($field, $meta); 
  227. foreach ($field['options'] as $key => $value) { 
  228. echo "<input type='radio' class='rw-radio' name='{$field['id']}' value='$key'" . checked($meta, $key, false) . " /> $value "; 
  229. $this->show_field_end($field, $meta); 
  230.  
  231. function show_field_checkbox($field, $meta) { 
  232. $this->show_field_begin($field, $meta); 
  233. echo "<input type='checkbox' class='rw-checkbox' name='{$field['id']}' id='{$field['id']}'" . checked(!empty($meta), true, false) . " /> {$field['desc']}</td>"; 
  234.  
  235. function show_field_wysiwyg($field, $meta) { 
  236. $this->show_field_begin($field, $meta); 
  237. echo "<textarea class='rw-wysiwyg theEditor large-text' name='{$field['id']}' id='{$field['id']}' cols='60' rows='10'>$meta</textarea>"; 
  238. $this->show_field_end($field, $meta); 
  239.  
  240. function show_field_file($field, $meta) { 
  241. global $post; 
  242.  
  243. if (!is_array($meta)) $meta = (array) $meta; 
  244.  
  245. $this->show_field_begin($field, $meta); 
  246. echo "{$field['desc']}<br />"; 
  247.  
  248. if (!empty($meta)) { 
  249. $nonce = wp_create_nonce('rw_ajax_delete'); 
  250. echo '<div style="margin-bottom: 10px"><strong>' . __('Uploaded files', 'delicacy') . '</strong></div>'; 
  251. echo '<ol class="rw-upload">'; 
  252. foreach ($meta as $att) { 
  253. // if (wp_attachment_is_image($att)) continue; // what's image uploader for? 
  254. echo "<li>" . wp_get_attachment_link($att, '' , false, false, ' ') . " (<a class='rw-delete-file' href='#' rel='$nonce|{$post->ID}|{$field['id']}|$att'>" . __('Delete', 'delicacy') . "</a>)</li>"; 
  255. echo '</ol>'; 
  256.  
  257. // show form upload 
  258. echo "<div style='clear: both'><strong>" . __('Upload new files', 'delicacy') . "</strong></div> 
  259. <div class='new-files'> 
  260. <div class='file-input'><input type='file' name='{$field['id']}[]' /></div> 
  261. <a class='rw-add-file' href='#'>" . __('Add more file', 'Delicacy') . "</a> 
  262. </div> 
  263. </td>"; 
  264.  
  265. function show_field_image($field, $meta) { 
  266. global $wpdb, $post; 
  267.  
  268. if (!is_array($meta)) $meta = (array) $meta; 
  269.  
  270. $this->show_field_begin($field, $meta); 
  271. echo "{$field['desc']}<br />"; 
  272.  
  273. $nonce_delete = wp_create_nonce('rw_ajax_delete'); 
  274. $nonce_sort = wp_create_nonce('rw_ajax_reorder'); 
  275.  
  276. echo "<input type='hidden' class='rw-images-data' value='{$post->ID}|{$field['id']}|$nonce_sort' /> 
  277. <ul class='rw-images rw-upload' id='rw-images-{$field['id']}'>"; 
  278.  
  279. // re-arrange images with 'menu_order', thanks Onur 
  280. if (!empty($meta)) { 
  281. $meta = implode(', ', $meta); 
  282. $images = $wpdb->get_col(" 
  283. SELECT ID FROM $wpdb->posts 
  284. WHERE post_type = 'attachment' 
  285. AND ID in ($meta) 
  286. ORDER BY menu_order ASC 
  287. "); 
  288. foreach ($images as $image) { 
  289. $src = wp_get_attachment_image_src($image); 
  290. $src = $src[0]; 
  291.  
  292. echo "<li id='item_$image'> 
  293. <img src='$src' /> 
  294. <a title='" . __('Delete this image', 'delicacy') . "' class='rw-delete-file' href='#' rel='$nonce_delete|{$post->ID}|{$field['id']}|$image'>" . __('Delete', 'delicacy') . "</a> 
  295. <input type='hidden' name='{$field['id']}[]' value='$image' /> 
  296. </li>"; 
  297. echo '</ul>'; 
  298.  
  299. echo "<a href='#' class='rw-upload-button button' rel='{$post->ID}|{$field['id']}'>" . __('Add more images', 'delicacy') . "</a>"; 
  300.  
  301. function show_field_color($field, $meta) { 
  302. if (empty($meta)) $meta = '#'; 
  303. $this->show_field_begin($field, $meta); 
  304. echo "<input class='rw-color' type='text' name='{$field['id']}' id='{$field['id']}' value='$meta' size='8' /> 
  305. <a href='#' class='rw-color-select' rel='{$field['id']}'>" . __('Select a color', 'delicacy') . "</a> 
  306. <div style='display:none' class='rw-color-picker' rel='{$field['id']}'></div>"; 
  307. $this->show_field_end($field, $meta); 
  308.  
  309. function show_field_checkbox_list($field, $meta) { 
  310. if (!is_array($meta)) $meta = (array) $meta; 
  311. $this->show_field_begin($field, $meta); 
  312. $html = array(); 
  313. foreach ($field['options'] as $key => $value) { 
  314. $html[] = "<input type='checkbox' class='rw-checkbox_list' name='{$field['id']}[]' value='$key'" . checked(in_array($key, $meta), true, false) . " /> $value"; 
  315. echo implode('<br />', $html); 
  316. $this->show_field_end($field, $meta); 
  317.  
  318. function show_field_date($field, $meta) { 
  319. $this->show_field_begin($field, $meta); 
  320. echo "<input type='text' class='rw-date' name='{$field['id']}' id='{$field['id']}' rel='{$field['format']}' value='$meta' size='30' />"; 
  321. $this->show_field_end($field, $meta); 
  322.  
  323. function show_field_time($field, $meta) { 
  324. $this->show_field_begin($field, $meta); 
  325. echo "<input type='text' class='rw-time' name='{$field['id']}' id='{$field['id']}' rel='{$field['format']}' value='$meta' size='30' />"; 
  326. $this->show_field_end($field, $meta); 
  327.  
  328. /******************** END META BOX FIELDS **********************/ 
  329.  
  330. /******************** BEGIN META BOX SAVE **********************/ 
  331.  
  332. // Save data from meta box 
  333. function save($post_id) { 
  334. global $post_type; 
  335. $post_type_object = get_post_type_object($post_type); 
  336.  
  337. if ((defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) // check autosave 
  338. || (!isset($_POST['post_ID']) || $post_id != $_POST['post_ID']) // check revision 
  339. || (!in_array($post_type, $this->_meta_box['pages'])) // check if current post type is supported 
  340. || (!check_admin_referer(basename(__FILE__), 'rw_meta_box_nonce')) // verify nonce 
  341. || (!current_user_can($post_type_object->cap->edit_post, $post_id))) { // check permission 
  342. return $post_id; 
  343.  
  344. foreach ($this->_fields as $field) { 
  345. $name = $field['id']; 
  346. $type = $field['type']; 
  347. $old = get_post_meta($post_id, $name, !$field['multiple']); 
  348. $new = isset($_POST[$name]) ? $_POST[$name] : ($field['multiple'] ? array() : ''); 
  349.  
  350. // validate meta value 
  351. if (class_exists('RW_Meta_Box_Validate') && method_exists('RW_Meta_Box_Validate', $field['validate_func'])) { 
  352. $new = call_user_func(array('RW_Meta_Box_Validate', $field['validate_func']), $new); 
  353.  
  354. // call defined method to save meta value, if there's no methods, call common one 
  355. $save_func = 'save_field_' . $type; 
  356. if (method_exists($this, $save_func)) { 
  357. call_user_func(array(&$this, 'save_field_' . $type), $post_id, $field, $old, $new); 
  358. } else { 
  359. $this->save_field($post_id, $field, $old, $new); 
  360.  
  361. // Common functions for saving field 
  362. function save_field($post_id, $field, $old, $new) { 
  363. $name = $field['id']; 
  364.  
  365. delete_post_meta($post_id, $name); 
  366. if ($new === '' || $new === array()) return; 
  367.  
  368. if ($field['multiple']) { 
  369. foreach ($new as $add_new) { 
  370. add_post_meta($post_id, $name, $add_new, false); 
  371. } else { 
  372. update_post_meta($post_id, $name, $new); 
  373.  
  374.  
  375. function save_field_wysiwyg($post_id, $field, $old, $new) { 
  376. $new = wpautop($new); 
  377. $this->save_field($post_id, $field, $old, $new); 
  378.  
  379. function save_field_file($post_id, $field, $old, $new) { 
  380. $name = $field['id']; 
  381. if (empty($_FILES[$name])) return; 
  382.  
  383. self::fix_file_array($_FILES[$name]); 
  384.  
  385. foreach ($_FILES[$name] as $position => $fileitem) { 
  386. $file = wp_handle_upload($fileitem, array('test_form' => false)); 
  387.  
  388. if (empty($file['file'])) continue; 
  389. $filename = $file['file']; 
  390.  
  391. $attachment = array( 
  392. 'post_mime_type' => $file['type'],  
  393. 'guid' => $file['url'],  
  394. 'post_parent' => $post_id,  
  395. 'post_title' => preg_replace('/\.[^.]+$/', '', basename($filename)),  
  396. 'post_content' => '' 
  397. ); 
  398. $id = wp_insert_attachment($attachment, $filename, $post_id); 
  399. if (!is_wp_error($id)) { 
  400. wp_update_attachment_metadata($id, wp_generate_attachment_metadata($id, $filename)); 
  401. add_post_meta($post_id, $name, $id, false); // save file's url in meta fields 
  402.  
  403. /******************** END META BOX SAVE **********************/ 
  404.  
  405. /******************** BEGIN HELPER FUNCTIONS **********************/ 
  406.  
  407. // Add missed values for meta box 
  408. function add_missed_values() { 
  409. // default values for meta box 
  410. $this->_meta_box = array_merge(array( 
  411. 'context' => 'normal',  
  412. 'priority' => 'high',  
  413. 'pages' => array('post') 
  414. ), $this->_meta_box); 
  415.  
  416. // default values for fields 
  417. foreach ($this->_fields as &$field) { 
  418. $multiple = in_array($field['type'], array('checkbox_list', 'file', 'image')); 
  419. $std = $multiple ? array() : ''; 
  420. $format = 'date' == $field['type'] ? 'yy-mm-dd' : ('time' == $field['type'] ? 'hh:mm' : ''); 
  421.  
  422. $field = array_merge(array( 
  423. 'multiple' => $multiple,  
  424. 'std' => $std,  
  425. 'desc' => '',  
  426. 'format' => $format,  
  427. 'validate_func' => '' 
  428. ), $field); 
  429.  
  430. // Check if field with $type exists 
  431. function has_field($type) { 
  432. foreach ($this->_fields as $field) { 
  433. if ($type == $field['type']) return true; 
  434. return false; 
  435.  
  436. // Check if current page is edit page 
  437. static function is_edit_page() { 
  438. global $pagenow; 
  439. return in_array($pagenow, array('post.php', 'post-new.php')); 
  440.  
  441. /** 
  442. * Fixes the odd indexing of multiple file uploads from the format: 
  443. * $_FILES['field']['key']['index'] 
  444. * To the more standard and appropriate: 
  445. * $_FILES['field']['index']['key'] 
  446. */ 
  447. static function fix_file_array(&$files) { 
  448. $output = array(); 
  449. foreach ($files as $key => $list) { 
  450. foreach ($list as $index => $value) { 
  451. $output[$index][$key] = $value; 
  452. $files = $output; 
  453.  
  454. // Get proper jQuery UI version to not conflict with WP admin scripts 
  455. static function get_jqueryui_ver() { 
  456. global $wp_version; 
  457. if (version_compare($wp_version, '3.1', '>=')) { 
  458. return '1.8.10'; 
  459.  
  460. return '1.7.3'; 
  461.  
  462. /******************** END HELPER FUNCTIONS **********************/