Additional register_meta() changes in 4.6

In the last 2 weeks, the direction of register_meta() has changed significantly from the original write-up. There was a meeting to discuss some of the changes and a recap of that discussion. Some other discussion after that meeting led to a much more simplified version of register_meta() that is now shipping in 4.6.

Here’s what registering meta looked like in 4.5. This meta key has sanitization and authorization callbacks.

register_meta( 'post', 'my_meta_key', 'sanitize_my_meta_key', 'authorize_my_meta_key' );

The above code will continue to work in 4.6, though will not be considered completely registered. The callbacks will be registered, but the key will not be added to the global registry and register_meta() will return false.

Here’s what registering meta looks like in 4.6. This meta key will have sanitization and authorization callbacks, and be registered as public for the WordPress REST API.

$args = array(
    'sanitize_callback' => 'sanitize_my_meta_key',
    'auth_callback' => 'authorize_my_meta_key',
    'type' => 'string',
    'description' => 'My registered meta key',
    'single' => true,
    'show_in_rest' => true,
);
register_meta( 'post', 'my_meta_key', $args );

The above will register the meta key properly and return true.

There will no longer be a check for unique object types and subtypes for meta keys. There is no CURIE like syntax involved. Instead, be sure to uniquely prefix meta keys so that they do not conflict with others that may be registered with different arguments.

Additional helper functions get_registered_metadata(), get_registered_meta_keys(), unregister_meta(), and registered_meta_key_exists() have been added to make the innards of the global data more accessible.

The $wp_meta_keys variable should not be altered directly. It is possible that its structure will change in the future.

Any code currently using register_meta() and expecting pre-4.6 behavior will continue to work as is. Please report any breaks in compatibility that might be found.

For the full history, see #35658. 🙂

 

 

#4-6, #dev-notes, #options-meta

register_meta() Discussion Recap (July 14, 2016)

As announced yesterday, there was a discussion today covering the future of register_meta(), something that has been in progress for WordPress 4.6 in #35658. This is a recap. 🙂

Attendees: @helen, @ocean90, @rachelbaker, @mikeschinkel, @jsternberg, @sc0ttclark, @richardtape, @swissspidy, @joemcgill, @seancjones, @achbed, @jeremyfelt

Chat log.

Overview

In #35658, register_meta() uses object subtypes when registering meta so that key registration can be considered unique.

In 4.6 trunk, this information is passed as part of the 3rd argument—array( 'object_subtype' => 'books' )—to register_meta() and $object_subtype = '' has been added as an extra argument to several other pieces as a way to support that.

After exploring a bit more, it’s clear that $object_subtype will continue to spread as an additional argument throughout many _meta() functions so that registered meta is handled properly.

An alternative is to use CURIEs to describe complex meta types in a single string—'comment:reaction' rather than 'comment' and 'reaction'—so that the extra argument is not needed. Instead, processing exists to turn that string into an object type and subtype.

Examples

  • post:post
  • post:books
  • term:category
  • user:user
  • comment:reaction

The main question for today’s discussion

Is CURIE like notation the right way forward for handling complex meta types?

Decisions

After a very good and thorough conversation about the above and other points, these are the decisions for moving forward with work on #35658:

  1. Meta keys will be registered using CURIE like syntax.
    • register_meta( 'post:book', 'isbn', $args );
  2. The : used in the string to register meta keys will also be used in filters.
    • sanitize_post:book_meta_isbn
  3. Core object types will fallback to default subtypes if one is not specified.
    • post becomes post:post, comment becomes comment:comment, user becomes user:user, and term becomes term:category.
  4. Meta key registration for all/any subtypes of an object type will not be included in 4.6, but is likely something to add in the future.

Please check out the latest patches on #35658 and contribute code and thoughts on that ticket or share any questions/concerns in the discussion below.

Thanks everyone for attending today!

#4-6, #options-meta

register_meta() discussion on Thursday, July 14

Howdy!

There will be a meeting in #core at Thursday July 14, 19:00 UTC to discuss the changes in register_meta() for the 4.6 release. The dev notes have been published, but there is still work to be done to ensure things are great.

Of particular interest is a proposal to change to a CURIE like notation when working with the registration of meta. Instead of using two arguments (object type and subtype), there would be one argument containing both.

As an example, If a post type was registered with books as the slug and ISBN was stored as sanitized meta:

// In WordPress 4.5
register_meta( 'post', 'isbn', 'my_sanitize_callback' );

