Meta Box - Version 4.10.4

Version Description

  • Improvement: Add support for validate user edit and term edit forms (requires MB Term Meta or MB User Meta extension).
  • Improvement: Add new parameter label_description for fields to display a description below field label (name).
  • Improvement: Add support for edit custom fields in the media modal. Requires post_type set to attachment and media_modal set to true. See demo.
  • Improvement: For WPML users: Add support to hide fields in "Do not translate" mode and disable editing fields in "Copy" mode.
  • Fix: Users can't select same images for 2 different image_advanced fields.
  • Fix: max_status doesn't work.
Download this release

Release Info

Developer rilwis
Plugin Icon 128x128 Meta Box
Version 4.10.4
Comparing to
See all releases

Code changes from version 4.10.3 to 4.10.4

inc/autoloader.php CHANGED
@@ -86,6 +86,7 @@ class RWMB_Autoloader {
86
  'meta-box',
87
  'validation',
88
  'sanitize',
 
89
 
90
  // Walkers.
91
  'walkers/walker',
86
  'meta-box',
87
  'validation',
88
  'sanitize',
89
+ 'media-modal',
90
 
91
  // Walkers.
92
  'walkers/walker',
inc/core.php CHANGED
@@ -27,7 +27,6 @@ class RWMB_Core {
27
  add_filter( "plugin_action_links_$plugin", array( $this, 'plugin_links' ) );
28
  add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
29
  add_action( 'init', array( $this, 'register_meta_boxes' ) );
30
- add_action( 'init', array( $this, 'register_wpml_hooks' ) );
31
  add_action( 'edit_page_form', array( $this, 'fix_page_template' ) );
32
  }
33
 
@@ -114,13 +113,4 @@ class RWMB_Core {
114
  delete_post_meta( $post->ID, '_wp_page_template' );
115
  }
116
  }
117
-
118
- /**
119
- * Register wpml compatibility hooks.
120
- */
121
- public function register_wpml_hooks() {
122
- if ( defined( 'ICL_SITEPRESS_VERSION' ) ) {
123
- new RWMB_WPML;
124
- }
125
- }
126
  }
27
  add_filter( "plugin_action_links_$plugin", array( $this, 'plugin_links' ) );
28
  add_action( 'plugins_loaded', array( $this, 'load_textdomain' ) );
29
  add_action( 'init', array( $this, 'register_meta_boxes' ) );
 
30
  add_action( 'edit_page_form', array( $this, 'fix_page_template' ) );
31
  }
32
 
113
  delete_post_meta( $post->ID, '_wp_page_template' );
114
  }
115
  }
 
 
 
 
 
 
 
 
 
116
  }
