正如birgire在他的回答中指出的那样,WordPress使用AJAX更新元框状态,并且在AJAX请求中传递的数据不包含帖子ID,这使得在每个帖子的基础上更新框状态变得困难。
找到WordPress使用的AJAX操作后'closed-postboxes'
,便在admin js文件夹中搜索了此字符串,以查找WordPress如何发出AJAX请求。
我发现它发生postbox.js
在第118行。
看起来像这样:
save_state : function(page) {
var closed = $('.postbox').filter('.closed').map(function() {
return this.id;
}).get().join(',');
var hidden = $('.postbox').filter(':hidden').map(function() {
return this.id;
}).get().join(',');
$.post(ajaxurl, {
action: 'closed-postboxes',
closed: closed,
hidden: hidden,
closedpostboxesnonce: jQuery('#closedpostboxesnonce').val(),
page: page
});
}
本质上,WordPress使用“ postbox”类和“ closed”类查看DOM项目,并创建一个用逗号分隔的ID列表。对于具有“ postbox”类的隐藏DOM项也是如此。
因此,我的想法是:我可以创建一个伪造的 metabox,该类具有正确的类并且被隐藏,将其ID设置为包含帖子ID,并以此方式可以在AJAX请求中检索它。
这是我所做的:
add_action( 'dbx_post_sidebar', function() {
global $post;
if ( $post->post_type === 'mycpt' ) {
$id = $post->ID;
$f = '<span id="fakebox_pid_%d" class="postbox closed" style="display:none;"></span>';
printf( $f, $id );
}
});
这样,我创建了一个始终关闭且始终隐藏的metabox,因此WordPress将$_POST
在AJAX请求中以var的形式发送其ID ,并且一旦假框ID以可预测的方式包含帖子ID,我便能够识别该帖子。
之后,我研究了WordPress如何执行AJAX任务。
在admin-ajax.php
第72行中,WordPress的'wp_ajax_closed-postboxes'
优先级为1。
因此,要在WordPress之前采取行动,我可以将优先级为0的相同操作挂起。
add_action( 'wp_ajax_closed-postboxes', function() {
// check if we are in right post type: WordPress passes it in 'page' post var
$page = filter_input( INPUT_POST, 'page', FILTER_SANITIZE_STRING );
if ( $page !== 'mycpt' ) return;
// get post data
$data = filter_input_array( INPUT_POST, array(
'closed' => array( 'filter' => FILTER_SANITIZE_STRING ),
'hidden' => array( 'filter' => FILTER_SANITIZE_STRING )
) );
// search among closed boxes for the "fake" one, and return if not found
$look_for_fake = array_filter( explode( ',', $data[ 'closed' ] ), function( $id ) {
return strpos( $id, 'fakebox_pid_' ) === 0;
} );
if ( empty( $look_for_fake ) ) return;
$post_id = str_replace( 'fakebox_pid_', '', $look_for_fake[0] );
$user_id = get_current_user_id();
// remove fake id from values
$closed = implode(',', array_diff( explode(',', $data['closed'] ), $look_for_fake ) );
$hidden = implode(',', array_diff( explode(',', $data['hidden'] ), $look_for_fake ) );
// save metabox status on a per-post and per-user basis in a post meta
update_post_meta( $post_id, "_mycpt_closed_boxes_{user_id}", $closed );
update_post_meta( $post_id, "_mycpt_hidden_boxes_{user_id}", $hidden );
}, 0 );
将数据保存在post meta中可以过滤get_user_option_closedpostboxes_mycpt
和get_user_option_metaboxhidden_mycpt
(过滤器的两个变体get_user_option_{$option}
)从post meta强制WordPress加载选项:
add_filter( 'get_user_option_closedpostboxes_mycpt', function ( $result, $key, $user ) {
global $post;
$meta = get_post_meta( $post->ID, "_mycpt_closed_boxes_{$user->ID}", TRUE );
if ( ! empty( $meta ) ) {
$result = $meta;
}
return $result;
}, 10, 3 );
和
add_filter( 'get_user_option_metaboxhidden_mycpt', function ( $result, $key, $user ) {
global $post;
$meta = get_post_meta( $post->ID, "_mycpt_hidden_boxes_{$user->ID}", TRUE );
if ( ! empty( $meta ) ) {
$result = $meta;
}
return $result;
}, 10, 3 );
'get_user_option_*_post'
用于使WP识别自定义数据的想法。只是认为我不太喜欢wp_get_referer
在$_SERVER
var 上使用它,并不是很可靠,但是我认为我有一个克服“主要问题”的想法;)