// In current trunk for 4.6
register_meta( 'post', 'isbn', array( 'object_subtype' => 'books', 'sanitize_callback' => 'my_sanitize_callback' ) );

// After proposed change
register_meta( 'post:books', 'isbn', array( 'sanitize_callback' => 'my_sanitize_callback' ) );

Please stop in #core at Thursday July 14, 19:00 UTC tomorrow if you are interested. If you can’t make it, please leave a comment on this post with your thoughts.

#4-6, #fields-api, #options-meta

Enhancing register_meta() in 4.6

Note: Note: The direction of register_meta() has changed since this was published. Please see the most recent developer note explaining these changes.

#4-6, #dev-notes, #fields-api, #options-meta, #rest-api

In 4.6, register_meta() expands to support the registration of meta keys and what to expect from those keys. See #35658 for the discussion around this change.

The behavior of register_meta() is similar to register_post_type() in that the registration of this data is stored in the global scope. This makes an object’s meta data more accessible to parts of core and extending code.

Why make this change

In 4.5 and earlier, WordPress provided no method to explicitly register a meta key’s public state. Instead, register_meta() provided “protected” and “authenticated” meta. This can be used effectively for some things, particularly to determine what meta appears in the Custom Fields metabox when editing a post. It can not be used as a basis for arbitrarily showing meta keys and values to unauthenticated visitors.

What does this change involve

A global variable, $wp_meta_keys, contains all registered meta keys.

The function signature of register_meta() has changed to support 3 arguments, the last being an array. That array should contain data about the meta with these key/values:

  • sanitize_callback, a callable method to provide sanitization. This is the new version of the current 3rd parameter in register_meta().
  • auth_callback, a callable method to provide authorization. This is the new version of the current 4th parameter in register_meta().
  • object_subtype, a string containing an object subtype slug. If there is no object subtype, meta will not be registered and a WP_Error will be returned instead.
  • type, a string indicating what type of data the meta value will be. This is intentionally not restricted to a specific list of data types, however full names should be used when possible. (e.g. boolean, integer)
  • description, a string containing a basic description of the meta value.
  • single, whether code retrieving meta for this key should expect a single or multiple values when using something like get_post_meta().
  • show_in_rest, whether this should be shown as part of a post’s meta endpoint in the WordPress REST API. Note that this should be treated as experimental until the WordPress REST API provides support for meta.

The register_meta_args filter is available to add support for additional arguments. This need to explicitly whitelist further arguments is to reserve the right for core to add further arguments in the future; should you choose to use this filter, you should be prepared to follow along with further development. Your testing with trunk or betas is greatly appreciated!

By default, only WordPress core object types (post, user, term, comment) can be registered. To add support for custom object types, use the wp_object_types filter. This whitelisting is similar to the above.

Object sub types provide specific registration of meta keys to a type of data.

  • A standard WordPress post has an object type of “post” and an object subtype of “post”.
  • A custom post type registered with a slug of “my_cpt” has an object type of “post” and an object subtype of “my_cpt”.
  • A WordPress user has an object type of “user” and an object subtype of “user”.
  • A standard WordPress comment has an object type of “comment” and an object subtype of “comment”.
  • A standard WordPress category term has an object type of “term” and an object subtype of “category”.
  • A custom taxonomy registered with a slug of “my_tax” has an object type of “term” and an object subtype of “my_tax”.

Meta keys must be explicitly registered for each object type and subtype combination.

Additional helper functions get_registered_metadata(), get_registered_meta_keys(), unregister_meta(), and registered_meta_key_exists() have been added to make the innards of the global data more accessible.

The $wp_meta_keys variable should not be altered directly. It is possible that its structure will change in the future.

Any code currently using register_meta() and expecting pre-4.6 behavior will continue to work as is. Please report any breaks in compatibility that might be found.

Example

Here’s what registering meta looked like in 4.5. This meta key has sanitization and authorization callbacks.

register_meta( 'post', 'my_meta_key', 'sanitize_my_meta_key', 'authorize_my_meta_key' );

The above code will continue to work in 4.6, though will not be considered completely registered. The callbacks will be registered, but the key will not be added to the global registry. A WP_Error object will be returned with this explanation.

Here’s what registering meta looks like in 4.6. This meta key will have sanitization and authorization callbacks, and be registered as public for the WordPress REST API.

$args = array(
    'object_subtype' => 'post',
    'sanitize_callback' => 'sanitize_my_meta_key',
    'auth_callback' => 'authorize_my_meta_key',
    'type' => 'string',
    'description' => 'My registered meta key',
    'single' => true,
    'show_in_rest' => true,
);
register_meta( 'post', 'my_meta_key', $args );

