Làm cách nào để tạo trạng thái metaboxes mở / đóng và ẩn / hiển thị trên cơ sở mỗi bài?


9

Vấn đề thực sự của tôi là một chút phức tạp, vì vậy tôi sẽ thử ở đây để trừu tượng hóa nó và giữ cho nó đơn giản.

Tôi đang làm việc trên một ứng dụng tùy chỉnh dựa trên WordPress. Tôi đã đăng ký một loại bài đăng tùy chỉnh, hãy gọi nó là "người" nơi tôi lưu trữ thông tin về ... người.

CPT chỉ hỗ trợ đăng tiêu đề và đăng các trường mặc định nội dung, nhưng có một số metabox để lưu thông tin cá nhân (nghĩ rằng ứng dụng của tôi như một sổ địa chỉ).

Vì vậy, có một metabox để lưu trữ thông tin cá nhân, một để lưu trữ thông tin mạng xã hội, một cái khác để lưu trữ thông tin liên quan đến công việc, tức là nếu người đó đối với tôi là khách hàng, nhà cung cấp, nếu chúng tôi có tín dụng hoặc ghi nợ ...

Tôi đã đơn giản hóa ở đây, nhưng có một lượng metaboxes nhất quán, giả sử 12.

Vấn đề của tôi là, một số người tôi muốn lưu trữ thông tin chỉ là liên hệ ngẫu nhiên và tôi chỉ muốn lưu trữ thông tin cá nhân, những người khác là bạn bè và tôi muốn lưu trữ thông tin cá nhân và thông tin mạng xã hội, những người khác là khách hàng hoặc nhà cung cấp và tôi muốn lưu trữ thông tin liên quan đến công việc.

Nếu khi chỉnh sửa bài đăng tôi ẩn (thông qua menu tùy chọn màn hình ) hoặc đóng bất kỳ metabox nào tôi không cần, khi tôi mở một bài đăng khác khi tôi cần chúng, tôi phải hiển thị hoặc mở lại chúng. Điều đó bởi vì vị trí / trạng thái / đơn hàng của metaboxes được lưu trên cơ sở mỗi người dùng dưới dạng siêu dữ liệu của người dùng .

Nếu bạn tưởng tượng trong một số bài đăng tôi cần 2 metaboxes, trong một số 10 và 5, bạn hiểu điều đó gây khó chịu vì việc giữ tất cả chúng hiển thị / mở khiến màn hình chỉnh sửa có thể truy cập thấp (thanh cuộn dường như vô tận) và đôi khi thông tin tôi tìm kiếm là ở cuối trang sau một loạt các metaboxes không có thông tin ...

Câu hỏi:

Có thể lưu vị trí / trạng thái / đơn hàng metaboxes trên cơ sở mỗi bài đăng cho một loại bài cụ thể không?


PS: Tôi biết một số js / jQuery có thể giải quyết vấn đề, nhưng nếu có thể tôi sẽ tránh các giải pháp javascript.

Câu trả lời:


8

Vấn đề chính:

Vấn đề chính ở đây là trong closing- , hiding-ordering- cuộc gọi ajax, không có bài ID được gửi với tải trọng. Dưới đây là hai ví dụ về dữ liệu mẫu:

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

Chúng ta có thể khắc phục điều này bằng cách sử dụng một cuộc gọi ajax tùy chỉnh khác.

Tất nhiên chúng ta có thể chỉ cần móc vào save_posthook và sửa đổi dữ liệu mỗi khi bài được lưu. Nhưng đó không phải là trải nghiệm UI bình thường, vì vậy điều đó không được xem xét ở đây

Có một giải pháp không thanh lịch khác có sẵn với PHP, được mô tả ở đây dưới đây:

Một giải pháp không phải là Javascript:

Câu hỏi là nơi lưu trữ dữ liệu? Là dữ liệu meta người dùng , đăng dữ liệu meta hoặc có thể trong một bảng tùy chỉnh?

Ở đây chúng tôi lưu trữ nó dưới dạng dữ liệu meta của người dùng và lấy việc đóng các hộp meta bài làm ví dụ.

Khi closedpostboxes_postgiá trị meta được cập nhật, chúng tôi cũng lưu nó vào closedpostboxes_post_{post_id}giá trị meta.

Sau đó, chúng tôi chiếm quyền điều khiển tìm nạp closedpostboxes_postđể ghi đè lên nó với giá trị meta tương ứng dựa trên id người dùng và id bài đăng.

a) Cập nhật trong closed-postboxeshành động ajax:

Chúng ta có thể tìm nạp ID bài đăng, thông qua wp_get_referer()và sau đó sử dụng url_to_postid()chức năng tiện dụng . Lần đầu tiên tôi biết về hàm "vui" này sau khi đọc câu trả lời từ @s_ha_dum , vài tháng trước ;-) Thật không may là hàm không nhận ra ?post=123các biến GET, nhưng chúng ta có thể thực hiện một mẹo nhỏ bằng cách thay đổi nó để p=123xử lý nó.

Chúng tôi có thể kết nối updated_user_meta, điều đó được kích hoạt ngay sau khi dữ liệu meta của người dùng closedpostboxes_postđược cập nhật:

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) Tìm nạp dữ liệu:

Chúng tôi có thể móc vào get_user_option_closedpostboxes_posthook để sửa đổi dữ liệu được tìm nạp từ closedpostboxes_postmeta người dùng:

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

