如何按帖子保存打开/关闭以及隐藏/显示的元框状态?


9

我的实际问题有点复杂,因此我将在这里尝试对其进行抽象并使其简单。

我正在开发基于WordPress的自定义应用程序。我注册了一个自定义帖子类型,我们称其为“人”,用于存储有关...人的信息。

CPT仅支持帖子标题和帖子内容默认字段,但是有一些用于存储个人信息的元框(请将我的应用程序视为通讯录)。

因此,有一个metabox用于存储个人信息,一个用于存储社交网络信息,另一个用于存储与工作相关的信息,即如果该人对我来说是客户,供应商,如果我们有贷方或借方...

我在这里进行了简化,但是有大量的元框,比如说12。

我的问题是,一些我想为其存储信息的人只是随机联系人,我只想存储个人信息,其他人是朋友,我想存储个人信息和社交网络信息,其他人是客户或供应商,而我想要存储与工作相关的信息。

如果在编辑帖子时隐藏(通过屏幕选项菜单)或关闭不需要的任何metabox,则当我在需要它们的位置打开另一篇文章时,必须再次显示或打开它们。那是因为元框的位置/状态/顺序是按用户保存为用户元数据

如果您想象在某些帖子中我需要2个元框,分别在10个和5个中,您会发现这很烦人,因为将它们全部显示/打开会使编辑屏幕处于较低的可访问性(滚动条似乎无穷无尽),有时我寻找的信息是在页面末尾的一堆没有信息的metabox之后...

题:

是否可以针对特定帖子类型在每个帖子的基础上保存元框的位置/状态/顺序?


PS:我知道有些js / jQuery可以解决问题,但如果可能的话,我会避免使用JavaScript解决方案。

Answers:


8

主要问题:

这里的主要问题是,在闭合体hiding-ordering- Ajax调用,有一个与有效载荷没有发送帖子的ID。这是两个表单数据示例:

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]:

我们可以通过使用另一个自定义ajax调用来解决此问题。

当然,我们可以save_post在每次保存帖子时都挂入钩子并修改数据。但这不是正常的UI体验,因此这里不考虑

PHP提供了另一种非优雅的解决方案,如下所述:

非JavaScript解决方案:

问题是在哪里存储数据?作为用户元数据,发布元数据还是在自定义表中?

在这里,我们将其存储为用户元数据,并采取封闭职位元盒作为一个例子。

closedpostboxes_post元值更新时,我们也将其保存到closedpostboxes_post_{post_id}元值中。

然后,我们closedpostboxes_post根据用户ID和发布ID 劫持对的获取,以使用相应的元值覆盖它。

a)在closed-postboxesajax操作期间更新:

我们可以通过来获取帖子ID, wp_get_referer()然后使用方便的url_to_postid()功能。几个月前,我从@s_ha_dum阅读答案后,首先了解了这个“有趣”功能;-)不幸的是,该功能无法识别 ?post=123GET变量,但是我们可以通过将其更改p=123为可解决的方法来做一些技巧。

我们可以连接到updated_user_meta,它的用户元数据closedpostboxes_post已更新后立即触发:

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)提取数据:

我们可以挂钩,get_user_option_closedpostboxes_post以修改从closedpostboxes_post用户元数据中获取的数据:

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 );

我们可能还想考虑没有closedpostboxes_post_{post_id}可用的帖子的情况。因此它将使用上次保存的设置closedpostboxes_post。在这种默认情况下,您可能希望将其全部打开或全部关闭。修改此行为很容易。

对于其他自定义帖子类型,我们可以使用相应的closedpostboxes_{post_type}挂钩。

使用和用户meta来对metabox进行排序隐藏,应该有相同的可能。metaboxhidden_{post_type}meta-box-order_{post_data}

ps:很抱歉周末这个答案太长了,因为它们应该总是简短而愉快;-)


很棒+1。N / P长答案,我不希望简短。老实说,我没想到周末会发生什么:)我非常喜欢两件事:第一个想法是按用户每个帖子存储数据:我的想法是存储在帖子元中,但是那样用户将具有相同的状态。第二个'get_user_option_*_post'用于使WP识别自定义数据的想法。只是认为我不太喜欢wp_get_referer$_SERVERvar 上使用它,并不是很可靠,但是我认为我有一个克服“主要问题”的想法;)
gmazzap

谢谢,我想这取决于存储数据的最佳用户和帖子数量。也许此数据应该具有一些TTL,并且应该每月擦除一次?是的,我同意您的wp_get_referer()方法,这就是为什么我将其称为非优雅的PHP解决方案;-)我首先考虑为每个用户存储当前的帖子ID,但是如果用户正在编辑两个或多个,则该方法不起作用在浏览器中发布。期待听到您关于“主要问题”的想法享受周末;-)
birgire

经过43天的投票后,我不得不回答这个问题。再次感谢您的回答。
gmazzap

6

正如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_mycptget_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 );

使用具有相关信息+1的隐藏元

谢谢@birgire并再次感谢您的A,所有按用户和按帖子保存数据的想法都是您自己的:)
gmazzap
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.