If you are currently using register_meta() and would like to maintain support for older versions of WordPress, the best method will be to check for the registration of the sanitization and authorization callbacks after calling register_meta() and then registering them manually if not present. This manual registration is all that register_meta() was previously doing. Continuing from the example above:

// Pre-WordPress 4.6 compatibility
if ( ! has_filter( 'sanitize_post_meta_my_meta_key' ) ) {
    add_filter( 'sanitize_post_meta_my_meta_key', 'sanitize_my_meta_key', 10, 4 );
}

if ( ! has_filter( 'auth_post_meta_my_meta_key' ) ) {
    add_filter( 'auth_post_meta_my_meta_key', 'authorize_my_meta_key', 10, 6 );
}

What’s next

While the initial work on this is largely driven by the need to move the WordPress REST API forward, its scope is not limited to solving the problem of public access to metadata. By having information about the meta keys used in a code base, it becomes much easier for other pieces of code to make decisions about what can be done with this data.

This data can one day be useful to everything from the Customizer, the editing experience for all object types, the Fields API, and many plugins that rely on metadata. Work will continue on transforming this $wp_meta_keys object and the methods surrounding it to be flexible and explicit. As WordPress core becomes familiar with register_meta() and more confident in the approach, the set of default arguments can and likely will expand to include more data about registered meta keys as required.

Feedback

Please leave any feedback you have on this new iteration of register_meta() in the comments below. Test it out in your plugins and themes to be sure it is working in a backwards compatible way. Try out the new function signature and register meta for objects and their subtypes. This continues to be a work in progress and feedback is important.

#4-6, #dev-notes, #fields-api, #options-meta, #rest-api

A data schema for meta

register_meta() is a tiny function, which lets you register a sanitization and authorization callback for post meta, term meta, user meta or comment meta.

We’re going to expand the utility of this function to describe the data type of the field. This will be useful for the REST API as well as the Fields API.

This discussion has started in #35658, but I’d like to share the latest thoughts for general feedback.

Imagine a “movie” custom post type which has a “star-rating” post meta field, which should accept the value 1, 2, 3, 4 and 5. REST API clients like the iOS app would want to know this information, so they can offer appropriate UI for their users. Ditto for the Fields API.

Here are some loose thoughts on what that API might look like.

Here is a post meta field for “mood” that accepts any string. The value would fit in a single row. There would only be one row per post, as it is not “repeatable.” Repeatable might be a default.

<?php 
register_meta( 'post', 'mood', array( 'schema' => array( 
		'type' => 'string',
		'repeatable' => false
	)
) );

Here is a field that consists of an array of URLs. The value would fit in a single row, perhaps in JSON format, i.e. ['some.url/','some.other/url'], and is limited to one row per post.

<?php 
register_meta( 'post', 'related_content', array(
	'schema' => array( 
		'type' => 'array',
		'items' => array(
			'type' => 'url',
		),
		'repeatable' => false
	)
) );

Here is a field that consists of an album as it relates to an artist and includes a number of properties. The value would fit in a single row, and multiple rows can exist for a single artist.

<?php 
register_meta( 'post', 'album', array(
	'object_subtype' => 'artist',
	'schema' => array( 
		'type' => 'object',
		'properties' => array(
			'name' => array(
				'type' => 'string'
			),
			'cover' => array(
				'type' => 'wp-image'
			),
			'genre' => array(
				'type' => 'string',
				'options' => array( 'Rock', 'R&B', 'Shoegaze' ),
			),
			'release-date' => array(
				'type' => 'date'
			)
		)
		'repeatable' => true
	)
) );

What do you think?

#fields-api, #options-meta, #rest-api

Fields API — Where we’re at

Over the past few months, we’ve been working towards one of the most wide-reaching API’s to be suggested for core inclusion, apart from the REST API. It has the potential to touch nearly every area of WordPress that deals with forms and fields. The Fields API is nearly there, and we’re seeking more feedback on a number of areas we need to tie down before we go into the official proposal stage for core inclusion.

What is the Fields API?

We just published a specification for the Fields API last week and we’re seeking constructive feedback on that now. Please take a moment to read about it, ask questions, and voice your concerns with us so we can address those things before we go to the next official stage.

There’s also some helpful docs which include TerminologyRegistration of fields, and example code.

What can you do with the Fields API right now?