Chúng tôi cũng có thể muốn nghĩ về trường hợp không có bài đăng closedpostboxes_post_{post_id}nào. Vì vậy, nó sẽ sử dụng các cài đặt lưu cuối cùng từ closedpostboxes_post. Có lẽ bạn sẽ muốn mở tất cả hoặc đóng tất cả, trong trường hợp mặc định đó. Nó sẽ dễ dàng để sửa đổi hành vi này.

Đối với các loại bài tùy chỉnh khác, chúng tôi có thể sử dụng closedpostboxes_{post_type}hook tương ứng .

Điều tương tự cũng có thể xảy ra đối với việc đặt hàngẩn metaboxes với meta metaboxhidden_{post_type}meta-box-order_{post_data}người dùng.

ps: xin lỗi vì câu trả lời cuối tuần quá dài này, vì chúng phải luôn ngắn & jolly ;-)


Tuyệt vời +1. N / P cho câu trả lời dài, tôi sẽ không mong đợi những câu trả lời ngắn. Thành thật mà nói tôi đã không mong đợi bất kỳ điều gì vào cuối tuần :) Hai điều tôi rất thích: Thứ nhất là ý tưởng lưu trữ dữ liệu trên cơ sở mỗi người dùng mỗi bài đăng: ý tưởng của tôi là lưu trữ trong meta post, nhưng theo cách đó tất cả người dùng sẽ có trạng thái tương tự. Ý tưởng thứ 2 được sử dụng 'get_user_option_*_post'để làm cho WP nhận ra dữ liệu tùy chỉnh. Chỉ nghĩ tôi không thích quá nhiều là việc sử dụng wp_get_refererthực sự trên $_SERVERvar đó là không thực sự đáng tin cậy nhưng tôi nghĩ rằng tôi có một ý tưởng để vượt qua những "vấn đề chính";)
gmazzap

Cảm ơn, tôi đoán nó phụ thuộc vào số lượng người dùng và bài đăng nơi lưu trữ dữ liệu tốt nhất. Có lẽ dữ liệu này nên có một số TTL và bị xóa, ví dụ mỗi tháng một lần? Có, tôi đồng ý với bạn về wp_get_referer()phương pháp này, đó là lý do tại sao tôi gọi nó là một giải pháp PHP không thanh lịch ;-) Trước tiên tôi nghĩ đến việc lưu trữ id bài đăng hiện tại cho mỗi người dùng, nhưng nó không hoạt động nếu người dùng chỉnh sửa hai hoặc nhiều hơn bài viết trong trình duyệt. Mong được nghe về ý tưởng của bạn về "vấn đề chính" Tận hưởng cuối tuần ;-)
birgire

Sau 43 ngày, một upvote nhắc nhở tôi để trả lời điều này. Cảm ơn một lần nữa cho câu trả lời của bạn.
gmazzap

6

Như birgire đã chỉ ra trong câu trả lời của mình , WordPress sử dụng AJAX để cập nhật trạng thái metaboxes và dữ liệu được truyền trong yêu cầu AJAX không bao gồm id bài đăng và điều đó khiến cho việc cập nhật trạng thái hộp trên cơ sở mỗi bài đăng trở nên khó khăn.

Khi tôi tìm thấy hành động AJAX được sử dụng bởi WordPress 'closed-postboxes', tôi đã tìm kiếm chuỗi này trong thư mục js của quản trị viên để tìm cách WordPress thực hiện yêu cầu AJAX.

Tôi tìm thấy nó xảy ra postbox.jsở dòng # 118 .

Có vẻ như vậy:

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

Về cơ bản, WordPress xem xét các mục DOM với lớp 'hộp thư' và lớp 'đóng' và tạo danh sách ID được phân tách bằng dấu phẩy. Điều tương tự cũng được thực hiện đối với các mục DOM ẩn với lớp 'hộp thư'.

Vì vậy, suy nghĩ của tôi là: Tôi có thể tạo một metabox giả có các lớp phù hợp và được ẩn đi, đặt id của nó để chứa ID bài đăng và bằng cách này tôi có thể truy xuất nó trong yêu cầu AJAX.

Đây là những gì tôi đã làm:

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

Bằng cách này, tôi đã tạo một metabox luôn đóng và luôn ẩn, vì vậy WordPress sẽ gửi ID của nó dưới dạng $_POSTvar trong yêu cầu AJAX và một khi id hộp giả chứa ID bài đăng theo cách có thể dự đoán được, tôi có thể nhận ra bài đăng.

Sau đó tôi đã xem cách WordPress thực hiện nhiệm vụ AJAX.

Trong admin-ajax.phpdòng 72 , WordPress móc 'wp_ajax_closed-postboxes'với mức độ ưu tiên 1.

Vì vậy, để hành động trước WordPress, tôi có thể nối hành động tương tự với mức độ ưu tiên 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 );

Có dữ liệu được lưu trong meta bài đăng có thể lọc get_user_option_closedpostboxes_mycptget_user_option_metaboxhidden_mycpt(cả hai biến thể của get_user_option_{$option}bộ lọc) để buộc các tùy chọn tải WordPress từ meta post:

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

Thật là một ý tưởng tuyệt vời khi sử dụng một metabox ẩn với thông tin có liên quan +1
birgire

cảm ơn @birgire và cảm ơn lần nữa vì A của bạn, ý tưởng lưu dữ liệu trên cả cơ sở cho mỗi người dùng và mỗi bài đăng là của bạn :)
gmazzap
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.