Make WordPress Core

Changeset 43510


Ignore:
Timestamp:
07/19/2018 06:48:52 PM (8 years ago)
Author:
kadamwhite
Message:

REST API: Support meta registration for specific object subtypes.

Introduce an object_subtype argument to the args array for register_meta() which can be used to limit meta registration to a single subtype (e.g. a custom post type or taxonomy, vs all posts or taxonomies).

Introduce register_post_meta() and register_term_meta() wrapper methods for register_meta to provide a convenient interface for the common case of registering meta for a specific taxonomy or post type. These methods work the way plugin developers have often expected register_meta to function, and should be used in place of direct register_meta where possible.

Props flixos90, tharsheblows, spacedmonkey.

Merges [43378] to the 4.9 branch.
Fixes #38323.

Location:
branches/4.9
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • branches/4.9/src/wp-includes/capabilities.php

    r43287 r43510  
    277277                list( $_, $object_type, $_ ) = explode( '_', $cap );
    278278                $object_id = (int) $args[0];
    279 
    280                 switch ( $object_type ) {
    281                         case 'post':
    282                                 $post = get_post( $object_id );
    283                                 if ( ! $post ) {
    284                                         break;
    285                                 }
    286 
    287                                 $sub_type = get_post_type( $post );
    288                                 break;
    289 
    290                         case 'comment':
    291                                 $comment = get_comment( $object_id );
    292                                 if ( ! $comment ) {
    293                                         break;
    294                                 }
    295 
    296                                 $sub_type = empty( $comment->comment_type ) ? 'comment' : $comment->comment_type;
    297                                 break;
    298 
    299                         case 'term':
    300                                 $term = get_term( $object_id );
    301                                 if ( ! $term instanceof WP_Term ) {
    302                                         break;
    303                                 }
    304 
    305                                 $sub_type = $term->taxonomy;
    306                                 break;
    307 
    308                         case 'user':
    309                                 $user = get_user_by( 'id', $object_id );
    310                                 if ( ! $user ) {
    311                                         break;
    312                                 }
    313 
    314                                 $sub_type = 'user';
    315                                 break;
    316                 }
    317 
    318                 if ( empty( $sub_type ) ) {
     279                $object_subtype = get_object_subtype( $object_type, $object_id );
     280
     281                if ( empty( $object_subtype ) ) {
    319282                        $caps[] = 'do_not_allow';
    320283                        break;
    … …  
    325288                $meta_key = isset( $args[1] ) ? $args[1] : false;
    326289
    327                 $has_filter = has_filter( "auth_{$object_type}_meta_{$meta_key}" ) || has_filter( "auth_{$object_type}_{$sub_type}_meta_{$meta_key}" );
    328                 if ( $meta_key && $has_filter ) {
    329 
    330                         /**
    331                          * Filters whether the user is allowed to edit meta.
    332                          *
    333                          * Use the {@see auth_post_$object_type_meta_$meta_key} filter to modify capabilities for
    334                          * specific object types. Return true to have the mapped meta caps from edit_{$object_type} apply.
    335                          *
    336                          * The dynamic portion of the hook name, `$object_type` refers to the object type being filtered.
    337                          * The dynamic portion of the hook name, `$meta_key`, refers to the meta key passed to map_meta_cap().
    338                          *
    339                          * @since 3.3.0 As 'auth_post_meta_{$meta_key}'.
    340                          * @since 4.6.0
    341                          *
    342                          * @param bool   $allowed  Whether the user can add the post meta. Default false.
    343                          * @param string $meta_key The meta key.
    344                          * @param int    $post_id  Post ID.
    345                          * @param int    $user_id  User ID.
    346                          * @param string $cap      Capability name.
    347                          * @param array  $caps     User capabilities.
    348                          */
    349                         $allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}", false, $meta_key, $object_id, $user_id, $cap, $caps );
    350 
    351                         /**
    352                          * Filters whether the user is allowed to add post meta to a post of a given type.
    353                          *
    354                          * Use the {@see auth_$object_type_$sub_type_meta_$meta_key} filter to modify capabilities for
    355                          * specific object types/subtypes. Return true to have the mapped meta caps from edit_{$object_type} apply.
    356                          *
    357                          * The dynamic portion of the hook name, `$object_type` refers to the object type being filtered.
    358                          * The dynamic portion of the hook name, `$sub_type` refers to the object subtype being filtered.
    359                          * The dynamic portion of the hook name, `$meta_key`, refers to the meta key passed to map_meta_cap().
    360                          *
    361                          * @since 4.6.0 As 'auth_post_{$post_type}_meta_{$meta_key}'.
    362                          * @since 4.7.0
    363                          *
    364                          * @param bool   $allowed  Whether the user can add the post meta. Default false.
    365                          * @param string $meta_key The meta key.
    366                          * @param int    $post_id  Post ID.
    367                          * @param int    $user_id  User ID.
    368                          * @param string $cap      Capability name.
    369                          * @param array  $caps     User capabilities.
    370                          */
    371                         $allowed = apply_filters( "auth_{$object_type}_{$sub_type}_meta_{$meta_key}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
     290                if ( $meta_key ) {
     291                        $allowed = ! is_protected_meta( $meta_key, $object_type );
     292
     293                        if ( ! empty( $object_subtype ) && has_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) {
     294
     295                                /**
     296                                 * Filters whether the user is allowed to edit a specific meta key of a specific object type and subtype.
     297                                 *
     298                                 * The dynamic portions of the hook name, `$object_type`, `$meta_key`,
     299                                 * and `$object_subtype`, refer to the metadata object type (comment, post, term or user),
     300                                 * the meta key value, and the object subtype respectively.
     301                                 *
     302                                 * @since 4.9.8
     303                                 *
     304                                 * @param bool     $allowed   Whether the user can add the object meta. Default false.
     305                                 * @param string   $meta_key  The meta key.
     306                                 * @param int      $object_id Object ID.
     307                                 * @param int      $user_id   User ID.
     308                                 * @param string   $cap       Capability name.
     309                                 * @param string[] $caps      Array of the user's capabilities.
     310                                 */
     311                                $allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
     312                        } else {
     313
     314                                /**
     315                                 * Filters whether the user is allowed to edit a specific meta key of a specific object type.
     316                                 *
     317                                 * Return true to have the mapped meta caps from `edit_{$object_type}` apply.
     318                                 *
     319                                 * The dynamic portion of the hook name, `$object_type` refers to the object type being filtered.
     320                                 * The dynamic portion of the hook name, `$meta_key`, refers to the meta key passed to map_meta_cap().
     321                                 *
     322                                 * @since 3.3.0 As `auth_post_meta_{$meta_key}`.
     323                                 * @since 4.6.0
     324                                 *
     325                                 * @param bool     $allowed   Whether the user can add the object meta. Default false.
     326                                 * @param string   $meta_key  The meta key.
     327                                 * @param int      $object_id Object ID.
     328                                 * @param int      $user_id   User ID.
     329                                 * @param string   $cap       Capability name.
     330                                 * @param string[] $caps      Array of the user's capabilities.
     331                                 */
     332                                $allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
     333                        }
     334
     335                        if ( ! empty( $object_subtype ) ) {
     336
     337                                /**
     338                                 * Filters whether the user is allowed to edit meta for specific object types/subtypes.
     339                                 *
     340                                 * Return true to have the mapped meta caps from `edit_{$object_type}` apply.
     341                                 *
     342                                 * The dynamic portion of the hook name, `$object_type` refers to the object type being filtered.
     343                                 * The dynamic portion of the hook name, `$object_subtype` refers to the object subtype being filtered.
     344                                 * The dynamic portion of the hook name, `$meta_key`, refers to the meta key passed to map_meta_cap().
     345                                 *
     346                                 * @since 4.6.0 As `auth_post_{$post_type}_meta_{$meta_key}`.
     347                                 * @since 4.7.0
     348                                 * @deprecated 4.9.8 Use `auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}`
     349                                 *
     350                                 * @param bool     $allowed   Whether the user can add the object meta. Default false.
     351                                 * @param string   $meta_key  The meta key.
     352                                 * @param int      $object_id Object ID.
     353                                 * @param int      $user_id   User ID.
     354                                 * @param string   $cap       Capability name.
     355                                 * @param string[] $caps      Array of the user's capabilities.
     356                                 */
     357                                $allowed = apply_filters_deprecated( "auth_{$object_type}_{$object_subtype}_meta_{$meta_key}", array( $allowed, $meta_key, $object_id, $user_id, $cap, $caps ), '4.9.8', "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" );
     358                        }
    372359
    373360                        if ( ! $allowed ) {
    374361                                $caps[] = $cap;
    375362                        }
    376                 } elseif ( $meta_key && is_protected_meta( $meta_key, $object_type ) ) {
    377                         $caps[] = $cap;
    378363                }
    379364                break;
  • branches/4.9/src/wp-includes/meta.php

    r42056 r43510  
    4545        }
    4646
     47        $meta_subtype = get_object_subtype( $meta_type, $object_id );
     48
    4749        $column = sanitize_key($meta_type . '_id');
    4850
    … …  
    5052        $meta_key = wp_unslash($meta_key);
    5153        $meta_value = wp_unslash($meta_value);
    52         $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
     54        $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
    5355
    5456        /**
    … …  
    158160        }
    159161
     162        $meta_subtype = get_object_subtype( $meta_type, $object_id );
     163
    160164        $column = sanitize_key($meta_type . '_id');
    161165        $id_column = 'user' == $meta_type ? 'umeta_id' : 'meta_id';
    … …  
    166170        $passed_value = $meta_value;
    167171        $meta_value = wp_unslash($meta_value);
    168         $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
     172        $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
    169173
    170174        /**
    … …  
    641645                }
    642646
     647                $meta_subtype = get_object_subtype( $meta_type, $object_id );
     648
    643649                // Sanitize the meta
    644650                $_meta_value = $meta_value;
    645                 $meta_value = sanitize_meta( $meta_key, $meta_value, $meta_type );
    646                 $meta_value = maybe_serialize( $meta_value );
     651                $meta_value  = sanitize_meta( $meta_key, $meta_value, $meta_type, $meta_subtype );
     652                $meta_value  = maybe_serialize( $meta_value );
    647653
    648654                // Format the data query arguments.
    … …  
    936942 *
    937943 * @since 3.1.3
     944 * @since 4.9.8 The `$object_subtype` parameter was added.
    938945 *
    939946 * @param string $meta_key       Meta key.
    … …  
    943950 * @return mixed Sanitized $meta_value.
    944951 */
    945 function sanitize_meta( $meta_key, $meta_value, $object_type ) {
     952function sanitize_meta( $meta_key, $meta_value, $object_type, $object_subtype = '' ) {
     953        if ( ! empty( $object_subtype ) && has_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) {
     954
     955                /**
     956                 * Filters the sanitization of a specific meta key of a specific meta type and subtype.
     957                 *
     958                 * The dynamic portions of the hook name, `$object_type`, `$meta_key`,
     959                 * and `$object_subtype`, refer to the metadata object type (comment, post, term or user),
     960                 * the meta key value, and the object subtype respectively.
     961                 *
     962                 * @since 4.9.8
     963                 *
     964                 * @param mixed  $meta_value     Meta value to sanitize.
     965                 * @param string $meta_key       Meta key.
     966                 * @param string $object_type    Object type.
     967                 * @param string $object_subtype Object subtype.
     968                 */
     969                return apply_filters( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $meta_value, $meta_key, $object_type, $object_subtype );
     970        }
     971
    946972        /**
    947973         * Filters the sanitization of a specific meta key of a specific meta type.
    … …  
    963989 * Registers a meta key.
    964990 *
     991 * It is recommended to register meta keys for a specific combination of object type and object subtype. If passing
     992 * an object subtype is omitted, the meta key will be registered for the entire object type, however it can be partly
     993 * overridden in case a more specific meta key of the same name exists for the same object type and a subtype.
     994 *
     995 * If an object type does not support any subtypes, such as users or comments, you should commonly call this function
     996 * without passing a subtype.
     997 *
    965998 * @since 3.3.0
    966999 * @since 4.6.0 {@link https://core.trac.wordpress.org/ticket/35658 Modified
    9671000 *              to support an array of data to attach to registered meta keys}. Previous arguments for
    9681001 *              `$sanitize_callback` and `$auth_callback` have been folded into this array.
     1002 * @since 4.9.8 The `$object_subtype` argument was added to the arguments array.
    9691003 *
    9701004 * @param string $object_type    Type of object this meta is registered to.
    … …  
    9731007 *     Data used to describe the meta key when registered.
    9741008 *
     1009 *     @type string $object_subtype    A subtype; e.g. if the object type is "post", the post type. If left empty,
     1010 *                                     the meta key will be registered on the entire object type. Default empty.
    9751011 *     @type string $type              The type of data associated with this meta key.
    9761012 *                                     Valid values are 'string', 'boolean', 'integer', and 'number'.
    … …  
    9951031
    9961032        $defaults = array(
     1033                'object_subtype'    => '',
    9971034                'type'              => 'string',
    9981035                'description'       => '',
    … …  
    10351072        $args = wp_parse_args( $args, $defaults );
    10361073
     1074        $object_subtype = ! empty( $args['object_subtype'] ) ? $args['object_subtype'] : '';
     1075
    10371076        // If `auth_callback` is not provided, fall back to `is_protected_meta()`.
    10381077        if ( empty( $args['auth_callback'] ) ) {
    … …  
    10461085        // Back-compat: old sanitize and auth callbacks are applied to all of an object type.
    10471086        if ( is_callable( $args['sanitize_callback'] ) ) {
    1048                 add_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
     1087                if ( ! empty( $object_subtype ) ) {
     1088                        add_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['sanitize_callback'], 10, 4 );
     1089                } else {
     1090                        add_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'], 10, 3 );
     1091                }
    10491092        }
    10501093
    10511094        if ( is_callable( $args['auth_callback'] ) ) {
    1052                 add_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 );
     1095                if ( ! empty( $object_subtype ) ) {
     1096                        add_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['auth_callback'], 10, 6 );
     1097                } else {
     1098                        add_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'], 10, 6 );
     1099                }
    10531100        }
    10541101
    10551102        // Global registry only contains meta keys registered with the array of arguments added in 4.6.0.
    10561103        if ( ! $has_old_auth_cb && ! $has_old_sanitize_cb ) {
    1057                 $wp_meta_keys[ $object_type ][ $meta_key ] = $args;
     1104                unset( $args['object_subtype'] );
     1105
     1106                $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] = $args;
    10581107
    10591108                return true;
    … …  
    10671116 *
    10681117 * @since 4.6.0
     1118 * @since 4.9.8 The `$object_subtype` parameter was added.
    10691119 *
    10701120 * @param string $object_type    The type of object.
    10711121 * @param string $meta_key       The meta key.
    1072  *
    1073  * @return bool True if the meta key is registered to the object type. False if not.
    1074  */
    1075 function registered_meta_key_exists( $object_type, $meta_key ) {
     1122 * @param string $object_subtype Optional. The subtype of the object type.
     1123 *
     1124 * @return bool True if the meta key is registered to the object type and, if provided,
     1125 *              the object subtype. False if not.
     1126 */
     1127function registered_meta_key_exists( $object_type, $meta_key, $object_subtype = '' ) {
     1128        $meta_keys = get_registered_meta_keys( $object_type, $object_subtype );
     1129
     1130        return isset( $meta_keys[ $meta_key ] );
     1131}
     1132
     1133/**
     1134 * Unregisters a meta key from the list of registered keys.
     1135 *
     1136 * @since 4.6.0
     1137 * @since 4.9.8 The `$object_subtype` parameter was added.
     1138 *
     1139 * @param string $object_type    The type of object.
     1140 * @param string $meta_key       The meta key.
     1141 * @param string $object_subtype Optional. The subtype of the object type.
     1142 * @return bool True if successful. False if the meta key was not registered.
     1143 */
     1144function unregister_meta_key( $object_type, $meta_key, $object_subtype = '' ) {
    10761145        global $wp_meta_keys;
    10771146
    1078         if ( ! is_array( $wp_meta_keys ) ) {
    1079                 return false;
    1080         }
    1081 
    1082         if ( ! isset( $wp_meta_keys[ $object_type ] ) ) {
    1083                 return false;
    1084         }
    1085 
    1086         if ( isset( $wp_meta_keys[ $object_type ][ $meta_key ] ) ) {
    1087                 return true;
    1088         }
    1089 
    1090         return false;
    1091 }
    1092 
    1093 /**
    1094  * Unregisters a meta key from the list of registered keys.
    1095  *
    1096  * @since 4.6.0
    1097  *
    1098  * @param string $object_type The type of object.
    1099  * @param string $meta_key    The meta key.
    1100  * @return bool True if successful. False if the meta key was not registered.
    1101  */
    1102 function unregister_meta_key( $object_type, $meta_key ) {
    1103         global $wp_meta_keys;
    1104 
    1105         if ( ! registered_meta_key_exists( $object_type, $meta_key ) ) {
    1106                 return false;
    1107         }
    1108 
    1109         $args = $wp_meta_keys[ $object_type ][ $meta_key ];
     1147        if ( ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
     1148                return false;
     1149        }
     1150
     1151        $args = $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ];
    11101152
    11111153        if ( isset( $args['sanitize_callback'] ) && is_callable( $args['sanitize_callback'] ) ) {
    1112                 remove_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] );
     1154                if ( ! empty( $object_subtype ) ) {
     1155                        remove_filter( "sanitize_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['sanitize_callback'] );
     1156                } else {
     1157                        remove_filter( "sanitize_{$object_type}_meta_{$meta_key}", $args['sanitize_callback'] );
     1158                }
    11131159        }
    11141160
    11151161        if ( isset( $args['auth_callback'] ) && is_callable( $args['auth_callback'] ) ) {
    1116                 remove_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] );
    1117         }
    1118 
    1119         unset( $wp_meta_keys[ $object_type ][ $meta_key ] );
     1162                if ( ! empty( $object_subtype ) ) {
     1163                        remove_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $args['auth_callback'] );
     1164                } else {
     1165                        remove_filter( "auth_{$object_type}_meta_{$meta_key}", $args['auth_callback'] );
     1166                }
     1167        }
     1168
     1169        unset( $wp_meta_keys[ $object_type ][ $object_subtype ][ $meta_key ] );
    11201170
    11211171        // Do some clean up
     1172        if ( empty( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
     1173                unset( $wp_meta_keys[ $object_type ][ $object_subtype ] );
     1174        }
    11221175        if ( empty( $wp_meta_keys[ $object_type ] ) ) {
    11231176                unset( $wp_meta_keys[ $object_type ] );
    … …  
    11311184 *
    11321185 * @since 4.6.0
    1133  *
    1134  * @param string $object_type The type of object. Post, comment, user, term.
     1186 * @since 4.9.8 The `$object_subtype` parameter was added.
     1187 *
     1188 * @param string $object_type    The type of object. Post, comment, user, term.
     1189 * @param string $object_subtype Optional. The subtype of the object type.
    11351190 * @return array List of registered meta keys.
    11361191 */
    1137 function get_registered_meta_keys( $object_type ) {
     1192function get_registered_meta_keys( $object_type, $object_subtype = '' ) {
    11381193        global $wp_meta_keys;
    11391194
    1140         if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $object_type ] ) ) {
     1195        if ( ! is_array( $wp_meta_keys ) || ! isset( $wp_meta_keys[ $object_type ] ) || ! isset( $wp_meta_keys[ $object_type ][ $object_subtype ] ) ) {
    11411196                return array();
    11421197        }
    11431198
    1144         return $wp_meta_keys[ $object_type ];
     1199        return $wp_meta_keys[ $object_type ][ $object_subtype ];
    11451200}
    11461201
    11471202/**
    11481203 * Retrieves registered metadata for a specified object.
     1204 *
     1205 * The results include both meta that is registered specifically for the
     1206 * object's subtype and meta that is registered for the entire object type.
    11491207 *
    11501208 * @since 4.6.0
    … …  
    11551213 *                            metadata for the specified object.
    11561214 * @return mixed A single value or array of values for a key if specified. An array of all registered keys
    1157  *               and values for an object ID if not.
     1215 *               and values for an object ID if not. False if a given $meta_key is not registered.
    11581216 */
    11591217function get_registered_metadata( $object_type, $object_id, $meta_key = '' ) {
     1218        $object_subtype = get_object_subtype( $object_type, $object_id );
     1219
    11601220        if ( ! empty( $meta_key ) ) {
    1161                 if ( ! registered_meta_key_exists( $object_type, $meta_key ) ) {
     1221                if ( ! empty( $object_subtype ) && ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
     1222                        $object_subtype = '';
     1223                }
     1224
     1225                if ( ! registered_meta_key_exists( $object_type, $meta_key, $object_subtype ) ) {
    11621226                        return false;
    11631227                }
    1164                 $meta_keys = get_registered_meta_keys( $object_type );
     1228
     1229                $meta_keys     = get_registered_meta_keys( $object_type, $object_subtype );
    11651230                $meta_key_data = $meta_keys[ $meta_key ];
    11661231
    … …  
    11711236
    11721237        $data = get_metadata( $object_type, $object_id );
     1238        if ( ! $data ) {
     1239                return array();
     1240        }
    11731241
    11741242        $meta_keys = get_registered_meta_keys( $object_type );
    1175         $registered_data = array();
    1176 
    1177         // Someday, array_filter()
    1178         foreach ( $meta_keys as $k => $v ) {
    1179                 if ( isset( $data[ $k ] ) ) {
    1180                         $registered_data[ $k ] = $data[ $k ];
    1181                 }
    1182         }
    1183 
    1184         return $registered_data;
     1243        if ( ! empty( $object_subtype ) ) {
     1244                $meta_keys = array_merge( $meta_keys, get_registered_meta_keys( $object_type, $object_subtype ) );
     1245        }
     1246
     1247        return array_intersect_key( $data, $meta_keys );
    11851248}
    11861249
    … …  
    11911254 *
    11921255 * @access private
    1193  * @since  4.6.0
    1194  *
    1195  * @param  array $args         Arguments from `register_meta()`.
    1196  * @param  array $default_args Default arguments for `register_meta()`.
     1256 * @since 4.6.0
     1257 *
     1258 * @param array $args         Arguments from `register_meta()`.
     1259 * @param array $default_args Default arguments for `register_meta()`.
    11971260 *
    11981261 * @return array Filtered arguments.
    11991262 */
    12001263function _wp_register_meta_args_whitelist( $args, $default_args ) {
    1201         $whitelist = array_keys( $default_args );
    1202 
    1203         // In an anonymous function world, this would be better as an array_filter()
    1204         foreach ( $args as $key => $value ) {
    1205                 if ( ! in_array( $key, $whitelist ) ) {
    1206                         unset( $args[ $key ] );
    1207                 }
    1208         }
    1209 
    1210         return $args;
    1211 }
     1264        return array_intersect_key( $args, $default_args );
     1265}
     1266
     1267/**
     1268 * Returns the object subtype for a given object ID of a specific type.
     1269 *
     1270 * @since 4.9.8
     1271 *
     1272 * @param string $object_type Type of object to request metadata for. (e.g. comment, post, term, user)
     1273 * @param int    $object_id   ID of the object to retrieve its subtype.
     1274 * @return string The object subtype or an empty string if unspecified subtype.
     1275 */
     1276function get_object_subtype( $object_type, $object_id ) {
     1277        $object_id      = (int) $object_id;
     1278        $object_subtype = '';
     1279
     1280        switch ( $object_type ) {
     1281                case 'post':
     1282                        $post_type = get_post_type( $object_id );
     1283
     1284                        if ( ! empty( $post_type ) ) {
     1285                                $object_subtype = $post_type;
     1286                        }
     1287                        break;
     1288
     1289                case 'term':
     1290                        $term = get_term( $object_id );
     1291                        if ( ! $term instanceof WP_Term ) {
     1292                                break;
     1293                        }
     1294
     1295                        $object_subtype = $term->taxonomy;
     1296                        break;
     1297
     1298                case 'comment':
     1299                        $comment = get_comment( $object_id );
     1300                        if ( ! $comment ) {
     1301                                break;
     1302                        }
     1303
     1304                        $object_subtype = 'comment';
     1305                        break;
     1306
     1307                case 'user':
     1308                        $user = get_user_by( 'id', $object_id );
     1309                        if ( ! $user ) {
     1310                                break;
     1311                        }
     1312
     1313                        $object_subtype = 'user';
     1314                        break;
     1315        }
     1316
     1317        /**
     1318         * Filters the object subtype identifier for a non standard object type.
     1319         *
     1320         * The dynamic portion of the hook, `$object_type`, refers to the object
     1321         * type (post, comment, term, or user).
     1322         *
     1323         * @since 4.9.8
     1324         *
     1325         * @param string $object_subtype Empty string to override.
     1326         * @param int    $object_id      ID of the object to get the subtype for.
     1327         */
     1328        return apply_filters( "get_object_subtype_{$object_type}", $object_subtype, $object_id );
     1329}
  • branches/4.9/src/wp-includes/post.php

    r43393 r43510  
    18851885
    18861886/**
     1887 * Registers a meta key for posts.
     1888 *
     1889 * @since 4.9.8
     1890 *
     1891 * @param string $post_type Post type to register a meta key for. Pass an empty string
     1892 *                          to register the meta key across all existing post types.
     1893 * @param string $meta_key  The meta key to register.
     1894 * @param array  $args      Data used to describe the meta key when registered. See
     1895 *                          {@see register_meta()} for a list of supported arguments.
     1896 * @return bool True if the meta key was successfully registered, false if not.
     1897 */
     1898function register_post_meta( $post_type, $meta_key, array $args ) {
     1899        $args['object_subtype'] = $post_type;
     1900
     1901        return register_meta( 'post', $meta_key, $args );
     1902}
     1903
     1904/**
     1905 * Unregisters a meta key for posts.
     1906 *
     1907 * @since 4.9.8
     1908 *
     1909 * @param string $post_type Post type the meta key is currently registered for. Pass
     1910 *                          an empty string if the meta key is registered across all
     1911 *                          existing post types.
     1912 * @param string $meta_key  The meta key to unregister.
     1913 * @return bool True on success, false if the meta key was not previously registered.
     1914 */
     1915function unregister_post_meta( $post_type, $meta_key ) {
     1916        return unregister_meta_key( 'post', $meta_key, $post_type );
     1917}
     1918
     1919/**
    18871920 * Retrieve post meta fields, based on post ID.
    18881921 *
  • branches/4.9/src/wp-includes/rest-api/fields/class-wp-rest-comment-meta-fields.php

    r41162 r43510  
    2929
    3030        /**
     31         * Retrieves the object meta subtype.
     32         *
     33         * @since 4.9.8
     34         *
     35         * @return string 'comment' There are no subtypes.
     36         */
     37        protected function get_meta_subtype() {
     38                return 'comment';
     39        }
     40
     41        /**
    3142         * Retrieves the type for register_rest_field() in the context of comments.
    3243         *
  • branches/4.9/src/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php

    r41162 r43510  
    2424         */
    2525        abstract protected function get_meta_type();
     26
     27        /**
     28         * Retrieves the object meta subtype.
     29         *
     30         * @since 4.9.8
     31         *
     32         * @return string Subtype for the meta type, or empty string if no specific subtype.
     33         */
     34        protected function get_meta_subtype() {
     35                return '';
     36        }
    2637
    2738        /**
    … …  
    318329                $registered = array();
    319330
    320                 foreach ( get_registered_meta_keys( $this->get_meta_type() ) as $name => $args ) {
     331                $meta_type    = $this->get_meta_type();
     332                $meta_subtype = $this->get_meta_subtype();
     333
     334                $meta_keys = get_registered_meta_keys( $meta_type );
     335                if ( ! empty( $meta_subtype ) ) {
     336                        $meta_keys = array_merge( $meta_keys, get_registered_meta_keys( $meta_type, $meta_subtype ) );
     337                }
     338
     339                foreach ( $meta_keys as $name => $args ) {
    321340                        if ( empty( $args['show_in_rest'] ) ) {
    322341                                continue;
  • branches/4.9/src/wp-includes/rest-api/fields/class-wp-rest-post-meta-fields.php

    r41162 r43510  
    4848
    4949        /**
     50         * Retrieves the object meta subtype.
     51         *
     52         * @since 4.9.8
     53         *
     54         * @return string Subtype for the meta type, or empty string if no specific subtype.
     55         */
     56        protected function get_meta_subtype() {
     57                return $this->post_type;
     58        }
     59
     60        /**
    5061         * Retrieves the type for register_rest_field().
    5162         *
  • branches/4.9/src/wp-includes/rest-api/fields/class-wp-rest-term-meta-fields.php

    r41162 r43510  
    4848
    4949        /**
     50         * Retrieves the object meta subtype.
     51         *
     52         * @since 4.9.8
     53         *
     54         * @return string Subtype for the meta type, or empty string if no specific subtype.
     55         */
     56        protected function get_meta_subtype() {
     57                return $this->taxonomy;
     58        }
     59
     60        /**
    5061         * Retrieves the type for register_rest_field().
    5162         *
  • branches/4.9/src/wp-includes/rest-api/fields/class-wp-rest-user-meta-fields.php

    r41162 r43510  
    2929
    3030        /**
     31         * Retrieves the object meta subtype.
     32         *
     33         * @since 4.9.8
     34         *
     35         * @return string 'user' There are no subtypes.
     36         */
     37        protected function get_meta_subtype() {
     38                return 'user';
     39        }
     40
     41        /**
    3142         * Retrieves the type for register_rest_field().
    3243         *
  • branches/4.9/src/wp-includes/taxonomy.php

    r41987 r43510  
    12791279
    12801280/**
     1281 * Registers a meta key for terms.
     1282 *
     1283 * @since 4.9.8
     1284 *
     1285 * @param string $taxonomy Taxonomy to register a meta key for. Pass an empty string
     1286 *                         to register the meta key across all existing taxonomies.
     1287 * @param string $meta_key The meta key to register.
     1288 * @param array  $args     Data used to describe the meta key when registered. See
     1289 *                         {@see register_meta()} for a list of supported arguments.
     1290 * @return bool True if the meta key was successfully registered, false if not.
     1291 */
     1292function register_term_meta( $taxonomy, $meta_key, array $args ) {
     1293        $args['object_subtype'] = $taxonomy;
     1294
     1295        return register_meta( 'term', $meta_key, $args );
     1296}
     1297
     1298/**
     1299 * Unregisters a meta key for terms.
     1300 *
     1301 * @since 4.9.8
     1302 *
     1303 * @param string $taxonomy Taxonomy the meta key is currently registered for. Pass
     1304 *                         an empty string if the meta key is registered across all
     1305 *                         existing taxonomies.
     1306 * @param string $meta_key The meta key to unregister.
     1307 * @return bool True on success, false if the meta key was not previously registered.
     1308 */
     1309function unregister_term_meta( $taxonomy, $meta_key ) {
     1310        return unregister_meta_key( 'term', $meta_key, $taxonomy );
     1311}
     1312
     1313/**
    12811314 * Check if Term exists.
    12821315 *
  • branches/4.9/tests/phpunit/tests/meta/registerMeta.php

    r38398 r43510  
    44 */
    55class Tests_Meta_Register_Meta extends WP_UnitTestCase {
     6
    67        protected static $post_id;
     8        protected static $term_id;
     9        protected static $comment_id;
     10        protected static $user_id;
    711
    812        public static function wpSetUpBeforeClass( $factory ) {
    9                 self::$post_id = $factory->post->create();
     13                self::$post_id    = $factory->post->create( array( 'post_type' => 'page' ) );
     14                self::$term_id    = $factory->term->create( array( 'taxonomy' => 'category' ) );
     15                self::$comment_id = $factory->comment->create();
     16                self::$user_id    = $factory->user->create();
     17        }
     18
     19        public static function wpTearDownAfterClass() {
     20                wp_delete_post( self::$post_id, true );
     21                wp_delete_term( self::$term_id, 'category' );
     22                wp_delete_comment( self::$comment_id, true );
     23                self::delete_user( self::$user_id );
    1024        }
    1125
    … …  
    7084                $expected = array(
    7185                        'post' => array(
    72                                 'flight_number' => array(
    73                                         'type' => 'string',
    74                                         'description' => '',
    75                                         'single' => false,
    76                                         'sanitize_callback' => null,
    77                                         'auth_callback' => '__return_true',
    78                                         'show_in_rest' => false,
     86                                '' => array(
     87                                        'flight_number' => array(
     88                                                'type'              => 'string',
     89                                                'description'       => '',
     90                                                'single'            => false,
     91                                                'sanitize_callback' => null,
     92                                                'auth_callback'     => '__return_true',
     93                                                'show_in_rest'      => false,
     94                                        ),
    7995                                ),
    8096                        ),
    8197                );
    8298
    83                 $this->assertEquals( $actual, $expected );
     99                $this->assertEquals( $expected, $actual );
    84100        }
    85101
    … …  
    92108                $expected = array(
    93109                        'term' => array(
    94                                 'category_icon' => array(
    95                                         'type' => 'string',
    96                                         'description' => '',
    97                                         'single' => false,
    98                                         'sanitize_callback' => null,
    99                                         'auth_callback' => '__return_true',
    100                                         'show_in_rest' => false,
     110                                '' => array(
     111                                        'category_icon' => array(
     112                                                'type'              => 'string',
     113                                                'description'       => '',
     114                                                'single'            => false,
     115                                                'sanitize_callback' => null,
     116                                                'auth_callback'     => '__return_true',
     117                                                'show_in_rest'      => false,
     118                                        ),
    101119                                ),
    102120                        ),
    103121                );
    104122
    105                 $this->assertEquals( $actual, $expected );
     123                $this->assertEquals( $expected, $actual );
    106124        }
    107125
    … …  
    144162                $expected = array(
    145163                        'post' => array(
    146                                 'flight_number' => array(
    147                                         'type' => 'string',
    148                                         'description' => '',
    149                                         'single' => false,
    150                                         'sanitize_callback' => array( $this, '_new_sanitize_meta_cb' ),
    151                                         'auth_callback' => '__return_true',
    152                                         'show_in_rest' => false,
     164                                '' => array(
     165                                        'flight_number' => array(
     166                                                'type'              => 'string',
     167                                                'description'       => '',
     168                                                'single'            => false,
     169                                                'sanitize_callback' => array( $this, '_new_sanitize_meta_cb' ),
     170                                                'auth_callback'     => '__return_true',
     171                                                'show_in_rest'      => false,
     172                                        ),
    153173                                ),
    154174                        ),
    … …  
    297317                $this->assertEmpty( $meta );
    298318        }
     319
     320        /**
     321         * @ticket 38323
     322         * @dataProvider data_get_types_and_subtypes
     323         */
     324        public function test_register_meta_with_subtype_populates_wp_meta_keys( $type, $subtype ) {
     325                global $wp_meta_keys;
     326
     327                register_meta( $type, 'flight_number', array( 'object_subtype' => $subtype ) );
     328
     329                $expected = array(
     330                        $type => array(
     331                                $subtype => array(
     332                                        'flight_number' => array(
     333                                                'type'              => 'string',
     334                                                'description'       => '',
     335                                                'single'            => false,
     336                                                'sanitize_callback' => null,
     337                                                'auth_callback'     => '__return_true',
     338                                                'show_in_rest'      => false,
     339                                        ),
     340                                ),
     341                        ),
     342                );
     343
     344                $actual = $wp_meta_keys;
     345
     346                // Reset global so subsequent data tests do not get polluted.
     347                $wp_meta_keys = array();
     348
     349                $this->assertEquals( $expected, $actual );
     350        }
     351
     352        /**
     353         * @ticket 38323
     354         * @dataProvider data_get_types_and_subtypes
     355         */
     356        public function test_unregister_meta_with_subtype_unpopulates_wp_meta_keys( $type, $subtype ) {
     357                global $wp_meta_keys;
     358
     359                register_meta( $type, 'flight_number', array( 'object_subtype' => $subtype ) );
     360                unregister_meta_key( $type, 'flight_number', $subtype );
     361
     362                $actual = $wp_meta_keys;
     363
     364                // Reset global so subsequent data tests do not get polluted.
     365                $wp_meta_keys = array();
     366
     367                $this->assertEmpty( $actual );
     368        }
     369
     370        /**
     371         * @ticket 38323
     372         * @dataProvider data_get_types_and_subtypes
     373         */
     374        public function test_unregister_meta_without_subtype_keeps_subtype_meta_key( $type, $subtype ) {
     375                global $wp_meta_keys;
     376
     377                register_meta( $type, 'flight_number', array( 'object_subtype' => $subtype ) );
     378
     379                // Unregister meta key without subtype.
     380                unregister_meta_key( $type, 'flight_number' );
     381
     382                $expected = array(
     383                        $type => array(
     384                                $subtype => array(
     385                                        'flight_number' => array(
     386                                                'type'              => 'string',
     387                                                'description'       => '',
     388                                                'single'            => false,
     389                                                'sanitize_callback' => null,
     390                                                'auth_callback'     => '__return_true',
     391                                                'show_in_rest'      => false,
     392                                        ),
     393                                ),
     394                        ),
     395                );
     396
     397                $actual = $wp_meta_keys;
     398
     399                // Reset global so subsequent data tests do not get polluted.
     400                $wp_meta_keys = array();
     401
     402                $this->assertEquals( $expected, $actual );
     403        }
     404
     405        /**
     406         * @ticket 38323
     407         * @dataProvider data_get_types_and_subtypes
     408         */
     409        public function test_get_registered_meta_keys_with_subtype( $type, $subtype ) {
     410                register_meta( $type, 'registered_key1', array( 'object_subtype' => $subtype ) );
     411                register_meta( $type, 'registered_key2', array( 'object_subtype' => $subtype ) );
     412
     413                $meta_keys = get_registered_meta_keys( $type, $subtype );
     414
     415                $this->assertArrayHasKey( 'registered_key1', $meta_keys );
     416                $this->assertArrayHasKey( 'registered_key2', $meta_keys );
     417                $this->assertEmpty( get_registered_meta_keys( $type ) );
     418        }
     419
     420        /**
     421         * @ticket 38323
     422         * @dataProvider data_get_types_and_subtypes
     423         */
     424        public function test_get_registered_metadata_with_subtype( $type, $subtype ) {
     425                register_meta( $type, 'registered_key1', array() );
     426
     427                // This will override the above registration for objects of $subtype.
     428                register_meta( $type, 'registered_key1', array(
     429                        'object_subtype' => $subtype,
     430                        'single'         => true,
     431                ) );
     432
     433                // For testing with $single => false.
     434                register_meta( $type, 'registered_key2', array(
     435                        'object_subtype' => $subtype,
     436                ) );
     437
     438                // Register another meta key for a different subtype.
     439                register_meta( $type, 'registered_key3', array(
     440                        'object_subtype' => 'other_subtype',
     441                ) );
     442
     443                $object_property_name = $type . '_id';
     444                $object_id = self::$$object_property_name;
     445
     446                add_metadata( $type, $object_id, 'registered_key1', 'value1' );
     447                add_metadata( $type, $object_id, 'registered_key2', 'value2' );
     448                add_metadata( $type, $object_id, 'registered_key3', 'value3' );
     449
     450                $meta = get_registered_metadata( $type, $object_id );
     451
     452                $key1 = get_registered_metadata( $type, $object_id, 'registered_key1' );
     453                $key2 = get_registered_metadata( $type, $object_id, 'registered_key2' );
     454                $key3 = get_registered_metadata( $type, $object_id, 'registered_key3' );
     455
     456                $this->assertSame( array( 'registered_key1', 'registered_key2' ), array_keys( $meta ) );
     457                $this->assertSame( 'value1', $meta['registered_key1'][0] );
     458                $this->assertSame( 'value2', $meta['registered_key2'][0] );
     459
     460                $this->assertSame( 'value1', $key1 );
     461                $this->assertSame( array( 'value2' ), $key2 );
     462                $this->assertFalse( $key3 );
     463        }
     464
     465        /**
     466         * @ticket 38323
     467         * @dataProvider data_get_types_and_subtypes
     468         */
     469        public function test_get_object_subtype( $type, $expected_subtype ) {
     470                $object_property_name = $type . '_id';
     471                $object_id = self::$$object_property_name;
     472
     473                $this->assertSame( $expected_subtype, get_object_subtype( $type, $object_id ) );
     474        }
     475
     476        /**
     477         * @ticket 38323
     478         */
     479        public function test_get_object_subtype_custom() {
     480                add_filter( 'get_object_subtype_customtype', array( $this, 'filter_get_object_subtype_for_customtype' ), 10, 2 );
     481
     482                $subtype_for_3 = get_object_subtype( 'customtype', 3 );
     483                $subtype_for_4 = get_object_subtype( 'customtype', 4 );
     484
     485                $this->assertSame( 'odd', $subtype_for_3 );
     486                $this->assertSame( 'even', $subtype_for_4 );
     487        }
     488
     489        public function filter_get_object_subtype_for_customtype( $subtype, $object_id ) {
     490                if ( $object_id % 2 === 1 ) {
     491                        return 'odd';
     492                }
     493
     494                return 'even';
     495        }
     496
     497        public function data_get_types_and_subtypes() {
     498                return array(
     499                        array( 'post', 'page' ),
     500                        array( 'term', 'category' ),
     501                        array( 'comment', 'comment' ),
     502                        array( 'user', 'user' ),
     503                );
     504        }
    299505}
  • branches/4.9/tests/phpunit/tests/post/meta.php

    r35244 r43510  
    66 */
    77class Tests_Post_Meta extends WP_UnitTestCase {
     8
     9        private $last_register_meta_call = array(
     10                'object_type' => '',
     11                'meta_key'    => '',
     12                'args'        => array(),
     13        );
     14
    815        function setUp() {
    916                parent::setUp();
    … …  
    238245
    239246        }
     247
     248        /**
     249         * @ticket 38323
     250         * @dataProvider data_register_post_meta
     251         */
     252        public function test_register_post_meta( $post_type, $meta_key, $args ) {
     253                add_filter( 'register_meta_args', array( $this, 'filter_register_meta_args_set_last_register_meta_call' ), 10, 4 );
     254
     255                register_post_meta( $post_type, $meta_key, $args );
     256
     257                $args['object_subtype'] = $post_type;
     258
     259                // Reset global so subsequent data tests do not get polluted.
     260                $GLOBALS['wp_meta_keys'] = array();
     261
     262                $this->assertEquals( 'post', $this->last_register_meta_call['object_type'] );
     263                $this->assertEquals( $meta_key, $this->last_register_meta_call['meta_key'] );
     264                $this->assertEquals( $args, $this->last_register_meta_call['args'] );
     265        }
     266
     267        public function data_register_post_meta() {
     268                return array(
     269                        array( 'post', 'registered_key1', array( 'single' => true ) ),
     270                        array( 'page', 'registered_key2', array() ),
     271                        array( '', 'registered_key3', array( 'sanitize_callback' => 'absint' ) ),
     272                );
     273        }
     274
     275        public function filter_register_meta_args_set_last_register_meta_call( $args, $defaults, $object_type, $meta_key ) {
     276                $this->last_register_meta_call['object_type'] = $object_type;
     277                $this->last_register_meta_call['meta_key']    = $meta_key;
     278                $this->last_register_meta_call['args']        = $args;
     279
     280                return $args;
     281        }
     282
     283        /**
     284         * @ticket 38323
     285         * @dataProvider data_unregister_post_meta
     286         */
     287        public function test_unregister_post_meta( $post_type, $meta_key ) {
     288                global $wp_meta_keys;
     289
     290                register_post_meta( $post_type, $meta_key, array() );
     291                unregister_post_meta( $post_type, $meta_key );
     292
     293                $actual = $wp_meta_keys;
     294
     295                // Reset global so subsequent data tests do not get polluted.
     296                $wp_meta_keys = array();
     297
     298                $this->assertEmpty( $actual );
     299        }
     300
     301        public function data_unregister_post_meta() {
     302                return array(
     303                        array( 'post', 'registered_key1' ),
     304                        array( 'page', 'registered_key2' ),
     305                        array( '', 'registered_key3' ),
     306                );
     307        }
    240308}
  • branches/4.9/tests/phpunit/tests/rest-api/rest-post-meta-fields.php

    r41176 r43510  
    1313        protected static $wp_meta_keys_saved;
    1414        protected static $post_id;
     15        protected static $cpt_post_id;
    1516
    1617        public static function wpSetUpBeforeClass( $factory ) {
     18                register_post_type( 'cpt', array(
     19                        'show_in_rest' => true,
     20                        'supports'     => array( 'custom-fields' ),
     21                ) );
     22
    1723                self::$wp_meta_keys_saved = $GLOBALS['wp_meta_keys'];
    1824                self::$post_id = $factory->post->create();
     25                self::$cpt_post_id        = $factory->post->create( array( 'post_type' => 'cpt' ) );
    1926        }
    2027
    … …  
    2229                $GLOBALS['wp_meta_keys'] = self::$wp_meta_keys_saved;
    2330                wp_delete_post( self::$post_id, true );
     31                wp_delete_post( self::$cpt_post_id, true );
     32
     33                unregister_post_type( 'cpt' );
    2434        }
    2535
    … …  
    99109                ));
    100110
     111                register_post_type( 'cpt', array(
     112                        'show_in_rest' => true,
     113                        'supports'     => array( 'custom-fields' ),
     114                ) );
     115
     116                register_post_meta( 'cpt', 'test_cpt_single', array(
     117                        'show_in_rest'   => true,
     118                        'single'         => true,
     119                ) );
     120
     121                register_post_meta( 'cpt', 'test_cpt_multi', array(
     122                        'show_in_rest'   => true,
     123                        'single'         => false,
     124                ) );
     125
     126                // Register 'test_single' on subtype to override for bad auth.
     127                register_post_meta( 'cpt', 'test_single', array(
     128                        'show_in_rest'   => true,
     129                        'single'         => true,
     130                        'auth_callback'  => '__return_false',
     131                ) );
     132
    101133                /** @var WP_REST_Server $wp_rest_server */
    102134                global $wp_rest_server;
    … …  
    10111043
    10121044        /**
     1045         * @ticket 38323
     1046         * @dataProvider data_get_subtype_meta_value
     1047         */
     1048        public function test_get_subtype_meta_value( $post_type, $meta_key, $single, $in_post_type ) {
     1049                $post_id  = self::$post_id;
     1050                $endpoint = 'posts';
     1051                if ( 'cpt' === $post_type ) {
     1052                        $post_id  = self::$cpt_post_id;
     1053                        $endpoint = 'cpt';
     1054                }
     1055
     1056                $meta_value = 'testvalue';
     1057
     1058                add_post_meta( $post_id, $meta_key, $meta_value );
     1059
     1060                $request = new WP_REST_Request( 'GET', sprintf( '/wp/v2/%s/%d', $endpoint, $post_id ) );
     1061                $response = rest_get_server()->dispatch( $request );
     1062
     1063                $this->assertEquals( 200, $response->get_status() );
     1064
     1065                $data = $response->get_data();
     1066
     1067                $this->assertArrayHasKey( 'meta', $data );
     1068                $this->assertInternalType( 'array', $data['meta'] );
     1069
     1070                if ( $in_post_type ) {
     1071                        $expected_value = $meta_value;
     1072                        if ( ! $single ) {
     1073                                $expected_value = array( $expected_value );
     1074                        }
     1075
     1076                        $this->assertArrayHasKey( $meta_key, $data['meta'] );
     1077                        $this->assertEquals( $expected_value, $data['meta'][ $meta_key ] );
     1078                } else {
     1079                        $this->assertArrayNotHasKey( $meta_key, $data['meta'] );
     1080                }
     1081        }
     1082
     1083        public function data_get_subtype_meta_value() {
     1084                return array(
     1085                        array( 'cpt', 'test_cpt_single', true, true ),
     1086                        array( 'cpt', 'test_cpt_multi', false, true ),
     1087                        array( 'cpt', 'test_single', true, true ),
     1088                        array( 'cpt', 'test_multi', false, true ),
     1089                        array( 'post', 'test_cpt_single', true, false ),
     1090                        array( 'post', 'test_cpt_multi', false, false ),
     1091                        array( 'post', 'test_single', true, true ),
     1092                        array( 'post', 'test_multi', false, true ),
     1093                );
     1094        }
     1095
     1096        /**
     1097         * @ticket 38323
     1098         * @dataProvider data_set_subtype_meta_value
     1099         */
     1100        public function test_set_subtype_meta_value( $post_type, $meta_key, $single, $in_post_type, $can_write ) {
     1101                $post_id  = self::$post_id;
     1102                $endpoint = 'posts';
     1103                if ( 'cpt' === $post_type ) {
     1104                        $post_id  = self::$cpt_post_id;
     1105                        $endpoint = 'cpt';
     1106                }
     1107
     1108                $meta_value = 'value_to_set';
     1109
     1110                $this->grant_write_permission();
     1111
     1112                $request = new WP_REST_Request( 'POST', sprintf( '/wp/v2/%s/%d', $endpoint, $post_id ) );
     1113                $request->set_body_params( array(
     1114                        'meta' => array(
     1115                                $meta_key => $meta_value,
     1116                        ),
     1117                ) );
     1118
     1119                $response = rest_get_server()->dispatch( $request );
     1120                if ( ! $can_write ) {
     1121                        $this->assertEquals( 403, $response->get_status() );
     1122                        $this->assertEmpty( get_post_meta( $post_id, $meta_key, $single ) );
     1123                        return;
     1124                }
     1125
     1126                $this->assertEquals( 200, $response->get_status() );
     1127
     1128                $data = $response->get_data();
     1129                $this->assertArrayHasKey( 'meta', $data );
     1130                $this->assertInternalType( 'array', $data['meta'] );
     1131
     1132                if ( $in_post_type ) {
     1133                        $expected_value = $meta_value;
     1134                        if ( ! $single ) {
     1135                                $expected_value = array( $expected_value );
     1136                        }
     1137
     1138                        $this->assertEquals( $expected_value, get_post_meta( $post_id, $meta_key, $single ) );
     1139                        $this->assertArrayHasKey( $meta_key, $data['meta'] );
     1140                        $this->assertEquals( $expected_value, $data['meta'][ $meta_key ] );
     1141                } else {
     1142                        $this->assertEmpty( get_post_meta( $post_id, $meta_key, $single ) );
     1143                        $this->assertArrayNotHasKey( $meta_key, $data['meta'] );
     1144                }
     1145        }
     1146
     1147        public function data_set_subtype_meta_value() {
     1148                $data = $this->data_get_subtype_meta_value();
     1149
     1150                foreach ( $data as $index => $dataset ) {
     1151                        $can_write = true;
     1152
     1153                        // This combination is not writable because of an auth callback of '__return_false'.
     1154                        if ( 'cpt' === $dataset[0] && 'test_single' === $dataset[1] ) {
     1155                                $can_write = false;
     1156                        }
     1157
     1158                        $data[ $index ][] = $can_write;
     1159                }
     1160
     1161                return $data;
     1162        }
     1163
     1164        /**
    10131165         * Internal function used to disable an insert query which
    10141166         * will trigger a wpdb error for testing purposes.
  • branches/4.9/tests/phpunit/tests/term/meta.php

    r41662 r43510  
    77 */
    88class Tests_Term_Meta extends WP_UnitTestCase {
     9
     10        private $last_register_meta_call = array(
     11                'object_type' => '',
     12                'meta_key'    => '',
     13                'args'        => array(),
     14        );
     15
    916        public function setUp() {
    1017                parent::setUp();
    … …  
    442449                $q->set( 'cache_results', true );
    443450        }
     451
     452        /**
     453         * @ticket 38323
     454         * @dataProvider data_register_term_meta
     455         */
     456        public function test_register_term_meta( $taxonomy, $meta_key, $args ) {
     457                add_filter( 'register_meta_args', array( $this, 'filter_register_meta_args_set_last_register_meta_call' ), 10, 4 );
     458
     459                register_term_meta( $taxonomy, $meta_key, $args );
     460
     461                $args['object_subtype'] = $taxonomy;
     462
     463                // Reset global so subsequent data tests do not get polluted.
     464                $GLOBALS['wp_meta_keys'] = array();
     465
     466                $this->assertEquals( 'term', $this->last_register_meta_call['object_type'] );
     467                $this->assertEquals( $meta_key, $this->last_register_meta_call['meta_key'] );
     468                $this->assertEquals( $args, $this->last_register_meta_call['args'] );
     469        }
     470
     471        public function data_register_term_meta() {
     472                return array(
     473                        array( 'wptests_tax', 'registered_key1', array( 'single' => true ) ),
     474                        array( 'category', 'registered_key2', array() ),
     475                        array( '', 'registered_key3', array( 'sanitize_callback' => 'absint' ) ),
     476                );
     477        }
     478
     479        public function filter_register_meta_args_set_last_register_meta_call( $args, $defaults, $object_type, $meta_key ) {
     480                $this->last_register_meta_call['object_type'] = $object_type;
     481                $this->last_register_meta_call['meta_key']    = $meta_key;
     482                $this->last_register_meta_call['args']        = $args;
     483
     484                return $args;
     485        }
     486
     487        /**
     488         * @ticket 38323
     489         * @dataProvider data_unregister_term_meta
     490         */
     491        public function test_unregister_term_meta( $taxonomy, $meta_key ) {
     492                global $wp_meta_keys;
     493
     494                register_term_meta( $taxonomy, $meta_key, array() );
     495                unregister_term_meta( $taxonomy, $meta_key );
     496
     497                $actual = $wp_meta_keys;
     498
     499                // Reset global so subsequent data tests do not get polluted.
     500                $wp_meta_keys = array();
     501
     502                $this->assertEmpty( $actual );
     503        }
     504
     505        public function data_unregister_term_meta() {
     506                return array(
     507                        array( 'wptests_tax', 'registered_key1' ),
     508                        array( 'category', 'registered_key2' ),
     509                        array( '', 'registered_key3' ),
     510                );
     511        }
    444512}
Note: See TracChangeset for help on using the changeset viewer.