inc/field.php CHANGED
@@ -118,9 +118,13 @@ abstract class RWMB_Field {
118
  $field_label = '';
119
  if ( $field['name'] ) {
120
  $field_label = sprintf(
121
- '<div class="rwmb-label"><label for="%s">%s</label></div>',
122
- $field['id'],
123
- $field['name']
 
 
 
 
124
  );
125
  }
126
 
@@ -143,7 +147,18 @@ abstract class RWMB_Field {
143
  * @return string
144
  */
145
  public static function end_html( $meta, $field ) {
146
- return RWMB_Clone::add_clone_button( $field ) . self::call( 'element_description', $field ) . '</div>';
 
 
 
 
 
 
 
 
 
 
 
147
  }
148
 
149
  /**
@@ -152,8 +167,8 @@ abstract class RWMB_Field {
152
  * @param array $field Field parameters.
153
  * @return string
154
  */
155
- protected static function element_description( $field ) {
156
- $id = $field['id'] ? " id='{$field['id']}-description'" : '';
157
  return $field['desc'] ? "<p{$id} class='description'>{$field['desc']}</p>" : '';
158
  }
159
 
@@ -299,16 +314,17 @@ abstract class RWMB_Field {
299
  */
300
  public static function normalize( $field ) {
301
  $field = wp_parse_args( $field, array(
302
- 'id' => '',
303
- 'name' => '',
304
- 'multiple' => false,
305
- 'std' => '',
306
- 'desc' => '',
307
- 'format' => '',
308
- 'before' => '',
309
- 'after' => '',
310
- 'field_name' => isset( $field['id'] ) ? $field['id'] : '',
311
- 'placeholder' => '',
 
312
 
313
  'clone' => false,
314
  'max_clone' => 0,
118
  $field_label = '';
119
  if ( $field['name'] ) {
120
  $field_label = sprintf(
121
+ '<div class="rwmb-label">
122
+ <label for="%s">%s</label>
123
+ %s
124
+ </div>',
125
+ esc_attr( $field['id'] ),
126
+ $field['name'],
127
+ self::label_description( $field )
128
  );
129
  }
130
 
147
  * @return string
148
  */
149
  public static function end_html( $meta, $field ) {
150
+ return RWMB_Clone::add_clone_button( $field ) . self::call( 'input_description', $field ) . '</div>';
151
+ }
152
+
153
+ /**
154
+ * Display field label description.
155
+ *
156
+ * @param array $field Field parameters.
157
+ * @return string
158
+ */
159
+ protected static function label_description( $field ) {
160
+ $id = $field['id'] ? ' id="' . esc_attr( $field['id'] ) . '-label-description"' : '';
161
+ return $field['label_description'] ? "<p{$id} class='description'>{$field['label_description']}</p>" : '';
162
  }
163
 
164
  /**
167
  * @param array $field Field parameters.
168
  * @return string
169
  */
170
+ protected static function input_description( $field ) {
171
+ $id = $field['id'] ? ' id="' . esc_attr( $field['id'] ) . '-description"' : '';
172
  return $field['desc'] ? "<p{$id} class='description'>{$field['desc']}</p>" : '';
173
  }
174
 
314
  */
315
  public static function normalize( $field ) {
316
  $field = wp_parse_args( $field, array(
317
+ 'id' => '',
318
+ 'name' => '',
319
+ 'label_description' => '',
320
+ 'multiple' => false,
321
+ 'std' => '',
322
+ 'desc' => '',
323
+ 'format' => '',
324
+ 'before' => '',
325
+ 'after' => '',
326
+ 'field_name' => isset( $field['id'] ) ? $field['id'] : '',
327
+ 'placeholder' => '',
328
 
329
  'clone' => false,
330
  'max_clone' => 0,
inc/fields/checkbox.php CHANGED
@@ -42,7 +42,17 @@ class RWMB_Checkbox_Field extends RWMB_Input_Field {
42
  * @param array $field Field parameters.
43
  * @return string
44
  */
45
- public static function element_description( $field ) {
 
 
 
 
 
 
 
 
 
 
46
  return '';
47
  }
48
 
42
  * @param array $field Field parameters.
43
  * @return string
44
  */
45
+ public static function input_description( $field ) {
46
+ return '';
47
+ }
48
+
49
+ /**
50
+ * Do not show field label description.
51
+ *
52
+ * @param array $field Field parameters.
53
+ * @return string
54
+ */
55
+ public static function label_description( $field ) {
56
  return '';
57
  }
58
 
inc/fields/fieldset-text.php CHANGED
@@ -38,7 +38,17 @@ class RWMB_Fieldset_Text_Field extends RWMB_Text_Field {
38
  * @param array $field Field parameters.
39
  * @return string
40
  */
41
- public static function element_description( $field ) {
 
 
 
 
 
 
 
 
 
 
42
  return '';
43
  }
44
 
38
  * @param array $field Field parameters.
39
  * @return string
40
  */
41
+ public static function input_description( $field ) {
42
+ return '';
43
+ }
44
+
45
+ /**
46
+ * Do not show field description.
47
+ *
48
+ * @param array $field Field parameters.
49
+ * @return string
50
+ */
51
+ public static function label_description( $field ) {
52
  return '';
53
  }
54
 
inc/fields/heading.php CHANGED
@@ -38,6 +38,6 @@ class RWMB_Heading_Field extends RWMB_Field {
38
  * @return string
39
  */
40
  public static function end_html( $meta, $field ) {
41
- return self::element_description( $field );
42
  }
43
  }
38
  * @return string
39
  */
40
  public static function end_html( $meta, $field ) {
41
+ return self::input_description( $field );
42
  }
43
  }
inc/fields/key-value.php CHANGED
@@ -64,7 +64,17 @@ class RWMB_Key_Value_Field extends RWMB_Text_Field {
64
  * @param array $field Field parameters.
65
  * @return string
66
  */
67
- public static function element_description( $field ) {
 
 
 
 
 
 
 
 
 
 
68
  return '';
69
  }
70
 
64
  * @param array $field Field parameters.
65
  * @return string
66
  */
67
+ public static function input_description( $field ) {
68
+ return '';
69
+ }
70
+
71
+ /**
72
+ * Do not show field description.
73
+ *
74
+ * @param array $field Field parameters.
75
+ * @return string
76
+ */
77
+ public static function label_description( $field ) {
78
  return '';
79
  }
80
 
inc/fields/media.php CHANGED
@@ -84,10 +84,10 @@ class RWMB_Media_Field extends RWMB_File_Field {
84
  ) );
85
 
86
  $field['js_options'] = wp_parse_args( $field['js_options'], array(
87
- 'mimeType' => $field['mime_type'],
88
- 'maxFiles' => $field['max_file_uploads'],
89
- 'forceDelete' => $field['force_delete'] ? true : false,
90
- 'maxStatus' => $field['max_status'],
91
  ) );
92
 
93
  $field['multiple'] = true;
84
  ) );
85
 
86
  $field['js_options'] = wp_parse_args( $field['js_options'], array(
87
+ 'mimeType' => $field['mime_type'],
88
+ 'maxFiles' => $field['max_file_uploads'],
89
+ 'forceDelete' => $field['force_delete'] ? true : false,
90
+ 'maxStatus' => $field['max_status'],
91
  ) );
92
 
93
  $field['multiple'] = true;
inc/loader.php CHANGED
@@ -18,7 +18,7 @@ class RWMB_Loader {
18
  */
19
  protected function constants() {
20
  // Script version, used to add version for scripts and styles.
21
- define( 'RWMB_VER', '4.10.3' );
22
 
23
  list( $path, $url ) = self::get_path( dirname( dirname( __FILE__ ) ) );
24
 
@@ -88,8 +88,15 @@ class RWMB_Loader {
88
 
89
  $sanitize = new RWMB_Sanitizer;
90
  $sanitize->init();
 
 
 
91
  }
92
 
 
 
 
 
93
  // Public functions.
94
  require_once RWMB_INC_DIR . 'functions.php';
95
  }
18
  */
19
  protected function constants() {
20
  // Script version, used to add version for scripts and styles.
21
+ define( 'RWMB_VER', '4.10.4' );
22
 
23
  list( $path, $url ) = self::get_path( dirname( dirname( __FILE__ ) ) );
24
 
88
 
89
  $sanitize = new RWMB_Sanitizer;
90
  $sanitize->init();
91
+
92
+ $media_modal = new RWMB_Media_Modal;
93
+ $media_modal->init();
94
  }
95
 
96
+ // WPML Compatibility.
97
+ $wpml = new RWMB_WPML;
98
+ $wpml->init();
99
+
100
  // Public functions.
101
  require_once RWMB_INC_DIR . 'functions.php';
102
  }
inc/media-modal.php ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Add support for editing attachment custom fields in the media modal.
4
+ *
5
+ * @package Meta Box
6
+ */
7
+
8
+ /**
9
+ * The media modal class.
10
+ * Handling showing and saving custom fields in the media modal.
11
+ */
12
+ class RWMB_Media_Modal {
13
+ /**
14
+ * List of custom fields.
15
+ *
16
+ * @var array
17
+ */
18
+ protected $fields = array();
19
+
20
+ /**
21
+ * Initialize.
22
+ */
23
+ public function init() {
24
+ add_action( 'init', array( $this, 'get_fields' ) );
25
+ add_filter( 'attachment_fields_to_edit', array( $this, 'add_fields' ), 11, 2 );
26
+ add_filter( 'attachment_fields_to_save', array( $this, 'save_fields' ), 11, 2 );
27
+
28
+ add_filter( 'rwmb_show', array( $this, 'is_in_normal_mode' ), 10, 2 );
29
+ }
30
+
31
+ /**
32
+ * Get list of custom fields and store in the current object for future use.
33
+ */
34
+ public function get_fields() {
35
+ $meta_boxes = RWMB_Core::get_meta_boxes();
36
+ foreach ( $meta_boxes as $meta_box ) {
37
+ $meta_box = RW_Meta_Box::normalize( $meta_box );
38
+ $meta_box['fields'] = RW_Meta_Box::normalize_fields( $meta_box['fields'] );
39
+
40
+ if ( $this->is_in_modal( $meta_box ) ) {
41
+ $this->fields = array_merge( $this->fields, array_values( $meta_box['fields'] ) );
42
+ }
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Add fields to the attachment edit popup.
48
+ *
49
+ * @param array $form_fields An array of attachment form fields.
50
+ * @param WP_Post $post The WP_Post attachment object.
51
+ *
52
+ * @return mixed
53
+ */
54
+ public function add_fields( $form_fields, WP_Post $post ) {
55
+ foreach ( $this->fields as $field ) {
56
+ $form_field = $field;
57
+ $form_field['label'] = $field['name'];
58
+ $form_field['input'] = 'html';
59
+
60
+ // Just ignore the field 'std' because there's no way to check it.
61
+ $meta = RWMB_Field::call( $field, 'meta', $post->ID, true );
62
+ $form_field['value'] = $meta;
63
+
64
+ $field['field_name'] = 'attachments[' . $post->ID . '][' . $field['field_name'] . ']';
65
+ $form_field['html'] = RWMB_Field::call( $field, 'html', $meta );
66
+
67
+ $form_fields[ $field['id'] ] = $form_field;
68
+ }
69
+
70
+ return $form_fields;
71
+ }
72
+
73
+ /**
74
+ * Save custom fields.
75
+ *
76
+ * @param array $post An array of post data.
77
+ * @param array $attachment An array of attachment metadata.
78
+ *
79
+ * @return array
80
+ */
81
+ public function save_fields( $post, $attachment ) {
82
+ foreach ( $this->fields as $field ) {
83
+ $key = $field['id'];
84
+
85
+ $old = RWMB_Field::call( $field, 'raw_meta', $post['ID'] );
86
+ $new = isset( $attachment[ $key ] ) ? $attachment[ $key ] : '';
87
+
88
+ // Allow field class change the value.
89
+ if ( $field['clone'] ) {
90
+ $new = RWMB_Clone::value( $new, $old, $post['ID'], $field );
91
+ } else {
92
+ $new = RWMB_Field::call( $field, 'value', $new, $old, $post['ID'] );
93
+ $new = RWMB_Field::filter( 'sanitize', $new, $field );
94
+ }
95
+ $new = RWMB_Field::filter( 'value', $new, $field, $old );
96
+
97
+ // Call defined method to save meta value, if there's no methods, call common one.
98
+ RWMB_Field::call( $field, 'save', $new, $old, $post['ID'] );
99
+ }
100
+
101
+ return $post;
102
+ }
103
+
104
+ /**
105
+ * Whether or not show the meta box when editing custom fields in the normal mode.
106
+ *
107
+ * @param bool $show Whether to show the meta box in normal editing mode.
108
+ * @param array $meta_box Meta Box parameters.
109
+ *
110
+ * @return bool
111
+ */
112
+ public function is_in_normal_mode( $show, $meta_box ) {
113
+ $show = $show && ! $this->is_in_modal( $meta_box );
114
+
115
+ return $show;
116
+ }
117
+
118
+ /**
119
+ * Check if the meta box is for editing custom fields in the media modal.
120
+ *
121
+ * @param array $meta_box Meta Box parameters.
122
+ *
123
+ * @return bool
124
+ */
125
+ protected function is_in_modal( $meta_box ) {
126
+ return in_array( 'attachment', $meta_box['post_types'], true ) && ! empty( $meta_box['media_modal'] );
127
+ }
128
+ }
inc/wpml.php CHANGED
@@ -9,12 +9,27 @@
9
  * WPML compatibility class
10
  */
11
  class RWMB_WPML {
 
 
 
 
 
 
 
 
 
 
 
12
 
13
  /**
14
  * Register hooks.
15
  */
16
- public function __construct() {
 
 
 
17
  add_filter( 'wpml_duplicate_generic_string', array( $this, 'wpml_translate_values' ), 10, 3 );
 
18
  }
19
 
20
  /**
@@ -29,7 +44,7 @@ class RWMB_WPML {
29
  $fields = RWMB_Core::get_fields();
30
 
31
  foreach ( $fields as $field ) {
32
- if ( ! in_array( $field['type'], array( 'post', 'taxonomy_advanced' ) ) || $field['id'] !== $meta_data['key'] ) {
33
  continue;
34
  }
35
 
@@ -56,4 +71,49 @@ class RWMB_WPML {
56
 
57
  return $value;
58
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  }
9
  * WPML compatibility class
10
  */
11
  class RWMB_WPML {
12
+ /**
13
+ * Initialize.
14
+ */
15
+ public function init() {
16
+ /**
17
+ * Run before meta boxes are registered so it can modify fields.
18
+ *
19
+ * @see modify_field()
20
+ */
21
+ add_action( 'init', array( $this, 'register_hooks' ), 9 );
22
+ }
23
 
24
  /**
25
  * Register hooks.
26
  */
27
+ public function register_hooks() {
28
+ if ( ! defined( 'ICL_SITEPRESS_VERSION' ) ) {
29
+ return;
30
+ }
31
  add_filter( 'wpml_duplicate_generic_string', array( $this, 'wpml_translate_values' ), 10, 3 );
32
+ add_filter( 'rwmb_normalize_field', array( $this, 'modify_field' ) );
33
  }
34
 
35
  /**
44
  $fields = RWMB_Core::get_fields();
45
 
46
  foreach ( $fields as $field ) {
47
+ if ( ! in_array( $field['type'], array( 'post', 'taxonomy_advanced' ), true ) || $field['id'] !== $meta_data['key'] ) {
48
  continue;
49
  }
50
 
71
 
72
  return $value;
73
  }
74
+
75
+ /**
76
+ * Modified field depends on its translation status.
77
+ * If the post is a translated version of another post and the field is set to:
78
+ * - Do not translate: hide the field.
79
+ * - Copy: make it disabled so users cannot edit.
80
+ * - Translate: do nothing.
81
+ *
82
+ * @param array $field Field parameters.
83
+ *
84
+ * @return mixed
85
+ */
86
+ public function modify_field( $field ) {
87
+ global $wpml_post_translations;
88
+
89
+ if ( empty( $field['id'] ) ) {
90
+ return $field;
91
+ }
92
+
93
+ // Get post ID.
94
+ $post_id = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT );
95
+ if ( ! $post_id ) {
96
+ $post_id = filter_input( INPUT_POST, 'post_ID', FILTER_SANITIZE_NUMBER_INT );
97
+ }
98
+
99
+ // If the post is the original one: do nothing.
100
+ if ( ! $wpml_post_translations->get_source_lang_code( $post_id ) ) {
101
+ return $field;
102
+ }
103
+
104
+ // Get setting for the custom field translation.
105
+ $custom_fields_translation = apply_filters( 'wpml_sub_setting', false, 'translation-management', 'custom_fields_translation' );
106
+ if ( ! isset( $custom_fields_translation[ $field['id'] ] ) ) {
107
+ return $field;
108
+ }
109
+
110
+ $setting = intval( $custom_fields_translation[ $field['id'] ] );
111
+ if ( 0 === $setting ) { // Do not translate: hide it.
112
+ $field['class'] .= ' hidden';
113
+ } elseif ( 1 === $setting ) { // Copy: disable editing.
114
+ $field['disabled'] = true;
115
+ }
116
+
117
+ return $field;
118
+ }
119
  }
js/media.js CHANGED
@@ -42,8 +42,18 @@ jQuery( function ( $ ) {
42
  return wp.media.model.Attachments.prototype.add.call( this, models, options );
43
  },
44
 
 
 
 
 
 
 
 
 
 
 
45
  destroyAll: function() {
46
- _.each(_.clone( this.models), function( model ) {
47
  model.destroy();
48
  });
49
  }
@@ -60,7 +70,7 @@ jQuery( function ( $ ) {
60
  ids: [],
61
  mimeType: '',
62
  forceDelete: false,
63
- showStatus: true,
64
  length: 0
65
  },
66
 
@@ -98,15 +108,7 @@ jQuery( function ( $ ) {
98
  // Get more then trigger ready
99
  this.get( 'items' ).more();
100
  }
101
- },
102
-
103
- // Method to remove media items
104
- removeItem: function ( item ) {
105
- this.get( 'items' ).remove( item );
106
- if ( this.get( 'forceDelete' ) === true ) {
107
- item.destroy();
108
- }
109
- },
110
  } );
111
 
112
  /***
@@ -180,45 +182,48 @@ jQuery( function ( $ ) {
180
  tagName: 'ul',
181
  className: 'rwmb-media-list',
182
 
183
- getItemView: _.memoize(
184
- function( item ) {
185
- return new this.itemView( {
186
- model: item,
187
- controller: this.controller
188
- } );
189
- },
190
- function( item ) {
191
- return item.cid;
192
- } ),
193
-
194
  //Add item view
195
  addItemView: function ( item ) {
196
- this.$el.append( this.getItemView( item ).el );
 
 
 
 
 
 
 
 
 
 
 
197
  },
198
 
199
  //Remove item view
200
  removeItemView: function ( item ) {
201
- var itemView = this.getItemView( item );
202
- if ( itemView ) {
203
- itemView.remove();
204
- }
205
  },
206
 
207
  initialize: function ( options ) {
208
- this.controller = options.controller;
209
- this.itemView = options.itemView || MediaItem;
 
 
 
 
 
 
 
 
 
 
210
 
211
- this.setEvents();
 
212
 
213
  // Sort media using sortable
214
  this.initSortable();
215
  },
216
 
217
- setEvents: function () {
218
- this.listenTo( this.controller.get( 'items' ), 'add', this.addItemView );
219
- this.listenTo( this.controller.get( 'items' ), 'remove', this.removeItemView );
220
- },
221
-
222
  initSortable: function () {
223
  var collection = this.controller.get( 'items' );
224
  this.$el.sortable( {
@@ -253,33 +258,33 @@ jQuery( function ( $ ) {
253
  } );
254
 
255
  /***
256
- * MediaStatus
257
- * Tracks status of media field if maxStatus is greater than 0
258
  */
259
  MediaStatus = views.MediaStatus = Backbone.View.extend( {
260
  tagName: 'div',
261
  className: 'rwmb-media-status',
262
  template: wp.template( 'rwmb-media-status' ),
263
 
264
- //Initialize
265
  initialize: function ( options ) {
266
  this.controller = options.controller;
267
 
268
- //Auto hide if showStatus is false
269
- if ( ! this.controller.get( 'showStatus' ) ) {
270
  this.$el.hide();
 
271
  }
272
 
273
- //Rerender if changes happen in controller
274
  this.listenTo( this.controller.get( 'items' ), 'update', this.render );
275
 
276
- //Render
277
  this.render();
278
  },
279
 
280
  render: function () {
281
- var attrs = _.clone( this.controller.attributes );
282
- this.$el.html( this.template( attrs ) );
283
  }
284
  } );
285
 
@@ -355,9 +360,38 @@ jQuery( function ( $ ) {
355
 
356
 
357
  events: {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
358
  // Event when remove button clicked
359
  'click .rwmb-remove-media': function ( e ) {
360
- this.controller.removeItem( this.model );
361
  return false;
362
  },
363
 
42
  return wp.media.model.Attachments.prototype.add.call( this, models, options );
43
  },
44
 
45
+ remove: function( models, options ) {
46
+ var models = wp.media.model.Attachments.prototype.remove.call( this, models, options );
47
+ if ( this.controller.get( 'forceDelete' ) === true ) {
48
+ models = !_.isArray( models ) ? [models] : models;
49
+ _.each( models, function( model ) {
50
+ model.destroy();
51
+ });
52
+ }
53
+ },
54
+
55
  destroyAll: function() {
56
+ _.each( _.clone( this.models), function( model ) {
57
  model.destroy();
58
  });
59
  }
70
  ids: [],
71
  mimeType: '',
72
  forceDelete: false,
73
+ maxStatus: true,
74
  length: 0
75
  },
76
 
108
  // Get more then trigger ready
109
  this.get( 'items' ).more();
110
  }
111
+ }
 
 
 
 
 
 
 
 
112
  } );
113
 
114
  /***
182
  tagName: 'ul',
183
  className: 'rwmb-media-list',
184
 
 
 
 
 
 
 
 
 
 
 
 
185
  //Add item view
186
  addItemView: function ( item ) {
187
+ var index = this.controller.get( 'items' ).indexOf( item ),
188
+ itemEl = this.getItemView( item ).el;
189
+
190
+ if( 0 >= index ) {
191
+ this.$el.prepend( itemEl );
192
+ }
193
+ else if( this.$el.children().length <= index ) {
194
+ this.$el.append( itemEl )
195
+ }
196
+ else {
197
+ this.$el.children().eq( index - 1 ).after( itemEl );
198
+ }
199
  },
200
 
201
  //Remove item view
202
  removeItemView: function ( item ) {
203
+ this.getItemView(item).$el.detach();
 
 
 
204
  },
205
 
206
  initialize: function ( options ) {
207
+ this.controller = options.controller;
208
+ this.itemView = options.itemView || MediaItem;
209
+ this.getItemView = _.memoize(
210
+ function( item ) {
211
+ return new this.itemView( {
212
+ model: item,
213
+ controller: this.controller
214
+ } );
215
+ },
216
+ function( item ) {
217
+ return item.cid;
218
+ } );
219
 
220
+ this.listenTo( this.controller.get( 'items' ), 'add', this.addItemView );
221
+ this.listenTo( this.controller.get( 'items' ), 'remove', this.removeItemView );
222
 
223
  // Sort media using sortable
224
  this.initSortable();
225
  },
226
 
 
 
 
 
 
227
  initSortable: function () {
228
  var collection = this.controller.get( 'items' );
229
  this.$el.sortable( {
258
  } );
259
 
260
  /***
261
+ * MediaStatus view.
262
+ * Show number of selected/uploaded files and number of files remain if "maxStatus" parameter is true.
263
  */
264
  MediaStatus = views.MediaStatus = Backbone.View.extend( {
265
  tagName: 'div',
266
  className: 'rwmb-media-status',
267
  template: wp.template( 'rwmb-media-status' ),
268
 
 
269
  initialize: function ( options ) {
270
  this.controller = options.controller;
271
 
272
+ // Auto hide if maxStatus is false
273
+ if ( ! this.controller.get( 'maxStatus' ) ) {
274
  this.$el.hide();
275
+ return;
276
  }
277
 
278
+ // Re-render if changes happen in controller
279
  this.listenTo( this.controller.get( 'items' ), 'update', this.render );
280
 
281
+ // Render
282
  this.render();
283
  },
284
 
285
  render: function () {
286
+ var attributes = _.clone( this.controller.attributes );
287
+ this.$el.html( this.template( attributes ) );
288
  }
289
  } );
290
 
360
 
361
 
362
  events: {
363
+ 'click .rwmb-switch': function( e ) {
364
+ if ( this._frame ) {
365
+ //this.stopListening( this._frame );
366
+ this._frame.dispose();
367
+ }
368
+ this._frame = wp.media( {
369
+ className: 'media-frame rwmb-media-frame',
370
+ multiple: false,
371
+ title: i18nRwmbMedia.select,
372
+ editing: true,
373
+ library: {
374
+ type: this.controller.get( 'mimeType' )
375
+ }
376
+ } );
377
+
378
+ this._frame.on( 'select', function () {
379
+ var selection = this._frame.state().get( 'selection' ),
380
+ collection = this.controller.get( 'items' ),
381
+ index = collection.indexOf( this.model );
382
+ if( !_.isEmpty( selection ) ) {
383
+ collection.remove( this.model );
384
+ collection.add( selection, { at: index } );
385
+ }
386
+ }, this );
387
+
388
+ this._frame.open();
389
+ return false;
390
+ },
391
+
392
  // Event when remove button clicked
393
  'click .rwmb-remove-media': function ( e ) {
394
+ this.controller.get( 'items' ).remove( this.model );
395
  return false;
396
  },
397
 
js/validate.js CHANGED
@@ -1,19 +1,31 @@
1
  jQuery( function ( $ ) {
2
  'use strict';
3
 
4
- var $form = $( '#post' ),
5
- rules = {
6
- invalidHandler: function () {
7
- // Re-enable the submit ( publish/update ) button and hide the ajax indicator
8
- $( '#publish' ).removeClass( 'button-primary-disabled' );
9
- $( '#ajax-loading' ).attr( 'style', '' );
10
- $form.siblings( '#message' ).remove();
11
- $form.before( '<div id="message" class="error"><p>' + rwmbValidate.summaryMessage + '</p></div>' );
12
- },
13
- ignore: ':not([class|="rwmb"])'
14
- };
15
 
16
- // Gather all validation rules
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  $( '.rwmb-validation-rules' ).each( function () {
18
  var subRules = $( this ).data( 'rules' );
19
  $.extend( true, rules, subRules );
@@ -26,6 +38,6 @@ jQuery( function ( $ ) {
26
  } );
27
  } );
28
 
29
- // Execute
30
  $form.validate( rules );
31
  } );
1
  jQuery( function ( $ ) {
2
  'use strict';
3
 
4
+ var rules = {
5
+ invalidHandler: function () {
6
+ // Re-enable the submit ( publish/update ) button and hide the ajax indicator
7
+ $( '#publish' ).removeClass( 'button-primary-disabled' );
8
+ $( '#ajax-loading' ).attr( 'style', '' );
9
+ $form.siblings( '#message' ).remove();
10
+ $form.before( '<div id="message" class="error"><p>' + rwmbValidate.summaryMessage + '</p></div>' );
11
+ },
12
+ ignore: ':not([class|="rwmb"])'
13
+ };
 
14
 
15
+ // Edit post form.
16
+ var $form = $( '#post' );
17
+
18
+ // Edit user form.
19
+ if ( ! $form.length ) {
20
+ $form = $( '#your-profile' );
21
+ }
22
+
23
+ // Edit term form.
24
+ if ( ! $form.length ) {
25
+ $form = $( '#edittag' );
26
+ }
27
+
28
+ // Gather all validation rules.
29
  $( '.rwmb-validation-rules' ).each( function () {
30
  var subRules = $( this ).data( 'rules' );
31
  $.extend( true, rules, subRules );
38
  } );
39
  } );
40
 
41
+ // Execute.
42
  $form.validate( rules );
43
  } );
meta-box.php CHANGED
@@ -3,8 +3,8 @@
3
  * Plugin Name: Meta Box
4
  * Plugin URI: https://metabox.io
5
  * Description: Create custom meta boxes and custom fields in WordPress.
6
- * Version: 4.10.3
7
- * Author: Rilwis
8
  * Author URI: http://www.deluxeblogtips.com
9
  * License: GPL2+
10
  * Text Domain: meta-box
3
  * Plugin Name: Meta Box
4
  * Plugin URI: https://metabox.io
5
  * Description: Create custom meta boxes and custom fields in WordPress.
6
+ * Version: 4.10.4
7
+ * Author: Anh Tran
8
  * Author URI: http://www.deluxeblogtips.com
9
  * License: GPL2+
10
  * Text Domain: meta-box
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link: http://paypal.me/anhtnt
4
  Tags: meta-box, custom fields, custom field, meta, meta-boxes, admin, advanced, custom, edit, field, file, image, magic fields, matrix, more fields, Post, repeater, simple fields, text, textarea, type, cms, fields post
5
  Requires at least: 4.1
6
  Tested up to: 4.7.2
7
- Stable tag: 4.10.3
8
  License: GPLv2 or later
9
 
10
  Meta Box plugin is a powerful, professional developer toolkit to create custom meta boxes and custom fields for WordPress.
@@ -91,6 +91,14 @@ To getting started with the plugin API, please read [this tutorial](https://meta
91
 
92
  == Changelog ==
93
 
 
 
 
 
 
 
 
 
94
  = 4.10.3 =
95
  * Fix: `force_delete` causes files to be deleted even when set to `false`.
96
  * Fix: `max_file_uploads` not working.
4
  Tags: meta-box, custom fields, custom field, meta, meta-boxes, admin, advanced, custom, edit, field, file, image, magic fields, matrix, more fields, Post, repeater, simple fields, text, textarea, type, cms, fields post
5
  Requires at least: 4.1
6
  Tested up to: 4.7.2
7
+ Stable tag: 4.10.4
8
  License: GPLv2 or later
9
 
10
  Meta Box plugin is a powerful, professional developer toolkit to create custom meta boxes and custom fields for WordPress.
91
 
92
  == Changelog ==
93
 
94
+ = 4.10.4 =
95
+ * Improvement: Add support for validate user edit and term edit forms (requires MB Term Meta or MB User Meta extension).
96
+ * Improvement: Add new parameter `label_description` for fields to display a description below field label (name).
97
+ * Improvement: Add support for edit custom fields in the media modal. Requires `post_type` set to `attachment` and `media_modal` set to true. [See demo](https://github.com/rilwis/meta-box/blob/master/demo/media-modal.php).
98
+ * Improvement: For WPML users: Add support to hide fields in "Do not translate" mode and disable editing fields in "Copy" mode.
99
+ * Fix: Users can't select same images for 2 different `image_advanced` fields.
100
+ * Fix: `max_status` doesn't work.
101
+
102
  = 4.10.3 =
103
  * Fix: `force_delete` causes files to be deleted even when set to `false`.
104
  * Fix: `max_file_uploads` not working.