The Main Problem:
The main problem here is that in the closing-, hiding- and ordering- ajax calls, there’s no post ID sent with the payload. Here are two form data examples:
1) action:closed-postboxes
closed:formatdiv,tagsdiv-post_tag,trackbacksdiv,authordiv
hidden:slugdiv
closedpostboxesnonce:8723ee108f
page:post
2) action:meta-box-order
_ajax_nonce:b6b48d2d16
page_columns:2
page:post
order[side]:submitdiv,formatdiv,categorydiv,tagsdiv-post_tag,postimagediv
order[normal]:postexcerpt,postcustom,trackbacksdiv,commentsdiv,authordiv
order[advanced]:
We could get around this by using another custom ajax call.
We could of course just hook into the save_post
hook and modify the data each time the post is saved. But that’s not the normal UI experience, so that’s not consider here
There’s another non-elegant solution available with PHP, described here below:
A Non Javascript Solution:
The question is where to store the data? As user meta data, post meta data or maybe in a custom table?
Here we store it as user meta data and take the closing of post meta boxes as an example.
When the closedpostboxes_post
meta value is updated, we save it into the closedpostboxes_post_{post_id}
meta value as well.
Then we hijack the fetching of closedpostboxes_post
to override it with the corresponding meta value based on user id and post id.
a) Updating during the closed-postboxes
ajax action:
We can fetch the post ID, through the wp_get_referer()
and then use the
handy url_to_postid()
function. I first knew about this “funny” function after reading the answer from @s_ha_dum, few months ago 😉 Unfortunately the function doesn’t recognize ?post=123
GET variables, but we can do a little trick by just changing it to p=123
to get around it.
We can hook into updated_user_meta
, that’s fired just after the user meta data for closedpostboxes_post
has been updated:
add_action( 'updated_user_meta',
function ( $meta_id, $object_id, $meta_key, $_meta_value )
{
$post_id = url_to_postid( str_replace( 'post=", "p=', wp_get_referer() ) );
if( 'closedpostboxes_post' === $meta_key && $post_id > 0 )
update_user_meta(
$object_id,
'closedpostboxes_post_' . $post_id,
$_meta_value
);
}
, 10, 4 );
b) Fetching data:
We can hook into the get_user_option_closedpostboxes_post
hook to modify the data fetched from the closedpostboxes_post
user meta:
add_filter( 'get_user_option_closedpostboxes_post',
function ( $result, $option, $user )
{
$post_id = filter_input( INPUT_GET, 'post', FILTER_SANITIZE_NUMBER_INT );
$newresult = get_user_option( 'closedpostboxes_post_'. $post_id , $user->ID );
return ( $newresult ) ? $newresult : $result;
}
, 10, 3 );
We might also want to think about the case where there’s no post based closedpostboxes_post_{post_id}
available. So it will use the last saved settings from closedpostboxes_post
. Maybe you would want to have it all open or all closed, in that default case. It would be easy to modify this behaviour.
For other custom post types we can use the corresponding closedpostboxes_{post_type}
hook.
Same should be possible for the ordering and hiding of metaboxes with the metaboxhidden_{post_type}
and meta-box-order_{post_data}
user meta.
ps: sorry for this too long weekend answer, since they should always be short & jolly 😉