We’ve put together a number of implementations to show the potential of what the Fields API can accomplish inside of core. Each implementation is an example of what could end up in WordPress core, should the Fields API itself be merged.

  • Edit User screen (@sc0ttkclark, @technosailor)
    • Our internal implementation of the Edit User screen replaces core’s entirely using the Fields API.
    • Here’s a code example to add more fields (and sections and controls) to the Edit User screen
    • Backwards compatibility hooks
    • To-do: Further custom markup and/or styling to match uniform layout
  • Edit Post screen (@sc0ttkclark, @brentvr)
    • Our internal implementation of the Edit Post screen will replace core’s entirely using the Fields API, we only have support for adding new sections / controls right now
    • To-do: Register sections / controls based on what’s on the Post Editor screen currently (@brentvr is working on this now)
    • To-do: Add additional output types (no meta box, etc)
    • To-do: Backwards compatibility hooks
  • Term Add / Edit (@sc0ttkclark, @technosailor)
    • Our internal implementations of the Add Term and Edit Term screen replaces core’s entirely using the Fields API
    • Backwards compatibility hooks
  • Comment Edit (@sc0ttkclark)
    • Our internal implementation of the Edit Comment screen will replace core’s entirely using the Fields API, we only have support for adding new sections / controls right now
    • To-do: Register sections / controls based on what’s on the Comment Editor screen currently
    • To-do: Add additional output types (no meta box, etc)
    • To-do: Backwards compatibility hooks
  • Settings Pages (@sc0ttkclark, @technosailor)
  • Widget Forms (@nicholasio, @sc0ttkclark)
    • Our internal implementation of the Widget forms will integrate with WP_Widget to allow form fields to be registered for widgets and rendered/saved automatically without the custom code every widget ends up needing currently.
    • To-do: WP_Widget integration (@nicholasio is working on this now)
  • Nav Menu Items (@diddledan, @sc0ttkclark)
    • Our internal implementation of the Nav Menu Items overrides the core Walker class used for building the Nav Menu Item forms, and uses the Fields API to output registered controls.
    • To-do: More compatibility work for CSS
    • To-do: Look into Customizer integration
    • To-do: Getting/saving values for nav menu item fields
  • Media
    • We do not currently have an implementation for this yet, but one was proposed by @wpsmith, and we are seeking additional help from anyone looking to make this happen
    • To-do: Add sections and controls to Media Edit
    • To-do: Add sections and controls to Media Modal Add/Edit
  • REST API
    • We currently have limited direct integration with the REST API, but we’d like to work with the REST API team towards implementing REST API options and building in addition configurations the REST API can consume for it’s endpoint(s)
    • register_rest_field integration

Possible Roadmap to Merge

Here’s an example of how Fields API could eventually be merged into core, along with other implementations.

  • Release 1: Merge Fields API into core
  • Release 2: Merge Implementation 1 into core
  • etc..

So each release could focus on a different implementation, which of course is a great time to coincide to UX / design work on each area, like the User Profile screen. There’s lots of options here, but hey, let’s discuss it!

How to help

If you’d like to help anywhere, please just reach out and I’ll get you into it. Every area needs review, some areas still need more work to finish, but any area could use additional help.

So who do you contact or where do you go to help?

Next Meeting Agenda – March 14th, 2016

Our next meeting is Monday 20:00 UTC 2015 and it promises to be eventful. We plan on discussing a number of things including:

#feature-plugins, #fields-api, #options-meta

Fields API updates from Q4 of 2015

It’s been a while since I updated everyone here about progress on the Fields API. I’ve been hard at work in the code and haven’t come up for air until now, but I’m planning on more regular updates in the coming weeks 🙂

Lots of progress since our last chat summary in early October, here’s a bird’s eye view of what we’ve been up to:

Where can we use help right now?

  • Review our User Profile screen implementation, let us know if it makes sense to you as a developer. It’s the kind of code you’ll be looking at needing for any custom screens you want to implement in plugins or themes, and it’s the kind of code we’re looking at using for other WordPress screens too (Post editor screen, Term editor screen, etc..)
  • Help us create use-cases for each type of object (Custom Post Types, Custom Taxonomies, Users, Comments, Media, Widgets, Nav Menu Items, etc..) — we’re specifically looking for what custom fields you could create for these use-cases and how it could make sense in real world scenarios. You can start out with just the structure, we can help you build the code for it.
  • We’re getting ready to release this on WordPress.org as a plugin, we could use some help to write up a good readme and ensure it directs people to the correct places for involvement

So who do you contact or where do you go to help?

Oh and did I mention that every minute you help us, you make someone really really really happy in the future? It could be your future self that gets to reap the rewards of your hard work!

#feature-plugins, #fields-api, #options-meta

Fields API chat summary – October 5th, 2015

Present: @sc0ttkclark, @nicholas_io, @tomharrigan, @ericlewis, @potatomaster

Logs: https://wordpress.slack.com/archives/core-fields/s1444021200000000

  • I just got 100 hours from 10up to work on Fields API!
  • I will be working on getting the WP 4.3 Customizer changes put into the Fields API, my first pass doesn’t have unit tests passing yet
  • We’ll be fleshing out Control classes, based on Customizer control classes and expand the main control class into individual classes as opposed to a ‘switch’
  • We laid out a few implementations we’d like to get into prototyping
    • User profile fields (piggy backing existing UI of section heading + fields) @sc0ttkclark
    • Settings API (cue the oooh’s and aaah’s sound effect) @sc0ttkclark
    • Post editor (meta boxes + fields) @tomharrigan
    • Widget forms @nicholas_io
    • Future: Term editor (sections + fields)
    • Future: Comment forms?
  • We want to improve the main Fields API readme to better explain the project, offer more links to information about the Customizer API since it’s what we based the Fields API on, and flesh out more examples
  • We need more examples, so any use-cases we can put together for any object type, would be handy to start putting that code together (structures, not custom implementations or overrides)

We certainly could use additional contributors involved with the project, especially as we seek to start more implementation prototypes of how things could work. Just hop into Slack #core-fields or check out our Github repo. Over the next 5 weeks my involvement in the project will be greatly increased, so if you are going to get involved — now would be the right timing!

Next chat: Monday 20:00 UTC 2015 (every Monday)

#chats, #feature-plugins, #fields-api, #meeting-notes, #options-meta

Fields API: Request for review of direction

Over the past many months this year, I have been working with guidance from @helen on the new Fields API with the intention of inclusion into WordPress core. It’s based on the Customizer API, so those who have experience with it will see a lot of similarities. The goal isn’t to create a duplicate API though, and in our proposal we would be implementing the Fields API within the Customizer API.

What does that bring to core as a whole? It gives core a very real and far reaching API to add fields to any type of object in a standard interface. Given the growth of the Customizer API and its inclusion of fields, @helen and I both agreed it’d be great to leverage everything it can do in our efforts here. The goal isn’t focused on the actual Customizer UI or editing on the front-end, it’s all data-oriented.

We would create implementations behind the scenes in the various existing APIs such as the Customizer API (now) and Settings API (later) while maintaining backwards compatibility. We’re also hoping to implement the Fields API within the User profile screen. This would give core more flexibility towards revamping the User profile screen in the future, without having to tackle even more than just a UI refresh. That’s also not mentioning the fact that plugin and theme authors could leverage this API to extend WordPress with their own fields in the various areas. Future places we’d look at integrating Fields API with would be the Post editor, which I’ve pegged as our third focus, behind User profile fields and the Customizer API.

Anyways, that leads us to the point we’re at now, we have an API but we don’t have all of the built-in Field Types (control types) yet or the latest Customizer API changes from 4.3 merged upstream into the Fields API. There are unit tests that are running (and passing!) so that is going to help us on our road towards core for sure.

We need developers to give their feedback on the direction we’re heading. Here are the relevant links for those reviewing:

I’d love more feedback from anyone contributing to WordPress, or the *countless* plugin and theme developers who would use the Fields API. Please comment on this post, or if you’d like to discuss further, you can hop into our weekly meetings on Mondays (Monday 20:00 UTC 2015) in the WP Slack #core-fields channel, or ping me there anytime.

Update – 08/10/2015:

We now also have a growing list of developers who are behind this project and would implement it in their projects if it were merged into core.

#fields-api, #options-meta

get_transient() is now more strict in 4.3

WordPress 4.3 includes a change to get_transient(). As reported in #23881 and #30380, get_transient() had a check for get_option( $transient_timeout ) < time().

Because get_option() can return false and false is always < time() 😖, get_transient() could delete transient timeout options in an unexpected way or cause two more unnecessary queries.

WordPress 4.3 now checks the return value first before comparing with the current time. This means that WordPress will no longer delete broken transients via get_transient() if you have only deleted the timeout option. See [33110].

If you have to delete a transient manually, please make sure that you’re deleting _transient_timeout_$transient and '_transient_' . $transient. (Hint: Transients can be stored in an external object cache too.)

See also: the Transients API Codex page.

#4-3, #dev-notes, #options-meta